GraphViz-2.14000755001750001750 012047110055 11744 5ustar00ronron000000000000GraphViz-2.14/Makefile.PL000444001750001750 174012047110055 14055 0ustar00ronron000000000000use Config; use ExtUtils::MakeMaker; if (open my $fh, '|dot', ) { close $fh; } else { die "Please install Graphviz from http://www.graphviz.org/.\n"; } WriteMakefile ( NAME => 'GraphViz', VERSION_FROM => 'lib/GraphViz.pm', LICENSE => 'perl', AUTHOR => 'Leon Brocard ', ABSTRACT => "Interface to AT&T's GraphViz. Deprecated. See GraphViz2", PREREQ_PM => { Carp => 1.01, Config => 0, File::Which => 1.09, Getopt::Long => 2.34, IO::Dir => 1.04, IO::File => 1.10, IPC::Run => 0.6, LWP::Simple => 6.00, Parse::RecDescent => 1.965001, Pod::Usage => 1.16, strict => 0, Test::More => 0.47, Test::Pod => 1.44, Time::HiRes => 1.51, vars => 0, warnings => 0, XML::Twig => 3.38, XML::XPath => 1.13, }, dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', }, ); GraphViz-2.14/README000444001750001750 5733012047110055 13011 0ustar00ronron000000000000NAME GraphViz - Interface to the GraphViz graphing tool SYNOPSIS use GraphViz; my $g = GraphViz->new(); $g->add_node('London'); $g->add_node('Paris', label => 'City of\nlurve'); $g->add_node('New York'); $g->add_edge('London' => 'Paris'); $g->add_edge('London' => 'New York', label => 'Far'); $g->add_edge('Paris' => 'London'); print $g->as_png; DESCRIPTION This module provides an interface to layout and image generation of directed and undirected graphs in a variety of formats (PostScript, PNG, etc.) using the "dot", "neato", "twopi", "circo" and "fdp" programs from the GraphViz project (http://www.graphviz.org/ or http://www.research.att.com/sw/tools/graphviz/). What is a graph? A (undirected) graph is a collection of nodes linked together with edges. A directed graph is the same as a graph, but the edges have a direction. What is GraphViz? This module is an interface to the GraphViz toolset (http://www.graphviz.org/). The GraphViz tools provide automatic graph layout and drawing. This module simplifies the creation of graphs and hides some of the complexity of the GraphViz module. Laying out graphs in an aesthetically-pleasing way is a hard problem - there may be multiple ways to lay out the same graph, each with their own quirks. GraphViz luckily takes part of this hard problem and does a pretty good job in a couple of seconds for most graphs. Why should I use this module? Observation aids comprehension. That is a fancy way of expressing that popular faux-Chinese proverb: "a picture is worth a thousand words". Text is not always the best way to represent anything and everything to do with a computer programs. Pictures and images are easier to assimilate than text. The ability to show a particular thing graphically can aid a great deal in comprehending what that thing really represents. Diagrams are computationally efficient, because information can be indexed by location; they group related information in the same area. They also allow relations to be expressed between elements without labeling the elements. A friend of mine used this to his advantage when trying to remember important dates in computer history. Instead of sitting down and trying to remember everything, he printed over a hundred posters (each with a date and event) and plastered these throughout his house. His spatial memory is still so good that asked last week (more than a year since the experiment) when Lisp was invented, he replied that it was upstairs, around the corner from the toilet, so must have been around 1958. Spreadsheets are also a wonderfully simple graphical representation of computational models. Applications Bundled with this module are several modules to help graph data structures (GraphViz::Data::Dumper), XML (GraphViz::XML), and Parse::RecDescent, Parse::Yapp, and yacc grammars (GraphViz::Parse::RecDescent, GraphViz::Parse::Yapp, and GraphViz::Parse::Yacc). Note that Marcel Grunauer has released some modules on CPAN to graph various other structures. See GraphViz::DBI and GraphViz::ISA for example. brian d foy has written an article about Devel::GraphVizProf for Dr. Dobb's Journal: http://www.ddj.com/columns/perl/2001/0104pl002/0104pl002.htm Award winning! I presented a paper and talk on "Graphing Perl" using GraphViz at the 3rd German Perl Workshop and received the "Best Knowledge Transfer" prize. Talk: http://www.astray.com/graphing_perl/graphing_perl.pdf Slides: http://www.astray.com/graphing_perl/ METHODS new This is the constructor. It accepts several attributes. my $g = GraphViz->new(); my $g = GraphViz->new(directed => 0); my $g = GraphViz->new(layout => 'neato', ratio => 'compress'); my $g = GraphViz->new(rankdir => 'BT'); my $g = GraphViz->new(width => 8.5, height => 11); my $g = GraphViz->new(width => 30, height => 20, pagewidth => 8.5, pageheight => 11); The most two important attributes are 'layout' and 'directed'. layout The 'layout' attribute determines which layout algorithm GraphViz.pm will use. Possible values are: dot The default GraphViz layout for directed graph layouts neato For undirected graph layouts - spring model twopi For undirected graph layouts - radial circo For undirected graph layouts - circular fdp For undirected graph layouts - force directed spring model directed The 'directed' attribute, which defaults to 1 (true) specifies directed (edges have arrows) graphs. Setting this to zero produces undirected graphs (edges do not have arrows). rankdir Another attribute 'rankdir' controls the direction in which the nodes are linked together. The default is 'TB' (arrows from top to bottom). Other legal values are 'BT' (bottom->top), 'LR' (left->right) and 'RL' (right->left). width, height The 'width' and 'height' attributes control the size of the bounding box of the drawing in inches. This is more useful for PostScript output as for raster graphic (such as PNG) the pixel dimensions can not be set, although there are generally 96 pixels per inch. pagewidth, pageheight The 'pagewidth' and 'pageheight' attributes set the PostScript pagination size in inches. That is, if the image is larger than the page then the resulting PostScript image is a sequence of pages that can be tiled or assembled into a mosaic of the full image. (This only works for PostScript output). concentrate The 'concentrate' attribute controls enables an edge merging technique to reduce clutter in dense layouts of directed graphs. The default is not to merge edges. random_start For undirected graphs, the 'random_start' attribute requests an initial random placement for the graph, which may give a better result. The default is not random. epsilon For undirected graphs, the 'epsilon' attribute decides how long the graph solver tries before finding a graph layout. Lower numbers allow the solver to fun longer and potentially give a better layout. Larger values can decrease the running time but with a reduction in layout quality. The default is 0.1. overlap The 'overlap' option allows you to set layout behavior for graph nodes that overlap. (From GraphViz documentation:) Determines if and how node overlaps should be removed. true (the default) overlaps are retained. scale overlaps are removed by uniformly scaling in x and y. false If the value converts to "false", node overlaps are removed by a Voronoi-based technique. scalexy x and y are separately scaled to remove overlaps. orthoxy, orthxy If the value is "orthoxy" or "orthoyx", overlaps are moved by optimizing two constraint problems, one for the x axis and one for the y. The suffix indicates which axis is processed first. NOTE: The methods related to "orthoxy" and "orthoyx" are still evolving. The semantics of these may change, or these methods may disappear altogether. compress If the value is "compress", the layout will be scaled down as much as possible without introducing any overlaps. Except for the Voronoi method, all of these transforms preserve the orthogonal ordering of the original layout. That is, if the x coordinates of two nodes are originally the same, they will remain the same, and if the x coordinate of one node is originally less than the x coordinate of another, this relation will still hold in the transformed layout. The similar properties hold for the y coordinates. no_overlap The 'no_overlap' overlap option, if set, tells the graph solver to not overlap the nodes. Deprecated, Use 'overlap' => 'false'. ratio The 'ratio' option sets the aspect ratio (drawing height/drawing width) for the drawing. Note that this is adjusted before the size attribute constraints are enforced. Default value is "fill". numeric If ratio is numeric, it is taken as the desired aspect ratio. Then, if the actual aspect ratio is less than the desired ratio, the drawing height is scaled up to achieve the desired ratio; if the actual ratio is greater than that desired ratio, the drawing width is scaled up. fill If ratio = "fill" and the size attribute is set, node positions are scaled, separately in both x and y, so that the final drawing exactly fills the specified size. compress If ratio = "compress" and the size attribute is set, dot attempts to compress the initial layout to fit in the given size. This achieves a tighter packing of nodes but reduces the balance and symmetry. This feature only works in dot. expand If ratio = "expand" the size attribute is set, and both the width and the height of the graph are less than the value in size, node positions are scaled uniformly until at least one dimension fits size exactly. Note that this is distinct from using size as the desired size, as here the drawing is expanded before edges are generated and all node and text sizes remain unchanged. auto If ratio = "auto" the page attribute is set and the graph cannot be drawn on a single page, then size is set to an ``ideal'' value. In particular, the size in a given dimension will be the smallest integral multiple of the page size in that dimension which is at least half the current size. The two dimensions are then scaled independently to the new size. This feature only works in dot. bgcolor The 'bgcolor' option sets the background colour. A colour value may be "h,s,v" (hue, saturation, brightness) floating point numbers between 0 and 1, or an X11 color name such as 'white', 'black', 'red', 'green', 'blue', 'yellow', 'magenta', 'cyan', or 'burlywood'. node,edge,graph The 'node', 'edge' and 'graph' attributes allow you to specify global node, edge and graph attributes (in addition to those controlled by the special attributes described above). The value should be a hash reference containing the corresponding key-value pairs. For example, to make all nodes box-shaped (unless explicity given another shape): my $g = GraphViz->new(node => {shape => 'box'}); add_node A graph consists of at least one node. All nodes have a name attached which uniquely represents that node. The add_node method creates a new node and optionally assigns it attributes. The simplest form is used when no attributes are required, in which the string represents the name of the node: $g->add_node('Paris'); Various attributes are possible: "label" provides a label for the node (the label defaults to the name if none is specified). The label can contain embedded newlines with '\n', as well as '\c', '\l', '\r' for center, left, and right justified lines: $g->add_node('Paris', label => 'City of\nlurve'); Attributes need not all be specified in the one line: successive declarations of the same node have a cumulative effect, in that any later attributes are just added to the existing ones. For example, the following two lines are equivalent to the one above: $g->add_node('Paris'); $g->add_node('Paris', label => 'City of\nlurve'); Note that multiple attributes can be specified. Other attributes include: height, width sets the minimum height or width shape sets the node shape. This can be one of: 'record', 'plaintext', 'ellipse', 'circle', 'egg', 'triangle', 'box', 'diamond', 'trapezium', 'parallelogram', 'house', 'hexagon', 'octagon' fontsize sets the label size in points fontname sets the label font family name color sets the outline colour, and the default fill colour if the 'style' is 'filled' and 'fillcolor' is not specified A colour value may be "h,s,v" (hue, saturation, brightness) floating point numbers between 0 and 1, or an X11 color name such as 'white', 'black', 'red', 'green', 'blue', 'yellow', 'magenta', 'cyan', or 'burlywood' fillcolor sets the fill colour when the style is 'filled'. If not specified, the 'fillcolor' when the 'style' is 'filled' defaults to be the same as the outline color style sets the style of the node. Can be one of: 'filled', 'solid', 'dashed', 'dotted', 'bold', 'invis' URL sets the url for the node in image map and PostScript files. The string '\N' value will be replaced by the node name. In PostScript files, URL information is embedded in such a way that Acrobat Distiller creates PDF files with active hyperlinks If you wish to add an anonymous node, that is a node for which you do not wish to generate a name, you may use the following form, where the GraphViz module generates a name and returns it for you. You may then use this name later on to refer to this node: my $nodename = $g->add_node('label' => 'Roman city'); Nodes can be clustered together with the "cluster" attribute, which is drawn by having a labelled rectangle around all the nodes in a cluster. An empty string means not clustered. $g->add_node('London', cluster => 'Europe'); $g->add_node('Amsterdam', cluster => 'Europe'); Clusters can also take a hashref so that you can set attributes: my $eurocluster = { name =>'Europe', style =>'filled', fillcolor =>'lightgray', fontname =>'arial', fontsize =>'12', }; $g->add_node('London', cluster => $eurocluster, @default_attrs); Nodes can be located in the same rank (that is, at the same level in the graph) with the "rank" attribute. Nodes with the same rank value are ranked together. $g->add_node('Paris', rank => 'top'); $g->add_node('Boston', rank => 'top'); Also, nodes can consist of multiple parts (known as ports). This is implemented by passing an array reference as the label, and the parts are displayed as a label. GraphViz has a much more complete port system, this is just a simple interface to it. See the 'from_port' and 'to_port' attributes of add_edge: $g->add_node('London', label => ['Heathrow', 'Gatwick']); add_edge Edges are directed (or undirected) links between nodes. This method creates a new edge between two nodes and optionally assigns it attributes. The simplest form is when now attributes are required, in which case the nodes from and to which the edge should be are specified. This works well visually in the program code: $g->add_edge('London' => 'Paris'); Attributes such as 'label' can also be used. This specifies a label for the edge. The label can contain embedded newlines with '\n', as well as '\c', '\l', '\r' for center, left, and right justified lines. $g->add_edge('London' => 'New York', label => 'Far'); Note that multiple attributes can be specified. Other attributes include: minlen sets an integer factor that applies to the edge length (ranks for normal edges, or minimum node separation for flat edges) weight sets the integer cost of the edge. Values greater than 1 tend to shorten the edge. Weight 0 flat edges are ignored for ordering nodes fontsize sets the label type size in points fontname sets the label font family name fontcolor sets the label text colour color sets the line colour for the edge A colour value may be "h,s,v" (hue, saturation, brightness) floating point numbers between 0 and 1, or an X11 color name such as 'white', 'black', 'red', 'green', 'blue', 'yellow', 'magenta', 'cyan', or 'burlywood' style sets the style of the node. Can be one of: 'filled', 'solid', 'dashed', 'dotted', 'bold', 'invis' dir sets the arrow direction. Can be one of: 'forward', 'back', 'both', 'none' tailclip, headclip when set to false disables endpoint shape clipping arrowhead, arrowtail sets the type for the arrow head or tail. Can be one of: 'none', 'normal', 'inv', 'dot', 'odot', 'invdot', 'invodot.' arrowsize sets the arrow size: (norm_length=10,norm_width=5, inv_length=6,inv_width=7,dot_radius=2) headlabel, taillabel sets the text for port labels. Note that labelfontcolor, labelfontname, labelfontsize are also allowed labeldistance, port_label_distance sets the distance from the edge / port to the label. Also labelangle decorateP if set, draws a line from the edge to the label samehead, sametail if set aim edges having the same value to the same port, using the average landing point constraint if set to false causes an edge to be ignored for rank assignment Additionally, adding edges between ports of a node is done via the 'from_port' and 'to_port' parameters, which currently takes in the offset of the port (ie 0, 1, 2...). $g->add_edge('London' => 'Paris', from_port => 0); as_canon, as_text, as_gif etc. methods There are a number of methods which generate input for dot / neato / twopi / circo / fdp or output the graph in a variety of formats. Note that if you pass a filename, the data is written to that filename. If you pass a filehandle, the data will be streamed to the filehandle. If you pass a scalar reference, then the data will be stored in that scalar. If you pass it a code reference, then it is called with the data (note that the coderef may be called multiple times if the image is large). Otherwise, the data is returned: Win32 Note: you will probably want to binmode any filehandles you write the output to if you want your application to be portable to Win32. my $png_image = $g->as_png; # or $g->as_png("pretty.png"); # save image # or $g->as_png(\*STDOUT); # stream image to a filehandle # or #g->as_png(\$text); # save data in a scalar # or $g->as_png(sub { $png_image .= shift }); as_debug The as_debug method returns the dot file which we pass to GraphViz. It does not lay out the graph. This is mostly useful for debugging. print $g->as_debug; as_canon The as_canon method returns the canonical dot / neato / twopi / circo / fdp file which corresponds to the graph. It does not layout the graph - every other as_* method does. print $g->as_canon; # prints out something like: digraph test { node [ label = "\N" ]; London [label=London]; Paris [label="City of\nlurve"]; New_York [label="New York"]; London -> Paris; London -> New_York [label=Far]; Paris -> London; } as_text The as_text method returns text which is a layed-out dot / neato / twopi / circo / fdp format file. print $g->as_text; # prints out something like: digraph test { node [ label = "\N" ]; graph [bb= "0,0,162,134"]; London [label=London, pos="33,116", width="0.89", height="0.50"]; Paris [label="City of\nlurve", pos="33,23", width="0.92", height="0.62"]; New_York [label="New York", pos="123,23", width="1.08", height="0.50"]; London -> Paris [pos="e,27,45 28,98 26,86 26,70 27,55"]; London -> New_York [label=Far, pos="e,107,40 49,100 63,85 84,63 101,46", lp="99,72"]; Paris -> London [pos="s,38,98 39,92 40,78 40,60 39,45"]; } as_ps Returns a string which contains a layed-out PostScript-format file. print $g->as_ps; as_hpgl Returns a string which contains a layed-out HP pen plotter-format file. print $g->as_hpgl; as_pcl Returns a string which contains a layed-out Laserjet printer-format file. print $g->as_pcl; as_mif Returns a string which contains a layed-out FrameMaker graphics-format file. print $g->as_mif; as_pic Returns a string which contains a layed-out PIC-format file. print $g->as_pic; as_gd Returns a string which contains a layed-out GD-format file. print $g->as_gd; as_gd2 Returns a string which contains a layed-out GD2-format file. print $g->as_gd2; as_gif Returns a string which contains a layed-out GIF-format file. print $g->as_gif; as_jpeg Returns a string which contains a layed-out JPEG-format file. print $g->as_jpeg; as_png Returns a string which contains a layed-out PNG-format file. print $g->as_png; $g->as_png("pretty.png"); # save image as_wbmp Returns a string which contains a layed-out Windows BMP-format file. print $g->as_wbmp; as_cmap (deprecated) Returns a string which contains a layed-out HTML client-side image map format file. Use as_cmpax instead. print $g->as_cmap; as_cmapx Returns a string which contains a layed-out HTML HTML/X client-side image map format file. print $g->as_cmapx; as_ismap (deprecated) Returns a string which contains a layed-out old-style server-side image map format file. Use as_imap instead. print $g->as_ismap; as_imap Returns a string which contains a layed-out HTML new-style server-side image map format file. print $g->as_imap; as_vrml Returns a string which contains a layed-out VRML-format file. print $g->as_vrml; as_vtx Returns a string which contains a layed-out VTX (Visual Thought) format file. print $g->as_vtx; as_mp Returns a string which contains a layed-out MetaPost-format file. print $g->as_mp; as_fig Returns a string which contains a layed-out FIG-format file. print $g->as_fig; as_svg Returns a string which contains a layed-out SVG-format file. print $g->as_svg; as_svgz Returns a string which contains a layed-out SVG-format file that is compressed. print $g->as_svgz; as_plain Returns a string which contains a layed-out simple-format file. print $g->as_plain; NOTES Older versions of GraphViz used a slightly different syntax for node and edge adding (with hash references). The new format is slightly clearer, although for the moment we support both. Use the new, clear syntax, please. SEE ALSO GraphViz::XML, GraphViz::Regex AUTHOR Leon Brocard COPYRIGHT Copyright (C) 2000-4, Leon Brocard This module is free software; you can redistribute it or modify it under the same terms as Perl itself. GraphViz-2.14/META.json000444001750001750 501612047110055 13524 0ustar00ronron000000000000{ "abstract" : "Interface to AT&T's GraphViz. Deprecated. See GraphViz2", "author" : [ "Leon Brocard " ], "dynamic_config" : 1, "generated_by" : "Module::Build version 0.4003, CPAN::Meta::Converter version 2.120630", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "GraphViz", "prereqs" : { "build" : { "requires" : { "Test::More" : "0.47", "Test::Pod" : "1.44" } }, "configure" : { "requires" : { "Module::Build" : "0.38" } }, "runtime" : { "requires" : { "Carp" : "1.01", "Config" : "0", "File::Which" : "1.09", "Getopt::Long" : "2.34", "IO::Dir" : "1.04", "IO::File" : "1.1", "IPC::Run" : "0.6", "LWP::Simple" : "6", "Parse::RecDescent" : "1.965001", "Pod::Usage" : "1.16", "Time::HiRes" : "1.51", "XML::Twig" : "3.38", "XML::XPath" : "1.13", "strict" : "0", "vars" : "0", "warnings" : "0" } } }, "provides" : { "Devel::GraphVizProf" : { "file" : "lib/Devel/GraphVizProf.pm", "version" : "2.14" }, "GraphViz" : { "file" : "lib/GraphViz.pm", "version" : "2.14" }, "GraphViz::Data::Grapher" : { "file" : "lib/GraphViz/Data/Grapher.pm", "version" : "2.14" }, "GraphViz::No" : { "file" : "lib/GraphViz/No.pm", "version" : "2.14" }, "GraphViz::Parse::RecDescent" : { "file" : "lib/GraphViz/Parse/RecDescent.pm", "version" : "2.14" }, "GraphViz::Parse::Yacc" : { "file" : "lib/GraphViz/Parse/Yacc.pm", "version" : "2.14" }, "GraphViz::Parse::Yapp" : { "file" : "lib/GraphViz/Parse/Yapp.pm", "version" : "2.14" }, "GraphViz::Regex" : { "file" : "lib/GraphViz/Regex.pm", "version" : "2.14" }, "GraphViz::Small" : { "file" : "lib/GraphViz/Small.pm", "version" : "2.14" }, "GraphViz::XML" : { "file" : "lib/GraphViz/XML.pm", "version" : "2.14" } }, "release_status" : "stable", "resources" : { "license" : [ "http://dev.perl.org/licenses/" ] }, "version" : "2.14" } GraphViz-2.14/CHANGES000444001750001750 2543512047110055 13125 0ustar00ronron000000000000Revision history for Perl module GraphViz. 2.14 Fri Nov 9 16:06:00 2012 - No code changes. - Patch t/foo.t to not assume text appears on specific lines of the output test files. 2.13 Fri Nov 9 08:27:00 2012 - No code changes. - Re-package distro because users get errors during testing. See RT#80709. Since I had this same error during my testing, I assume the uploaded version contains un-patched code. The errors are not in GraphViz, they are in the test code which has hard-coded line numbers where it looks for strings in the output. The output has been reformatted recently, and no longer matches those assumptions. See t/foo.t for details. Note: I did not write those tests :-). 2.12 Thu Nov 8 12:38:00 2012 - No code changes. - For pre-reqs such as strict, warnings, etc, which ship with Perl, set the version # to 0. Reported as RT#80663 by Father Chrysostomos for Tree::DAG_Node. 2.11 Tue Sep 18 08:22:00 2012 - Add VDX as an output format. 2.10 Mon Mar 26 10:11:00 2012 - Accept a patch kindly supplied by Alexander Kriegisch, to change handling of the rankdir attribute. The valid values are BT, LR, RL or TB, or their lower-case equivalents. Previously, only a true value was accepted, which meant LR. Now, any value not in that list defaults to LR. Files changed: README, CHANGES, Changelog.ini, GraphViz.pm, GraphViz/Regex.pm and simple.t. - Patch this file to replace BST with GMT, since both DateTime::Format::HTTP and DateTime::Format::Strptime fail to recognize BST. These modules are used by Module::Metadata::Changes to transform this file into Changelog.ini. 2.09 Thu Dec 15 11:08:00 2011 - Adopt Flavio Poletti's suggestion of trying to pipe to dot, in Build.PL/Makefile.PL, rather than using File::Which, to see if dot (Graphviz) is installed. This (hopefully) solves the problem of using File::Which on systems where it is not installed, before Build.PL/Makefile.PL has a chance to tell the user that File::Which is required. See: RT#73077. 2.08 Tue Nov 1 10:55:00 2011 - Wind back pre-reqs for various modules to match what was shipped with Perl V 5.8.1. Many thanx to Brian Cassidy for the error report: https://rt.cpan.org/Ticket/Display.html?id=72068. 2.07 Sun Oct 30 16:08:00 2011 - Rewrite Build.PL and Makefile.PL to try loading File::Which rather than assuming it is installed. This avoids the chicken-and-egg problem whereby these 2 programs need File::Which::which to find 'dot'. Many thanx to Richard Clamp for the error report: https://rt.cpan.org/Public/Bug/Display.html?id=71971. 2.06 Tue Oct 25 08:09:00 2011 - Add File::Which to the pre-reqs in Build.PL and Makefile.PL. 2.05 Thu Oct 20 10:52:00 2011 - Add 'Deprecated. See GraphViz2' to the docs. - Add Build.PL. - Add ability to set ORIENTATION. Thanx to Christian Lackas for the patch. See RT#71787. - Add Changelog.ini. - Add META.json, MYMETA.json and MYMETA.yml. - Add MANIFEST.SKIP. - Ensure all modules contain a version number. - Update the docs regarding the list of modules shipped in this distro. - Remove examples/remote.pl because GraphViz::Remote is no longer shipped. - Clean up examples/clusters2.pl. - Clean up Makefile.PL. 2.04 Fri Dec 12 21:31:24 GMT 2008 - perltidy everything - add human- and machine-readable license - add use warnings 2.03 Sun Nov 18 14:40:20 GMT 2007 - make the graph name configurable (patch by Ruslan Zakirov) 2.02 Fri Jan 7 18:51:06 GMT 2005 - remove dependencies on Graph and Math::Bezier - make GraphViz HTML-Like labels work (spotted by Patrice Dehais) - updated (including much documentation) to support newer additions to the dot language (by Max Baker) - new test which tests the POD 2.01 Fri Sep 24 17:02:29 GMT 2004 - no longer *always* quote the label in add_node() in order to let GraphViz::Data::Structure work again (sorry) 2.00 Wed Aug 25 16:30:53 GMT 2004 - thanks to Ron Savage, patched to work under systems which have an executable extension, such as Windows 1.9 Tue Aug 24 15:30:31 GMT 2004 - check for "dot" in the Makefile.PL instead of a test, as suggested by Autrijus Tang - renamed Changes to CHANGES - clusters can now take attributes as a hashref, thanks to patch from Richard A.Wells (see clusters2.pl) - fix docbug in GraphViz::Parse::Yapp (spotted by Mark Fowler) - better quoting (patch by Barrie Slaymaker) - document as_debug (suggested by Richard Clamp) 1.8 Sun Feb 23 09:15:14 GMT 2003 - support for client-side image maps by Dan Boorstein 1.7 Sun Jan 19 21:55:14 GMT 2003 - quote bgcolor so that HSV works 1.6 Sat Jan 18 15:47:26 GMT 2003 - moved tests to Test::More - new test which checks if graphviz is installed - new 'layout' graph attribute to support twopi - you may have to change your programs! - new bgcolor graph attribute (idea by Scott Murman) - labels named "graph" now work 1.5 Sun Jan 13 16:59:14 GMT 2002 - updated code reference docs slightly - removed GraphViz::Remote as it was no longer working - new no_overlap graph attribute which tells the graph solver to not overlap the nodes (idea by Chris Ball) - added patches by Barrie Slaymaker to make GraphViz work under Win32! - this is the Flight 63 edition 1.4 Wed Oct 3 07:57:42 GMT 2001 - added new filehandle, scalar reference, and code reference scheme to as_* to allow streaming of data, rather than accumulating potentially very large output in memory (based on patch by Dave Rolsky) - new pagewidth and pageheight graph attributes for creating PostScript mosaics of large graphs (idea by Nelson Loyola) 1.3 Sun Aug 19 15:43:02 GMT 2001 - labels can now contain quotes - fixed bug: labels can now start with a number - fixed bug in Devel::GraphVizProf so that packages are now grouped seperately (lines with the same text used to be grouped together) - fixed undefined warning in GraphViz::Parse::RecDescent - increased coverage of tests - new 'rank' node attribute allows nodes to be ranked at the same level - make empty cluster names do nothing (patch by Barrie Slaymaker) 1.2 Fri Aug 10 18:54:21 GMT 2001 - removed the images in the examples directory and added a file (make_all.pl) to, errr, make all the images - This is the HAL2001 edition 1.1 Tue Jul 24 23:54:42 GMT 2001 - added extra parameter to as_* to allow easy saving of images: $graph->as_png("pretty.png") - added new GraphViz::Parse::Yapp module to visualise Parse::Yapp grammars - added new GraphViz::Parse::Yacc module to visualise Parse::Yacc grammars - This is the TPC5 edition 1.00 Thu Jun 14 15:10:28 GMT 2001 - finally released as version 1.00! - added a reference to brian d foy's DDJ article on Devel::GraphVizProf - put the entire Perl regular expression test suite through GraphViz::Regex and fixed all the bugs - no longer sort nodes by default (idea by Stephen Riehm), which makes graphs just work better. Not documented, do you want it to be? 0.14 Thu May 3 17:57:57 GMT 2001 - added support for InterpLit node in RecDescent grammars - added cumulative effect for node attributes (patch by Diego Zamboni) - changed the quoting rules again to make it easier to read the dot files (idea by Diego Zamboni) - make add_edge() automatically add any nodes specified for the edge that have not been previously added to stop the Graph module complaining (patch by Diego Zamboni) - new 'node', 'edge', and 'graph' graph attributes to specify global node, edge, and graph attributes (patch by Diego Zamboni) - removed t/regex.t and documented that GraphViz::Regex may not work on various perls - added GraphViz::Regex_YAPE module, another way to graph a regular expression 0.13 Mon Mar 19 19:31:18 GMT 2001 - removed 'use warnings' as suggested by David Adler so we no longer require Perl 5.6 - moved all modules into a new 'lib' directory (and updated examples) so that Devel::GraphVizProf gets installed - new 'concentrate' graph attribute to merge edges in cluttered directed graphs - new 'random_start' graph attribute, which requests an initial random placement for the graph - new 'epsilon' graph attribute, which decides how long the graph solver tries before finding a graph layout, requested by Pierre-Yves Genot - an empty cluster now means not clustered - added GraphViz::Regex and example regexp.pl which visualises a regular expression - now an award-winning module! 0.12 Tue Mar 6 17:37:21 GMT 2001 - fixed bug in redcarpet.pl example - new rankdir graph attribute, which controls the direction the nodes are linked together (patch by Mark Fowler) - new 'width' and 'height' graph attributes control the size of the bounding box of the drawing in inches, requested by Pierre-Yves Genot 0.11 Tue Mar 6 17:37:20 GMT 2001 - rearranged module naming: Data::GraphViz -> GraphViz::Data::Dumper, Parse::RecDescent::GraphViz -> GraphViz::Parse::RecDescent, XML::GraphViz -> GraphViz::XML, - added GraphViz::Remote so that you do not need to install the graphviz tools to use this module 0.10 Mon Mar 5 17:32:14 GMT 2001 - now allow simple add_edge({$from => $to}) syntax (idea by DJ Adams and Brian Ingerson) - much better documentation (especially on attributes) - new module Parse::RecDescent::GraphViz (and example) for graphing Parse::RecDescent grammars (idea by Damian Conway) - new module XML::GraphViz (and example) for graphing XML - new module Data::GraphViz (and example) for graphing data structures - new example ppmgraph.pl by Marcel Grunauer which graphs CPAN tarball dependencies using ActiveState's package list (thanks to Brian Ingerson too ;-) - new, better, testsuite - better quoting (especially in ports) to allow a greater range of characters - new undocumented (it may change) as_graph method, which returns a graph object with the coordinates of nodes and edges 0.09 Fri Jan 12 15:50:17 GMT 2001 - moved back to "dot" and "neato" from "dotneato" - now allow directed and undirected graphs - added GraphViz::No and GraphViz::Small subclasses which aid in visualising the structure of large graphs 0.08 Sun Dec 3 15:15:29 GMT 2000 - minor patch to cope with DESTROY 0.07 Sun Oct 1 15:19:55 2000 - new features: allows clusters and ports - includes the talk I gave on this at yapc::Europe 19100 - many more examples (well, see the examples directory!), including quite a few PNGs 0.06 Thu Aug 24 09:33:21 2000 - better quoting of nodes and edges (they can now have really wierd names) - new examples directory with xref.pl: "graphing subroutine cross-reference reports for Perl modules" and example graph to see what kind of things it can do 0.05 Wed Aug 18 13:12:25 2000 - now use dotneato to layout the graphs and can now ouput in a variety of file formats 0.04 Wed Aug 9 16:14:35 2000 - first released version GraphViz-2.14/Changelog.ini000444001750001750 2616112047110055 14517 0ustar00ronron000000000000[Module] Name=GraphViz Changelog.Creator=Module::Metadata::Changes V 2.04 Changelog.Parser=Config::IniFiles V 2.78 [V 2.14] Date=2012-11-09T16:06:00 Comments= <as_png("pretty.png") - added new GraphViz::Parse::Yapp module to visualise Parse::Yapp grammars - added new GraphViz::Parse::Yacc module to visualise Parse::Yacc grammars - This is the TPC5 edition EOT [V 1.00] Date=2001-06-14T15:10:28 Comments= < GraphViz::Data::Dumper, Parse::RecDescent::GraphViz -> GraphViz::Parse::RecDescent, XML::GraphViz -> GraphViz::XML, - added GraphViz::Remote so that you do not need to install the graphviz tools to use this module EOT [V 0.10] Date=2001-03-05T17:32:14 Comments= < $to}) syntax (idea by DJ Adams and Brian Ingerson) - much better documentation (especially on attributes) - new module Parse::RecDescent::GraphViz (and example) for graphing Parse::RecDescent grammars (idea by Damian Conway) - new module XML::GraphViz (and example) for graphing XML - new module Data::GraphViz (and example) for graphing data structures - new example ppmgraph.pl by Marcel Grunauer which graphs CPAN tarball dependencies using ActiveState's package list (thanks to Brian Ingerson too ;-) - new, better, testsuite - better quoting (especially in ports) to allow a greater range of characters - new undocumented (it may change) as_graph method, which returns a graph object with the coordinates of nodes and edges EOT [V 0.09] Date=2001-01-12T15:50:17 Comments= < new ( module_name => 'GraphViz', license => 'perl', dist_abstract => "Interface to AT&T's GraphViz. Deprecated. See GraphViz2", dist_author => 'Leon Brocard ', build_requires => { Test::More => 0.47, Test::Pod => 1.44, }, configure_requires => { Module::Build => 0.3800, }, requires => { Carp => 1.01, Config => 0, File::Which => 1.09, Getopt::Long => 2.34, IO::Dir => 1.04, IO::File => 1.10, IPC::Run => 0.6, LWP::Simple => 6.00, Parse::RecDescent => 1.965001, Pod::Usage => 1.16, strict => 0, Time::HiRes => 1.51, vars => 0, warnings => 0, XML::Twig => 3.38, XML::XPath => 1.13, }, ) -> create_build_script(); GraphViz-2.14/META.yml000444001750001750 276712047110055 13366 0ustar00ronron000000000000--- abstract: "Interface to AT&T's GraphViz. Deprecated. See GraphViz2" author: - 'Leon Brocard ' build_requires: Test::More: 0.47 Test::Pod: 1.44 configure_requires: Module::Build: 0.38 dynamic_config: 1 generated_by: 'Module::Build version 0.4003, CPAN::Meta::Converter version 2.120630' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: GraphViz provides: Devel::GraphVizProf: file: lib/Devel/GraphVizProf.pm version: 2.14 GraphViz: file: lib/GraphViz.pm version: 2.14 GraphViz::Data::Grapher: file: lib/GraphViz/Data/Grapher.pm version: 2.14 GraphViz::No: file: lib/GraphViz/No.pm version: 2.14 GraphViz::Parse::RecDescent: file: lib/GraphViz/Parse/RecDescent.pm version: 2.14 GraphViz::Parse::Yacc: file: lib/GraphViz/Parse/Yacc.pm version: 2.14 GraphViz::Parse::Yapp: file: lib/GraphViz/Parse/Yapp.pm version: 2.14 GraphViz::Regex: file: lib/GraphViz/Regex.pm version: 2.14 GraphViz::Small: file: lib/GraphViz/Small.pm version: 2.14 GraphViz::XML: file: lib/GraphViz/XML.pm version: 2.14 requires: Carp: 1.01 Config: 0 File::Which: 1.09 Getopt::Long: 2.34 IO::Dir: 1.04 IO::File: 1.1 IPC::Run: 0.6 LWP::Simple: 6 Parse::RecDescent: 1.965001 Pod::Usage: 1.16 Time::HiRes: 1.51 XML::Twig: 3.38 XML::XPath: 1.13 strict: 0 vars: 0 warnings: 0 resources: license: http://dev.perl.org/licenses/ version: 2.14 GraphViz-2.14/MANIFEST000444001750001750 157712047110055 13244 0ustar00ronron000000000000Build.PL Changelog.ini CHANGES examples/clusters.pl examples/clusters2.pl examples/directories.pl examples/dprofpp.graphviz examples/dumper.pl examples/make_all.pl examples/perly.output examples/port.pl examples/ppmgraph.pl examples/primes.pl examples/primes_aux.pl examples/rank.pl examples/README examples/recdescent.pl examples/redcarpet.pl examples/regex.pl examples/simple.pl examples/text.pl examples/tmon.out examples/undirected.pl examples/xml.pl examples/xref.pl examples/xref_aux.pl examples/yacc.pl examples/Yapp.output examples/yapp.pl lib/Devel/GraphVizProf.pm lib/GraphViz.pm lib/GraphViz/Data/Grapher.pm lib/GraphViz/No.pm lib/GraphViz/Parse/RecDescent.pm lib/GraphViz/Parse/Yacc.pm lib/GraphViz/Parse/Yapp.pm lib/GraphViz/Regex.pm lib/GraphViz/Small.pm lib/GraphViz/XML.pm Makefile.PL MANIFEST This list of files META.json META.yml README t/dumper.t t/foo.t t/pod.t t/simple.t GraphViz-2.14/t000755001750001750 012047110055 12207 5ustar00ronron000000000000GraphViz-2.14/t/dumper.t000555001750001750 321612047110055 14032 0ustar00ronron000000000000#!/usr/bin/perl -w use strict; use warnings; use lib '../lib', 'lib'; use GraphViz::Data::Grapher; use Test::More tests => 1; my @lines = ; foreach my $lines ( split '-- test --', ( join "", @lines ) ) { my ( $test, $expect ) = split '-- expect --', $lines; next unless $test; $expect =~ s|^\n||mg; $expect =~ s|\n$||mg; $test =~ s|^\n||mg; $test =~ s|\n$||mg; my $g; eval $test; my $result = $g->_as_debug; $result =~ s|^\n||mg; $result =~ s|\n$||mg; is( $result, $expect, "got expected graph" ); } __DATA__ -- test -- my @d = ("red", { a => [3, 1, 4, 1], b => { q => 'a', w => 'b'}}, "blue", undef, GraphViz::Data::Grapher->new(), 2); $g = GraphViz::Data::Grapher->new(\@d); -- expect -- digraph test { ratio="fill"; GraphViz [color="red", label="GraphViz"]; node1 [color="blue", label="@", shape="record"]; node2 [color="black", label="red|%|blue|undef|Object|2", shape="record"]; node3 [color="brown", label="a|b", shape="record"]; node4 [color="blue", label="@", shape="record"]; node5 [color="black", label="3|1|4|1", shape="record"]; node6 [color="blue", label="%", shape="record"]; node7 [color="brown", label="q|w", shape="record"]; node8 [color="blue", label="a", shape="record"]; node9 [color="blue", label="b", shape="record"]; "node1":port0 -> node2; "node2":port4 -> GraphViz; "node2":port1 -> node3; "node3":port0 -> node4; "node3":port1 -> node6; "node4":port0 -> node5; "node6":port0 -> node7; "node7":port0 -> node8; "node7":port1 -> node9; } GraphViz-2.14/t/pod.t000444001750001750 24512047110055 13274 0ustar00ronron000000000000#!perl -T use strict; use warnings; use Test::More; eval "use Test::Pod 1.14"; plan skip_all => "Test::Pod 1.14 required for testing POD" if $@; all_pod_files_ok(); GraphViz-2.14/t/foo.t000444001750001750 370412047110055 13320 0ustar00ronron000000000000#!/usr/bin/perl use strict; use warnings; use lib '../lib', 'lib'; use GraphViz; use Test::More tests => 30; # make a nice simple graph and check how output is handled. my $g = GraphViz->new(); $g->add_node( label => 'London' ); { # Check filehandle my $fh = do { local *FH; *FH; }; # doubled to avoid warnings open $fh, ">as_foo.1" or die "Cannot write to as_foo.1: $!"; $g->as_dot($fh); close $fh; my @result = read_file('as_foo.1'); check_result(@result); } { # Check filehandle #2 local *OUT; open OUT, ">as_foo.2" or die "Cannot write to as_foo.2: $!"; $g->as_dot( \*OUT ); close OUT; my @result = read_file('as_foo.2'); check_result(@result); } { # Check filename $g->as_dot('as_foo.3'); my @result = read_file('as_foo.3'); check_result(@result); } { # Check scalar ref my $result; $g->as_dot( \$result ); check_result( split /\n/, $result ); } { # Check returned my $result = $g->as_dot(); check_result( split /\n/, $result ); } { # Check coderef my $result; $g->as_dot( sub { $result .= shift } ); check_result( split /\n/, $result ); } unlink 'as_foo.1'; unlink 'as_foo.2'; unlink 'as_foo.3'; sub read_file { my $filename = shift; local *FILE; open FILE, "<$filename" or die "Cannot read $filename: $!"; return (); } sub check_result { my @result = @_; my $expect = <<'EOF'; Expected something like (except for the line spacing :-(. Hence the join...): digraph test { node [ label = "\N" ]; graph [bb= "0,0,66,38"]; node1 [label=London, pos="33,19", width="0.89", height="0.50"]; } EOF my($result) = join(' ', @result); like( $result, qr/digraph test {/ ); like( $result, qr/\s*graph\s*\[bb=.*/ ); like( $result, qr/.+ratio=fill/ ); like( $result, qr/\s*node\s*\[\s*label\s*=\s*"\\N"\s*\];\s*/ ); like( $result, qr/.+label=London,/ ); } GraphViz-2.14/t/simple.t000555001750001750 2355512047110055 14057 0ustar00ronron000000000000#!perl -T use strict; use warnings; use lib '../lib', 'lib'; use GraphViz; use Test::More tests => 30; my @lines = ; foreach my $lines ( split '-- test --', ( join "", @lines ) ) { my ( $test, $expect ) = split '-- expect --', $lines; next unless $test; $expect =~ s|^\n||mg; $expect =~ s|\n$||mg; $test =~ s|^\n||mg; $test =~ s|\n$||mg; my $g; eval $test; my $result = $g->_as_debug; $result =~ s|^\n||mg; $result =~ s|\n$||mg; is( $result, $expect ); } __DATA__ -- test -- $g = GraphViz->new(); -- expect -- digraph test { ratio="fill"; } -- test -- $g = GraphViz->new(directed => 1) -- expect -- digraph test { ratio="fill"; } -- test -- $g = GraphViz->new(directed => 0) -- expect -- graph test { ratio="fill"; } -- test -- $g = GraphViz->new(rankdir => 'LR') -- expect -- digraph test { rankdir=LR; ratio="fill"; } -- test -- $g = GraphViz->new(); $g->add_node(label => 'London'); -- expect -- digraph test { ratio="fill"; node1 [label="London"]; } -- test -- $g = GraphViz->new(directed => 0); $g->add_node('London'); -- expect -- graph test { ratio="fill"; London [label="London"]; } -- test -- $g = GraphViz->new(); $g->add_node('London', label => 'Big smoke'); -- expect -- digraph test { ratio="fill"; London [label="Big smoke"]; } -- test -- $g = GraphViz->new(); $g->add_node('London', label => 'Big\nsmoke'); -- expect -- digraph test { ratio="fill"; London [label="Big\nsmoke"]; } -- test -- $g = GraphViz->new(); $g->add_node('London', label => 'Big smoke', color => 'red'); -- expect -- digraph test { ratio="fill"; London [color="red", label="Big smoke"]; } -- test -- $g = GraphViz->new(sort => 1); $g->add_node('London'); $g->add_node('Paris'); -- expect -- digraph test { ratio="fill"; London [label="London"]; Paris [label="Paris"]; } -- test -- $g = GraphViz->new(sort => 1); $g->add_node('London'); $g->add_edge('London' => 'London'); -- expect -- digraph test { ratio="fill"; London [label="London"]; London -> London; } -- test -- $g = GraphViz->new(sort => 1); $g->add_node('London'); $g->add_edge('London' => 'London', label => 'Foo'); -- expect -- digraph test { ratio="fill"; London [label="London"]; London -> London [label="Foo"]; } -- test -- $g = GraphViz->new(sort => 1); $g->add_node('London'); $g->add_edge('London' => 'London', color => 'red'); -- expect -- digraph test { ratio="fill"; London [label="London"]; London -> London [color="red"]; } -- test -- $g = GraphViz->new(sort => 1); $g->add_node('London'); $g->add_node('Paris'); $g->add_edge('London' => 'Paris'); -- expect -- digraph test { ratio="fill"; London [label="London"]; Paris [label="Paris"]; London -> Paris; } -- test -- $g = GraphViz->new(sort => 1); $g->add_node('London'); $g->add_node('Paris'); $g->add_edge('London' => 'Paris'); $g->add_edge('Paris' => 'London'); -- expect -- digraph test { ratio="fill"; London [label="London"]; Paris [label="Paris"]; London -> Paris; Paris -> London; } -- test -- $g = GraphViz->new(sort => 1); $g->add_node('London'); $g->add_node('Paris'); $g->add_edge('London' => 'London'); $g->add_edge('Paris' => 'Paris'); $g->add_edge('London' => 'Paris'); $g->add_edge('Paris' => 'London'); -- expect -- digraph test { ratio="fill"; London [label="London"]; Paris [label="Paris"]; London -> London; London -> Paris; Paris -> London; Paris -> Paris; } -- test -- $g = GraphViz->new(sort => 1); $g->add_node('London'); $g->add_node('Paris', label => 'City of\nlurve'); $g->add_node('New York'); $g->add_edge('London' => 'Paris'); $g->add_edge('London' => 'New York', label => 'Far'); $g->add_edge('Paris' => 'London'); -- expect -- digraph test { ratio="fill"; London [label="London"]; "New York" [label="New York"]; Paris [label="City of\nlurve"]; London -> "New York" [label="Far"]; London -> Paris; Paris -> London; } -- test -- # Test clusters $g = GraphViz->new(sort => 1); $g->add_node('London', cluster => 'Europe'); $g->add_node('Paris', label => 'City of\nlurve', cluster => 'Europe'); $g->add_node('New York'); $g->add_edge('London' => 'Paris'); $g->add_edge('London' => 'New York', label => 'Far'); $g->add_edge('Paris' => 'London'); -- expect -- digraph test { ratio="fill"; "New York" [label="New York"]; London -> "New York" [label="Far"]; subgraph cluster_Europe { label="Europe"; London [label="London"]; Paris [label="City of\nlurve"]; London -> Paris; Paris -> London; } } -- test -- $g = GraphViz->new({directed => 0, sort => 1}); foreach my $i (1..16) { my $used = 0; $used = 1 if $i >= 2 and $i <= 4; foreach my $j (2..4) { if ($i != $j && $i % $j == 0) { $g->add_edge($i => $j); $used = 1; } } $g->add_node($i) if $used; } -- expect -- graph test { ratio="fill"; node7 [label="10"]; node8 [label="12"]; node9 [label="14"]; node10 [label="15"]; node11 [label="16"]; node1 [label="2"]; node2 [label="3"]; node3 [label="4"]; node4 [label="6"]; node5 [label="8"]; node6 [label="9"]; node7 -- node1; node8 -- node1; node8 -- node2; node8 -- node3; node9 -- node1; node10 -- node2; node11 -- node1; node11 -- node3; node3 -- node1; node4 -- node1; node4 -- node2; node5 -- node1; node5 -- node3; node6 -- node2; } -- test -- $g = GraphViz->new(sort => 1); $g->add_node('London', label => ['Heathrow', 'Gatwick']); $g->add_node('Paris', label => 'CDG'); $g->add_node('New York', label => 'JFK'); $g->add_edge('London' => 'Paris', from_port => 0); $g->add_edge('New York' => 'London', to_port => 1); -- expect -- digraph test { ratio="fill"; London [label="Heathrow|Gatwick", shape="record"]; "New York" [label="JFK"]; Paris [label="CDG"]; "London":port0 -> Paris; "New York" -> "London":port1; } -- test -- $g = GraphViz->new(width => 30, height => 20, pagewidth => 8.5, pageheight=> 11, sort => 1); $g->add_node('London'); $g->add_node('Paris'); $g->add_edge('London' => 'London'); $g->add_edge('Paris' => 'Paris'); $g->add_edge('London' => 'Paris'); $g->add_edge('Paris' => 'London'); -- expect -- digraph test { size="30,20"; page="8.5,11"; ratio="fill"; London [label="London"]; Paris [label="Paris"]; London -> London; London -> Paris; Paris -> London; Paris -> Paris; } -- test -- $g = GraphViz->new(concentrate => 1, sort => 1) -- expect -- digraph test { ratio="fill"; concentrate=true; } -- test -- $g = GraphViz->new(epsilon => 0.001, random_start => 1, sort => 1) -- expect -- digraph test { ratio="fill"; epsilon=0.001; start=rand; } -- test -- # Test incremental buildup $g = GraphViz->new(sort => 1); $g->add_node('London'); $g->add_node('London', cluster => 'Europe'); $g->add_node('London', color => 'blue'); $g->add_node('Paris'); $g->add_node('Paris', label => 'City of\nlurve'); $g->add_node('Paris', cluster => 'Europe'); $g->add_node('Paris', color => 'green'); $g->add_node('New York'); $g->add_node('New York', color => 'yellow'); $g->add_edge('London' => 'Paris'); $g->add_edge('London' => 'New York', label => 'Far', color => 'red'); $g->add_edge('Paris' => 'London'); -- expect -- digraph test { ratio="fill"; "New York" [color="yellow", label="New York"]; London -> "New York" [color="red", label="Far"]; subgraph cluster_Europe { label="Europe"; London [color="blue", label="London"]; Paris [color="green", label="City of\nlurve"]; London -> Paris; Paris -> London; } } -- test -- $g = GraphViz->new(node => { shape => 'box' }, edge => { color => 'red' }, graph => { rotate => "90" }, sort => 1); $g->add_node('London'); $g->add_node('Paris', label => 'City of\nlurve'); $g->add_node('New York'); $g->add_edge('London' => 'Paris'); $g->add_edge('London' => 'New York', label => 'Far'); $g->add_edge('Paris' => 'London'); -- expect -- digraph test { ratio="fill"; node [shape="box"]; edge [color="red"]; graph [rotate="90"]; London [label="London"]; "New York" [label="New York"]; Paris [label="City of\nlurve"]; London -> "New York" [label="Far"]; London -> Paris; Paris -> London; } -- test -- $g = GraphViz->new(sort => 1); $g->add_node('a'); $g->add_node('b'); $g->add_node('c'); $g->add_node('d'); $g->add_node('e'); $g->add_node('f'); -- expect -- digraph test { ratio="fill"; a [label="a"]; b [label="b"]; c [label="c"]; d [label="d"]; e [label="e"]; f [label="f"]; } -- test -- $g = GraphViz->new(sort => 1); $g->add_edge('a' => 'b'); $g->add_edge('b' => 'c'); $g->add_edge('c' => 'a'); -- expect -- digraph test { ratio="fill"; a [label="a"]; b [label="b"]; c [label="c"]; a -> b; b -> c; c -> a; } -- test -- $g = GraphViz->new(sort => 1); $g->add_node('London'); $g->add_node('Paris', label => 'City of\nlurve', rank => 'top'); $g->add_node('New York'); $g->add_node('Boston', rank => 'top'); $g->add_edge('Paris' => 'London'); $g->add_edge('London' => 'New York', label => 'Far'); $g->add_edge('Boston' => 'New York'); -- expect -- digraph test { ratio="fill"; Boston [label="Boston", rank="top"]; London [label="London"]; "New York" [label="New York"]; Paris [label="City of\nlurve", rank="top"]; Boston -> "New York"; London -> "New York" [label="Far"]; Paris -> London; {rank=same; Boston; Paris} } -- test -- $g = GraphViz->new(sort => 1, no_overlap => 1); $g->add_node('London'); $g->add_node('Paris', label => 'City of\nlurve', rank => 'top'); $g->add_node('New York'); $g->add_node('Boston', rank => 'top'); $g->add_edge('Paris' => 'London'); $g->add_edge('London' => 'New York', label => 'Far'); $g->add_edge('Boston' => 'New York'); -- expect -- digraph test { ratio="fill"; overlap="false"; Boston [label="Boston", rank="top"]; London [label="London"]; "New York" [label="New York"]; Paris [label="City of\nlurve", rank="top"]; Boston -> "New York"; London -> "New York" [label="Far"]; Paris -> London; {rank=same; Boston; Paris} } -- test -- $g = GraphViz->new(); $g->add_node('GOroot', label => '<root>', shape => 'plaintext'); -- expect -- digraph test { ratio="fill"; GOroot [label=<root>, shape="plaintext"]; } GraphViz-2.14/lib000755001750001750 012047110055 12512 5ustar00ronron000000000000GraphViz-2.14/lib/GraphViz.pm000444001750001750 11212012047110055 14774 0ustar00ronron000000000000package GraphViz; use strict; use warnings; use vars qw($AUTOLOAD $VERSION); use Carp; use Config; use IPC::Run qw(run binary); our $VERSION = '2.14'; =head1 NAME GraphViz - Interface to AT&T's GraphViz. Deprecated. See GraphViz2 =head1 SYNOPSIS use GraphViz; my $g = GraphViz->new(); $g->add_node('London'); $g->add_node('Paris', label => 'City of\nlurve'); $g->add_node('New York'); $g->add_edge('London' => 'Paris'); $g->add_edge('London' => 'New York', label => 'Far'); $g->add_edge('Paris' => 'London'); print $g->as_png; =head1 DESCRIPTION This module provides an interface to layout and image generation of directed and undirected graphs in a variety of formats (PostScript, PNG, etc.) using the "dot", "neato", "twopi", "circo" and "fdp" programs from the GraphViz project (http://www.graphviz.org/ or http://www.research.att.com/sw/tools/graphviz/). GraphViz is deprecated in favour of L. =head2 Modules in this distro =over 4 =item o GraphViz =item o GraphViz::No =item o GraphViz::Small =item o GraphViz::Regex =item o GraphViz::XML =item o GraphViz::Data::Grapher =item o GraphViz::Parse::RecDescent =item o GraphViz::Parse::Yacc =item o GraphViz::Parse::Yapp =back =head2 What is a graph? A (undirected) graph is a collection of nodes linked together with edges. A directed graph is the same as a graph, but the edges have a direction. =head2 What is GraphViz? This module is an interface to the GraphViz toolset (http://www.graphviz.org/). The GraphViz tools provide automatic graph layout and drawing. This module simplifies the creation of graphs and hides some of the complexity of the GraphViz module. Laying out graphs in an aesthetically-pleasing way is a hard problem - there may be multiple ways to lay out the same graph, each with their own quirks. GraphViz luckily takes part of this hard problem and does a pretty good job in a couple of seconds for most graphs. =head2 Why should I use this module? Observation aids comprehension. That is a fancy way of expressing that popular faux-Chinese proverb: "a picture is worth a thousand words". Text is not always the best way to represent anything and everything to do with a computer programs. Pictures and images are easier to assimilate than text. The ability to show a particular thing graphically can aid a great deal in comprehending what that thing really represents. Diagrams are computationally efficient, because information can be indexed by location; they group related information in the same area. They also allow relations to be expressed between elements without labeling the elements. A friend of mine used this to his advantage when trying to remember important dates in computer history. Instead of sitting down and trying to remember everything, he printed over a hundred posters (each with a date and event) and plastered these throughout his house. His spatial memory is still so good that asked last week (more than a year since the experiment) when Lisp was invented, he replied that it was upstairs, around the corner from the toilet, so must have been around 1958. Spreadsheets are also a wonderfully simple graphical representation of computational models. =head2 Applications Bundled with this module are several modules to help graph data structures (GraphViz::Data::Dumper), XML (GraphViz::XML), and Parse::RecDescent, Parse::Yapp, and yacc grammars (GraphViz::Parse::RecDescent, GraphViz::Parse::Yapp, and GraphViz::Parse::Yacc). Note that Marcel Grunauer has released some modules on CPAN to graph various other structures. See GraphViz::DBI and GraphViz::ISA for example. brian d foy has written an article about Devel::GraphVizProf for Dr. Dobb's Journal: http://www.ddj.com/columns/perl/2001/0104pl002/0104pl002.htm =head2 Award winning! I presented a paper and talk on "Graphing Perl" using GraphViz at the 3rd German Perl Workshop and received the "Best Knowledge Transfer" prize. Talk: http://www.astray.com/graphing_perl/graphing_perl.pdf Slides: http://www.astray.com/graphing_perl/ =head1 METHODS =head2 new This is the constructor. It accepts several attributes. my $g = GraphViz->new(); my $g = GraphViz->new(directed => 0); my $g = GraphViz->new(layout => 'neato', ratio => 'compress'); my $g = GraphViz->new(rankdir => 'BT'); my $g = GraphViz->new(width => 8.5, height => 11); my $g = GraphViz->new(width => 30, height => 20, pagewidth => 8.5, pageheight => 11); The most two important attributes are 'layout' and 'directed'. =over =item layout The 'layout' attribute determines which layout algorithm GraphViz.pm will use. Possible values are: =over =item dot The default GraphViz layout for directed graph layouts =item neato For undirected graph layouts - spring model =item twopi For undirected graph layouts - radial =item circo For undirected graph layouts - circular =item fdp For undirected graph layouts - force directed spring model =back =item directed The 'directed' attribute, which defaults to 1 (true) specifies directed (edges have arrows) graphs. Setting this to zero produces undirected graphs (edges do not have arrows). =item rankdir Another attribute 'rankdir' controls the direction in which the nodes are linked together. The default is 'TB' (arrows from top to bottom). Other legal values are 'BT' (bottom->top), 'LR' (left->right) and 'RL' (right->left). =item width, height The 'width' and 'height' attributes control the size of the bounding box of the drawing in inches. This is more useful for PostScript output as for raster graphic (such as PNG) the pixel dimensions can not be set, although there are generally 96 pixels per inch. =item pagewidth, pageheight The 'pagewidth' and 'pageheight' attributes set the PostScript pagination size in inches. That is, if the image is larger than the page then the resulting PostScript image is a sequence of pages that can be tiled or assembled into a mosaic of the full image. (This only works for PostScript output). =item concentrate The 'concentrate' attribute controls enables an edge merging technique to reduce clutter in dense layouts of directed graphs. The default is not to merge edges. =item orientation This option controls the angle, in degrees, used to rotate polygon node shapes. =item random_start For undirected graphs, the 'random_start' attribute requests an initial random placement for the graph, which may give a better result. The default is not random. =item epsilon For undirected graphs, the 'epsilon' attribute decides how long the graph solver tries before finding a graph layout. Lower numbers allow the solver to fun longer and potentially give a better layout. Larger values can decrease the running time but with a reduction in layout quality. The default is 0.1. =item overlap The 'overlap' option allows you to set layout behavior for graph nodes that overlap. (From GraphViz documentation:) Determines if and how node overlaps should be removed. =over =item true (the default) overlaps are retained. =item scale overlaps are removed by uniformly scaling in x and y. =item false If the value converts to "false", node overlaps are removed by a Voronoi-based technique. =item scalexy x and y are separately scaled to remove overlaps. =item orthoxy, orthxy If the value is "orthoxy" or "orthoyx", overlaps are moved by optimizing two constraint problems, one for the x axis and one for the y. The suffix indicates which axis is processed first. B: The methods related to "orthoxy" and "orthoyx" are still evolving. The semantics of these may change, or these methods may disappear altogether. =item compress If the value is "compress", the layout will be scaled down as much as possible without introducing any overlaps. =back Except for the Voronoi method, all of these transforms preserve the orthogonal ordering of the original layout. That is, if the x coordinates of two nodes are originally the same, they will remain the same, and if the x coordinate of one node is originally less than the x coordinate of another, this relation will still hold in the transformed layout. The similar properties hold for the y coordinates. =item no_overlap The 'no_overlap' overlap option, if set, tells the graph solver to not overlap the nodes. Deprecated, Use 'overlap' => 'false'. =item ratio The 'ratio' option sets the aspect ratio (drawing height/drawing width) for the drawing. Note that this is adjusted before the size attribute constraints are enforced. Default value is C. =over =item numeric If ratio is numeric, it is taken as the desired aspect ratio. Then, if the actual aspect ratio is less than the desired ratio, the drawing height is scaled up to achieve the desired ratio; if the actual ratio is greater than that desired ratio, the drawing width is scaled up. =item fill If ratio = C and the size attribute is set, node positions are scaled, separately in both x and y, so that the final drawing exactly fills the specified size. =item compress If ratio = C and the size attribute is set, dot attempts to compress the initial layout to fit in the given size. This achieves a tighter packing of nodes but reduces the balance and symmetry. This feature only works in dot. =item expand If ratio = C the size attribute is set, and both the width and the height of the graph are less than the value in size, node positions are scaled uniformly until at least one dimension fits size exactly. Note that this is distinct from using size as the desired size, as here the drawing is expanded before edges are generated and all node and text sizes remain unchanged. =item auto If ratio = C the page attribute is set and the graph cannot be drawn on a single page, then size is set to an ``ideal'' value. In particular, the size in a given dimension will be the smallest integral multiple of the page size in that dimension which is at least half the current size. The two dimensions are then scaled independently to the new size. This feature only works in dot. =back =item bgcolor The 'bgcolor' option sets the background colour. A colour value may be "h,s,v" (hue, saturation, brightness) floating point numbers between 0 and 1, or an X11 color name such as 'white', 'black', 'red', 'green', 'blue', 'yellow', 'magenta', 'cyan', or 'burlywood'. =item name The 'name' option sets name of the graph. This option is useful in few situations, like client side image map generation, see cmapx. By default 'test' is used. =item node,edge,graph The 'node', 'edge' and 'graph' attributes allow you to specify global node, edge and graph attributes (in addition to those controlled by the special attributes described above). The value should be a hash reference containing the corresponding key-value pairs. For example, to make all nodes box-shaped (unless explicity given another shape): my $g = GraphViz->new(node => {shape => 'box'}); =back =cut sub new { my $proto = shift; my $config = shift; my $class = ref($proto) || $proto; my $self = {}; # Cope with the old hashref format if ( ref($config) ne 'HASH' ) { my %config; %config = ( $config, @_ ) if @_; $config = \%config; } $self->{NODES} = {}; $self->{NODELIST} = []; $self->{EDGES} = []; if ( exists $config->{directed} ) { $self->{DIRECTED} = $config->{directed}; } else { $self->{DIRECTED} = 1; # default to directed } if ( exists $config->{layout} ) { $self->{LAYOUT} = $config->{layout}; } else { $self->{LAYOUT} = "dot"; # default layout } if ( exists $config->{name} ) { $self->{NAME} = $config->{name}; } else { $self->{NAME} = 'test'; } if ( exists $config->{bgcolor} ) { $self->{BGCOLOR} = $config->{bgcolor}; } $self->{RANK_DIR} = $config->{rankdir} if ( exists $config->{rankdir} ); $self->{WIDTH} = $config->{width} if ( exists $config->{width} ); $self->{HEIGHT} = $config->{height} if ( exists $config->{height} ); $self->{PAGEWIDTH} = $config->{pagewidth} if ( exists $config->{pagewidth} ); $self->{PAGEHEIGHT} = $config->{pageheight} if ( exists $config->{pageheight} ); $self->{CONCENTRATE} = $config->{concentrate} if ( exists $config->{concentrate} ); $self->{ORIENTATION} = $config->{orientation} if ( exists $config->{orientation} ); $self->{RANDOM_START} = $config->{random_start} if ( exists $config->{random_start} ); $self->{EPSILON} = $config->{epsilon} if ( exists $config->{epsilon} ); $self->{SORT} = $config->{sort} if ( exists $config->{sort} ); $self->{OVERLAP} = $config->{overlap} if ( exists $config->{overlap} ); # no_overlap overrides overlap setting. $self->{OVERLAP} = 'false' if ( exists $config->{no_overlap} ); $self->{RATIO} = $config->{ratio} || 'fill'; # Global node, edge and graph attributes $self->{NODE_ATTRS} = $config->{node} if ( exists $config->{node} ); $self->{EDGE_ATTRS} = $config->{edge} if ( exists $config->{edge} ); $self->{GRAPH_ATTRS} = $config->{graph} if ( exists $config->{graph} ); bless( $self, $class ); return $self; } =head2 add_node A graph consists of at least one node. All nodes have a name attached which uniquely represents that node. The add_node method creates a new node and optionally assigns it attributes. The simplest form is used when no attributes are required, in which the string represents the name of the node: $g->add_node('Paris'); Various attributes are possible: "label" provides a label for the node (the label defaults to the name if none is specified). The label can contain embedded newlines with '\n', as well as '\c', '\l', '\r' for center, left, and right justified lines: $g->add_node('Paris', label => 'City of\nlurve'); Attributes need not all be specified in the one line: successive declarations of the same node have a cumulative effect, in that any later attributes are just added to the existing ones. For example, the following two lines are equivalent to the one above: $g->add_node('Paris'); $g->add_node('Paris', label => 'City of\nlurve'); Note that multiple attributes can be specified. Other attributes include: =over 4 =item height, width sets the minimum height or width =item shape sets the node shape. This can be one of: 'record', 'plaintext', 'ellipse', 'circle', 'egg', 'triangle', 'box', 'diamond', 'trapezium', 'parallelogram', 'house', 'hexagon', 'octagon' =item fontsize sets the label size in points =item fontname sets the label font family name =item color sets the outline colour, and the default fill colour if the 'style' is 'filled' and 'fillcolor' is not specified A colour value may be "h,s,v" (hue, saturation, brightness) floating point numbers between 0 and 1, or an X11 color name such as 'white', 'black', 'red', 'green', 'blue', 'yellow', 'magenta', 'cyan', or 'burlywood' =item fillcolor sets the fill colour when the style is 'filled'. If not specified, the 'fillcolor' when the 'style' is 'filled' defaults to be the same as the outline color =item style sets the style of the node. Can be one of: 'filled', 'solid', 'dashed', 'dotted', 'bold', 'invis' =item URL sets the url for the node in image map and PostScript files. The string '\N' value will be replaced by the node name. In PostScript files, URL information is embedded in such a way that Acrobat Distiller creates PDF files with active hyperlinks =back If you wish to add an anonymous node, that is a node for which you do not wish to generate a name, you may use the following form, where the GraphViz module generates a name and returns it for you. You may then use this name later on to refer to this node: my $nodename = $g->add_node('label' => 'Roman city'); Nodes can be clustered together with the "cluster" attribute, which is drawn by having a labelled rectangle around all the nodes in a cluster. An empty string means not clustered. $g->add_node('London', cluster => 'Europe'); $g->add_node('Amsterdam', cluster => 'Europe'); Clusters can also take a hashref so that you can set attributes: my $eurocluster = { name =>'Europe', style =>'filled', fillcolor =>'lightgray', fontname =>'arial', fontsize =>'12', }; $g->add_node('London', cluster => $eurocluster, @default_attrs); Nodes can be located in the same rank (that is, at the same level in the graph) with the "rank" attribute. Nodes with the same rank value are ranked together. $g->add_node('Paris', rank => 'top'); $g->add_node('Boston', rank => 'top'); Also, nodes can consist of multiple parts (known as ports). This is implemented by passing an array reference as the label, and the parts are displayed as a label. GraphViz has a much more complete port system, this is just a simple interface to it. See the 'from_port' and 'to_port' attributes of add_edge: $g->add_node('London', label => ['Heathrow', 'Gatwick']); =cut sub add_node { my $self = shift; my $node = shift; # Cope with the new simple notation if ( ref($node) ne 'HASH' ) { my $name = $node; my %node; if ( @_ % 2 == 1 ) { # No name passed %node = ( $name, @_ ); } else { # Name passed %node = ( @_, name => $name ); } $node = \%node; } $self->add_node_munge($node) if $self->can('add_node_munge'); # The _code attribute is our internal name for the node $node->{_code} = $self->_quote_name( $node->{name} ); if ( not exists $node->{name} ) { $node->{name} = $node->{_code}; } if ( not exists $node->{label} ) { if ( exists $self->{NODES}->{ $node->{name} } and defined $self->{NODES}->{ $node->{name} }->{label} ) { # keep our old label if we already exist $node->{label} = $self->{NODES}->{ $node->{name} }->{label}; } else { $node->{label} = $node->{name}; } } else { $node->{label} =~ s#([|<>\[\]{}"])#\\$1#g unless $node->{shape} && ($node->{shape} eq 'record' || ( $node->{label} =~ /^<{shape} eq 'plaintext' ) ); } delete $node->{cluster} if exists $node->{cluster} && !length $node->{cluster}; $node->{_label} = $node->{label}; # Deal with ports if ( ref( $node->{label} ) eq 'ARRAY' ) { $node->{shape} = 'record'; # force a record my $nports = 0; $node->{label} = join '|', map { $_ =~ s#([|<>\[\]{}"])#\\$1#g; '' . $_ } ( @{ $node->{label} } ); } # Save ourselves if ( !exists( $self->{NODES}->{ $node->{name} } ) ) { $self->{NODES}->{ $node->{name} } = $node; } else { # If the node already exists, add or overwrite attributes. foreach ( keys %$node ) { $self->{NODES}->{ $node->{name} }->{$_} = $node->{$_}; } } $self->{CODES}->{ $node->{_code} } = $node->{name}; # Add the node to the nodelist, which contains the names of # all the nodes in the order that they were inserted (but only # if it's not already there) push @{ $self->{NODELIST} }, $node->{name} unless grep { $_ eq $node->{name} } @{ $self->{NODELIST} }; return $node->{name}; } =head2 add_edge Edges are directed (or undirected) links between nodes. This method creates a new edge between two nodes and optionally assigns it attributes. The simplest form is when now attributes are required, in which case the nodes from and to which the edge should be are specified. This works well visually in the program code: $g->add_edge('London' => 'Paris'); Attributes such as 'label' can also be used. This specifies a label for the edge. The label can contain embedded newlines with '\n', as well as '\c', '\l', '\r' for center, left, and right justified lines. $g->add_edge('London' => 'New York', label => 'Far'); Note that multiple attributes can be specified. Other attributes include: =over 4 =item minlen sets an integer factor that applies to the edge length (ranks for normal edges, or minimum node separation for flat edges) =item weight sets the integer cost of the edge. Values greater than 1 tend to shorten the edge. Weight 0 flat edges are ignored for ordering nodes =item fontsize sets the label type size in points =item fontname sets the label font family name =item fontcolor sets the label text colour =item color sets the line colour for the edge A colour value may be "h,s,v" (hue, saturation, brightness) floating point numbers between 0 and 1, or an X11 color name such as 'white', 'black', 'red', 'green', 'blue', 'yellow', 'magenta', 'cyan', or 'burlywood' =item style sets the style of the node. Can be one of: 'filled', 'solid', 'dashed', 'dotted', 'bold', 'invis' =item dir sets the arrow direction. Can be one of: 'forward', 'back', 'both', 'none' =item tailclip, headclip when set to false disables endpoint shape clipping =item arrowhead, arrowtail sets the type for the arrow head or tail. Can be one of: 'none', 'normal', 'inv', 'dot', 'odot', 'invdot', 'invodot.' =item arrowsize sets the arrow size: (norm_length=10,norm_width=5, inv_length=6,inv_width=7,dot_radius=2) =item headlabel, taillabel sets the text for port labels. Note that labelfontcolor, labelfontname, labelfontsize are also allowed =item labeldistance, port_label_distance sets the distance from the edge / port to the label. Also labelangle =item decorateP if set, draws a line from the edge to the label =item samehead, sametail if set aim edges having the same value to the same port, using the average landing point =item constraint if set to false causes an edge to be ignored for rank assignment =back Additionally, adding edges between ports of a node is done via the 'from_port' and 'to_port' parameters, which currently takes in the offset of the port (ie 0, 1, 2...). $g->add_edge('London' => 'Paris', from_port => 0); =cut sub add_edge { my $self = shift; my $edge = shift; # Also cope with simple $from => $to if ( ref($edge) ne 'HASH' ) { my $from = $edge; my %edge = ( from => $from, to => shift, @_ ); $edge = \%edge; } $self->add_edge_munge($edge) if $self->can('add_edge_munge'); if ( not exists $edge->{from} or not exists $edge->{to} ) { carp("GraphViz add_edge: 'from' or 'to' parameter missing!"); return; } my $from = $edge->{from}; my $to = $edge->{to}; $self->add_node($from) unless exists $self->{NODES}->{$from}; $self->add_node($to) unless exists $self->{NODES}->{$to}; push @{ $self->{EDGES} }, $edge; # should remove! } =head2 as_canon, as_text, as_gif etc. methods There are a number of methods which generate input for dot / neato / twopi / circo / fdp or output the graph in a variety of formats. Note that if you pass a filename, the data is written to that filename. If you pass a filehandle, the data will be streamed to the filehandle. If you pass a scalar reference, then the data will be stored in that scalar. If you pass it a code reference, then it is called with the data (note that the coderef may be called multiple times if the image is large). Otherwise, the data is returned: B you will probably want to binmode any filehandles you write the output to if you want your application to be portable to Win32. my $png_image = $g->as_png; # or $g->as_png("pretty.png"); # save image # or $g->as_png(\*STDOUT); # stream image to a filehandle # or #g->as_png(\$text); # save data in a scalar # or $g->as_png(sub { $png_image .= shift }); =over 4 =item as_debug The as_debug method returns the dot file which we pass to GraphViz. It does not lay out the graph. This is mostly useful for debugging. print $g->as_debug; =item as_canon The as_canon method returns the canonical dot / neato / twopi / circo / fdp file which corresponds to the graph. It does not layout the graph - every other as_* method does. print $g->as_canon; # prints out something like: digraph test { node [ label = "\N" ]; London [label=London]; Paris [label="City of\nlurve"]; New_York [label="New York"]; London -> Paris; London -> New_York [label=Far]; Paris -> London; } =item as_text The as_text method returns text which is a layed-out dot / neato / twopi / circo / fdp format file. print $g->as_text; # prints out something like: digraph test { node [ label = "\N" ]; graph [bb= "0,0,162,134"]; London [label=London, pos="33,116", width="0.89", height="0.50"]; Paris [label="City of\nlurve", pos="33,23", width="0.92", height="0.62"]; New_York [label="New York", pos="123,23", width="1.08", height="0.50"]; London -> Paris [pos="e,27,45 28,98 26,86 26,70 27,55"]; London -> New_York [label=Far, pos="e,107,40 49,100 63,85 84,63 101,46", lp="99,72"]; Paris -> London [pos="s,38,98 39,92 40,78 40,60 39,45"]; } =item as_ps Returns a string which contains a layed-out PostScript-format file. print $g->as_ps; =item as_hpgl Returns a string which contains a layed-out HP pen plotter-format file. print $g->as_hpgl; =item as_pcl Returns a string which contains a layed-out Laserjet printer-format file. print $g->as_pcl; =item as_mif Returns a string which contains a layed-out FrameMaker graphics-format file. print $g->as_mif; =item as_pic Returns a string which contains a layed-out PIC-format file. print $g->as_pic; =item as_gd Returns a string which contains a layed-out GD-format file. print $g->as_gd; =item as_gd2 Returns a string which contains a layed-out GD2-format file. print $g->as_gd2; =item as_gif Returns a string which contains a layed-out GIF-format file. print $g->as_gif; =item as_jpeg Returns a string which contains a layed-out JPEG-format file. print $g->as_jpeg; =item as_png Returns a string which contains a layed-out PNG-format file. print $g->as_png; $g->as_png("pretty.png"); # save image =item as_wbmp Returns a string which contains a layed-out Windows BMP-format file. print $g->as_wbmp; =item as_cmap (deprecated) Returns a string which contains a layed-out HTML client-side image map format file. Use as_cmapx instead. print $g->as_cmap; =item as_cmapx Returns a string which contains a layed-out HTML HTML/X client-side image map format file. Name and id attributes of map element are set to name of the graph. print $g->as_cmapx; =item as_ismap (deprecated) Returns a string which contains a layed-out old-style server-side image map format file. Use as_imap instead. print $g->as_ismap; =item as_imap Returns a string which contains a layed-out HTML new-style server-side image map format file. print $g->as_imap; =item as_vdx Returns a string which contains a VDX-format (Microsoft Visio) file. print $g->as_vdx; =item as_vrml Returns a string which contains a layed-out VRML-format file. print $g->as_vrml; =item as_vtx Returns a string which contains a layed-out VTX (Visual Thought) format file. print $g->as_vtx; =item as_mp Returns a string which contains a layed-out MetaPost-format file. print $g->as_mp; =item as_fig Returns a string which contains a layed-out FIG-format file. print $g->as_fig; =item as_svg Returns a string which contains a layed-out SVG-format file. print $g->as_svg; =item as_svgz Returns a string which contains a layed-out SVG-format file that is compressed. print $g->as_svgz; =item as_plain Returns a string which contains a layed-out simple-format file. print $g->as_plain; =back =cut # Generate magic methods to save typing sub AUTOLOAD { my $self = shift; my $type = ref($self) or croak("$self is not an object"); my $output = shift; my $name = $AUTOLOAD; $name =~ s/.*://; # strip fully-qualified portion return if $name =~ /DESTROY/; if ( $name eq 'as_text' ) { $name = "as_dot"; } if ( $name =~ /^as_(ps|hpgl|pcl|mif|pic|gd|gd2|gif|jpeg|png|wbmp|cmapx?|ismap|imap|vdx|vrml|vtx|mp|fig|svgz?|dot|canon|plain)$/ ) { my $data = $self->_as_generic( '-T' . $1, $self->_as_debug, $output ); return $data; } croak "Method $name not defined!"; } # Return the main dot text sub as_debug { my $self = shift; return $self->_as_debug(@_); } sub _as_debug { my $self = shift; my $dot; my $graph_type = $self->{DIRECTED} ? 'digraph' : 'graph'; $dot .= $graph_type . " " . $self->{NAME} . " {\n"; # the direction of the graph if ($self->{RANK_DIR}) { $self->{RANK_DIR} = uc $self->{RANK_DIR}; my(%valid) = (BT => 1, LR => 1, RL => 1, TB => 1); $self->{RANK_DIR} = 'LR' if (! $valid{$self->{RANK_DIR} }); $dot .= "\trankdir=" . $self->{RANK_DIR} . ";\n"; } # the size of the graph $dot .= "\tsize=\"" . $self->{WIDTH} . "," . $self->{HEIGHT} . "\";\n" if $self->{WIDTH} && $self->{HEIGHT}; $dot .= "\tpage=\"" . $self->{PAGEWIDTH} . "," . $self->{PAGEHEIGHT} . "\";\n" if $self->{PAGEWIDTH} && $self->{PAGEHEIGHT}; # Ratio setting $dot .= "\tratio=\"" . $self->{RATIO} . "\";\n"; # edge merging $dot .= "\tconcentrate=true;\n" if $self->{CONCENTRATE}; # Orientation $dot .= "\torientation=$self->{ORIENTATION};\n" if $self->{ORIENTATION}; # epsilon $dot .= "\tepsilon=" . $self->{EPSILON} . ";\n" if $self->{EPSILON}; # random start $dot .= "\tstart=rand;\n" if $self->{RANDOM_START}; # overlap $dot .= "\toverlap=\"" . $self->{OVERLAP} . "\";\n" if $self->{OVERLAP}; # color, bgcolor $dot .= "\tbgcolor=\"" . $self->{BGCOLOR} . "\";\n" if $self->{BGCOLOR}; # Global node, edge and graph attributes $dot .= "\tnode" . _attributes( $self->{NODE_ATTRS} ) . ";\n" if exists( $self->{NODE_ATTRS} ); $dot .= "\tedge" . _attributes( $self->{EDGE_ATTRS} ) . ";\n" if exists( $self->{EDGE_ATTRS} ); $dot .= "\tgraph" . _attributes( $self->{GRAPH_ATTRS} ) . ";\n" if exists( $self->{GRAPH_ATTRS} ); my %clusters = (); my %cluster_nodes = (); my %clusters_edge = (); my $arrow = $self->{DIRECTED} ? ' -> ' : ' -- '; # Add all the nodes my @nodelist = @{ $self->{NODELIST} }; @nodelist = sort @nodelist if $self->{SORT}; foreach my $name (@nodelist) { my $node = $self->{NODES}->{$name}; # Note all the clusters if ( exists $node->{cluster} && $node->{cluster} ) { # map "name" to value in case cluster attribute is not a simple string $clusters{ $node->{cluster} } = $node->{cluster}; push @{ $cluster_nodes{ $node->{cluster} } }, $name; next; } $dot .= "\t" . $node->{_code} . _attributes($node) . ";\n"; } # Add all the edges foreach my $edge ( sort { $a->{from} cmp $b->{from} || $a->{to} cmp $b->{to} } @{ $self->{EDGES} } ) { my $from = $self->{NODES}->{ $edge->{from} }->{_code}; my $to = $self->{NODES}->{ $edge->{to} }->{_code}; # Deal with ports if ( exists $edge->{from_port} ) { $from = '"' . $from . '"' . ':port' . $edge->{from_port}; } if ( exists $edge->{to_port} ) { $to = '"' . $to . '"' . ':port' . $edge->{to_port}; } if ( exists $self->{NODES}->{$from} && exists $self->{NODES}->{$from}->{cluster} && exists $self->{NODES}->{$to} && exists $self->{NODES}->{$to}->{cluster} && $self->{NODES}->{$from}->{cluster} eq $self->{NODES}->{$to}->{cluster} ) { $clusters_edge{ $self->{NODES}->{$from}->{cluster} } .= "\t\t" . $from . $arrow . $to . _attributes($edge) . ";\n"; } else { $dot .= "\t" . $from . $arrow . $to . _attributes($edge) . ";\n"; } } foreach my $clustername ( sort keys %cluster_nodes ) { my $cluster = $clusters{$clustername}; my $attrs; my $name; if ( ref($cluster) eq 'HASH' ) { if ( exists $cluster->{label} ) { $name = $cluster->{label}; } elsif ( exists $cluster->{name} ) { # "coerce" name attribute into label attribute $name = $cluster->{name}; $cluster->{label} = $name; delete $cluster->{name}; } $attrs = _attributes($cluster); } else { $name = $cluster; $attrs = _attributes( { label => $cluster } ); } # rewrite attributes string slightly $attrs =~ s/^\s\[//o; $attrs =~ s/,/;/go; $attrs =~ s/\]$//o; $dot .= "\tsubgraph cluster_" . $self->_quote_name($name) . " {\n"; $dot .= "\t\t$attrs;\n"; $dot .= join "", map { "\t\t" . $self->{NODES}->{$_}->{_code} . _attributes( $self->{NODES}->{$_} ) . ";\n"; } ( @{ $cluster_nodes{$cluster} } ); $dot .= $clusters_edge{$cluster} if exists $clusters_edge{$cluster}; $dot .= "\t}\n"; } # Deal with ranks my %ranks; foreach my $name (@nodelist) { my $node = $self->{NODES}->{$name}; next unless exists $node->{rank}; push @{ $ranks{ $node->{rank} } }, $name; } foreach my $rank ( keys %ranks ) { $dot .= qq|\t{rank=same; |; $dot .= join '; ', map { $self->_quote_name($_) } @{ $ranks{$rank} }; $dot .= qq|}\n|; } # {rank=same; Paris; Boston} $dot .= "}\n"; return $dot; } # Call dot / neato / twopi / circo / fdp with the input text and any parameters sub _as_generic { my ( $self, $type, $dot, $output ) = @_; my $buffer; my $out; if ( ref $output || UNIVERSAL::isa( \$output, 'GLOB' ) ) { # $output is a filehandle or a scalar reference or something. # have to take a reference to a bare filehandle or run will # complain $out = ref $output ? $output : \$output; } elsif ( defined $output ) { # if it's defined it must be a filename so we'll write to it. $out = $output; } else { # but otherwise we capture output in a scalar $out = \$buffer; } my $program = $self->{LAYOUT}; run [ $program, $type ], \$dot, ">", binary(), $out; return $buffer unless defined $output; } # Quote a node/edge name using dot / neato / circo / fdp / twopi's quoting rules sub _quote_name { my ( $self, $name ) = @_; my $realname = $name; return $self->{_QUOTE_NAME_CACHE}->{$name} if $name && exists $self->{_QUOTE_NAME_CACHE}->{$name}; if ( defined $name && $name =~ /^[a-zA-Z]\w*$/ && $name ne "graph" ) { # name is fine } elsif ( defined $name && $name =~ /^[a-zA-Z](\w| )*$/ ) { # name contains spaces, so quote it $name = '"' . $name . '"'; } else { # name contains weird characters - let's make up a name for it $name = 'node' . ++$self->{_NAME_COUNTER}; } $self->{_QUOTE_NAME_CACHE}->{$realname} = $name if defined $realname; # warn "# $realname -> $name\n"; return $name; } # Return the attributes of a node or edge as a dot / neato / circo / fdp / twopi attribute # string sub _attributes { my $thing = shift; my @attributes; foreach my $key ( keys %$thing ) { next if $key =~ /^_/; next if $key =~ /^(to|from|name|cluster|from_port|to_port)$/; my $value = $thing->{$key}; $value =~ s|"|\"|g; $value = '"' . $value . '"' unless ( $key eq 'label' && $value =~ /^<:1: syntax error near line 1 context: digraph >>> Graph <<< { Graphviz reserves some words as keywords, meaning they can't be used as an ID, e.g. for the name of the graph. So, don't do this: strict graph graph{...} strict graph Graph{...} strict graph strict{...} etc... Likewise for non-strict graphs, and digraphs. You can however add double-quotes around such reserved words: strict graph "graph"{...} Even better, use a more meaningful name for your graph... The keywords are: node, edge, graph, digraph, subgraph and strict. Compass points are not keywords. See L in the discussion of the syntax of DOT for details. =head1 NOTES Older versions of GraphViz used a slightly different syntax for node and edge adding (with hash references). The new format is slightly clearer, although for the moment we support both. Use the new, clear syntax, please. =head1 SEE ALSO GraphViz is deprecated in favour of L. =head1 Machine-Readable Change Log The file CHANGES was converted into Changelog.ini by L. =head1 AUTHOR Leon Brocard: EFE. Current maintainer: Ron Savage Iron@savage.net.auE>. =head1 COPYRIGHT Copyright (C) 2000-4, Leon Brocard =head1 LICENSE This module is free software; you can redistribute it or modify it under the same terms as Perl itself. =cut 1; GraphViz-2.14/lib/GraphViz000755001750001750 012047110055 14244 5ustar00ronron000000000000GraphViz-2.14/lib/GraphViz/XML.pm000444001750001750 475612047110055 15413 0ustar00ronron000000000000package GraphViz::XML; use strict; use warnings; use vars qw($VERSION); use Carp; use lib '..'; use GraphViz; use XML::Twig; our $VERSION = '2.14'; =head1 NAME GraphViz::XML - Visualise XML as a tree =head1 SYNOPSIS use GraphViz::XML; my $graph = GraphViz::XML->new($xml); print $g->as_png; =head1 DESCRIPTION This module makes it easy to visualise XML as a tree. XML is hard for humans to grasp, especially if the XML is computer-generated. This modules aims to visualise the XML as a graph in order to make the structure of the XML clear and to aid in understanding the XML. XML elements are represented as diamond nodes, with links to elements within them. Character data is represented in round nodes. Note that the XML::Twig module should be installed. =head1 METHODS =head2 new This is the constructor. It takes one mandatory argument, which is the XML to be visualised. A GraphViz object is returned. my $graph = GraphViz::XML->new($xml); =cut sub new { my $proto = shift; my $class = ref($proto) || $proto; my $xml = shift; my $t = XML::Twig->new(); $t->parse($xml); my $graph = GraphViz->new(); _init( $graph, $t->root ); return $graph; } =head2 as_* The XML can be visualised in a number of different graphical formats. Methods include as_ps, as_hpgl, as_pcl, as_mif, as_pic, as_gd, as_gd2, as_gif, as_jpeg, as_png, as_wbmp, as_ismap, as_imap, as_vrml, as_vtx, as_mp, as_fig, as_svg. See the GraphViz documentation for more information. The two most common methods are: # Print out a PNG-format file print $g->as_png; # Print out a PostScript-format file print $g->as_ps; =cut sub _init { my ( $g, $root ) = @_; #warn "$root $root->gi\n"; my $label = $root->gi; my $colour = 'blue'; my $shape = 'ellipse'; if ( $root->is_pcdata ) { $label = $root->text; $label =~ s|^\s+||; $label =~ s|\s+$||; $colour = 'black'; } else { $shape = "diamond"; } $g->add_node( $root, label => $label, color => $colour, shape => $shape ); foreach my $child ( $root->children ) { $g->add_edge( $root => $child ); _init( $g, $child ); } } =head1 BUGS GraphViz tends to reorder the nodes. I hope to find a work around soon (possibly with ports). =head1 AUTHOR Leon Brocard EFE =head1 COPYRIGHT Copyright (C) 2001, Leon Brocard This module is free software; you can redistribute it or modify it under the same terms as Perl itself. =cut 1; GraphViz-2.14/lib/GraphViz/Small.pm000444001750001750 177312047110055 16017 0ustar00ronron000000000000package GraphViz::Small; use strict; use warnings; use GraphViz; use vars qw($VERSION @ISA); @ISA = qw(GraphViz); our $VERSION = '2.14'; =head1 NAME GraphViz::Small - subclass of GraphViz with small nodes =head1 SYNOPSIS use GraphViz::Small; my $g = GraphViz::Small->new(); # methods as for GraphViz =head1 DESCRIPTION Graphs produced by GraphViz are occasionally huge, making it hard to observe the structure. This subclass simply makes the nodes small and empty, allowing the structure to stand out. =head1 METHODS As for GraphViz. =cut sub add_node_munge { my $self = shift; my $node = shift; $node->{label} = ''; $node->{height} = 0.2; $node->{width} = 0.2; $node->{style} = 'filled'; $node->{color} = 'black' unless $node->{color}; } =head1 AUTHOR Leon Brocard EFE =head1 COPYRIGHT Copyright (C) 2000-1, Leon Brocard This module is free software; you can redistribute it or modify it under the same terms as Perl itself. =cut 1; GraphViz-2.14/lib/GraphViz/No.pm000444001750001750 206412047110055 15315 0ustar00ronron000000000000package GraphViz::No; use strict; use warnings; use GraphViz; use vars qw($VERSION @ISA); @ISA = qw(GraphViz); our $VERSION = '2.14'; =head1 NAME GraphViz::No - subclass of GraphViz with no nodes =head1 SYNOPSIS use GraphViz::No; my $g = GraphViz::No->new(); # methods as for GraphViz =head1 DESCRIPTION Graphs produced by GraphViz are occasionally huge, making it hard to observe the structure. This subclass removes the nodes, so that only the edges are visible. This allows the structure to stand out. =head1 METHODS As for GraphViz. =cut sub add_node_munge { my $self = shift; my $node = shift; $node->{label} = ''; $node->{height} = 0; $node->{width} = 0; $node->{style} = 'invis'; } sub add_edge_munge { my $self = shift; my $edge = shift; $edge->{color} = rand() . "," . "1,1"; } =head1 AUTHOR Leon Brocard EFE =head1 COPYRIGHT Copyright (C) 2000-1, Leon Brocard This module is free software; you can redistribute it or modify it under the same terms as Perl itself. =cut 1; GraphViz-2.14/lib/GraphViz/Regex.pm000444001750001750 2114212047110055 16031 0ustar00ronron000000000000package GraphViz::Regex; use strict; use warnings; use vars qw($VERSION); use Carp; use Config; use lib '../..'; use lib '..'; use GraphViz; use IPC::Run qw(run); # See perldebguts our $VERSION = '2.14'; my $DEBUG = 0; # whether debugging statements are shown =head1 NAME GraphViz::Regex - Visualise a regular expression =head1 SYNOPSIS use GraphViz::Regex; my $regex = '(([abcd0-9])|(foo))'; my $graph = GraphViz::Regex->new($regex); print $graph->as_png; =head1 DESCRIPTION This module attempts to visualise a Perl regular expression. Understanding regular expressions is tricky at the best of times, and regexess almost always evolve in ways unforseen at the start. This module aims to visualise a regex as a graph in order to make the structure clear and aid in understanding the regex. The graph visualises how the Perl regular expression engine attempts to match the regex. Simple text matches or character classes are represented by.box-shaped nodes. Alternations are represented by a diamond-shaped node which points to the alternations. Repetitions are represented by self-edges with a label of the repetition type (the nodes being repeated are pointed to be a full edge, a dotted edge points to what to match after the repetition). Matched patterns (such as $1, $2, etc.) are represented by a 'START $1' .. 'END $1' node pair. This uses the GraphViz module to draw the graph. =head1 METHODS =head2 new This is the constructor. It takes one mandatory argument, which is a string of the regular expression to be visualised. A GraphViz object is returned. my $graph = GraphViz::Regex->new($regex); =cut sub new { my $proto = shift; my $class = ref($proto) || $proto; my $regex = shift; return _init($regex); } =head2 as_* The regex can be visualised in a number of different graphical formats. Methods include as_ps, as_hpgl, as_pcl, as_mif, as_pic, as_gd, as_gd2, as_gif, as_jpeg, as_png, as_wbmp, as_ismap, as_imap, as_vrml, as_vtx, as_mp, as_fig, as_svg. See the GraphViz documentation for more information. The two most common methods are: # Print out a PNG-format file print $g->as_png; # Print out a PostScript-format file print $g->as_ps; =cut sub _init { my $regex = shift; my $compiled; my $foo; my $perl = $Config{perlpath}; warn "perlpath: $perl\n" if $DEBUG; my $option = qq|use re "debug";qr/$regex/;|; run [$perl], \$option, \$foo, \$compiled; warn "[$compiled]\n" if $DEBUG; # die "Crap" unless $compiled; my $g = GraphViz->new( rankdir => 'LR' ); my %states; my %following; my $last_id; foreach my $line ( split /\n/, $compiled ) { next unless my ( $id, $state ) = $line =~ /(\d+):\s+(.+)$/; $states{$id} = $state; $following{$last_id} = $id if $last_id; $last_id = $id; } my %done; my @todo = (1); warn "last id: $last_id\n" if $DEBUG; if ( not defined $last_id ) { $g->add_node("Error compiling regex"); return $g; } while (@todo) { my $id = pop @todo; next unless $id; next if $done{$id}++; my $state = $states{$id}; my $following = $following{$id}; my ($next) = $state =~ /\((\d+)\)$/; # warn "todo: " . join(", ", @todo) . "\n" if $DEBUG; push @todo, $following; push @todo, $next if $next; my $match; warn "$id:\t$state\n" if $DEBUG; if ( ($match) = $state =~ /^EXACTF?L? <(.+)>/ ) { warn "\t$match $next\n" if $DEBUG; $g->add_node( $id, label => $match, shape => 'box' ); $g->add_edge( $id => $next ) if $next != 0; $done{$following}++ unless $next; } elsif ( ($match) = $state =~ /^ANYOF\[(.+)\]/ ) { warn "\tany $match $next\n" if $DEBUG; $g->add_node( $id, label => '[' . $match . ']', shape => 'box' ); $g->add_edge( $id => $next ) if $next != 0; $done{$following}++ unless $next; } elsif ( ($match) = $state =~ /^OPEN(\d+)/ ) { warn "\tOPEN $match $next\n" if $DEBUG; $g->add_node( $id, label => 'START \$' . $match ); $g->add_edge( $id => $following ); } elsif ( ($match) = $state =~ /^CLOSE(\d+)/ ) { warn "\tCLOSE $match $next\n" if $DEBUG; $g->add_node( $id, label => 'END \$' . $match ); $g->add_edge( $id => $next ); } elsif ( $state =~ /^END/ ) { warn "\tEND\n" if $DEBUG; $g->add_node( $id, label => 'END' ); } elsif ( $state =~ /^BRANCH/ ) { my $branch = $next; warn "\tbranch $branch / " . ($following) . "\n" if $DEBUG; my @children; push @children, $following; while ( $states{$branch} =~ /^BRANCH|TAIL/ ) { warn "\tdoing branch $branch\n" if $DEBUG; $done{$branch}++; push @children, $following{$branch}; ($branch) = $states{$branch} =~ /(\d+)/; } $g->add_node( $id, label => '', shape => 'diamond' ); foreach my $child (@children) { push @todo, $child; $g->add_edge( $id => $child ); } } elsif ( my ($repetition) = $state =~ /^(PLUS|STAR)/ ) { warn "\t$repetition $next\n" if $DEBUG; my $label = '?'; if ( $repetition eq 'PLUS' ) { $label = '+'; } elsif ( $repetition eq 'STAR' ) { $label = '*'; } $g->add_node( $id, label => 'REPEAT' ); $g->add_edge( $id => $id, label => $label ); $g->add_edge( $id => $following ); $g->add_edge( $id => $next, style => 'dashed' ); } elsif ( my ( $type, $min, $max ) = $state =~ /^CURLY([NMX]?)\[?\d*\]? \{(\d+),(\d+)\}/ ) { warn "\tCURLY$type $min $max $next\n" if $DEBUG; $g->add_node( $id, label => 'REPEAT' ); $g->add_edge( $id => $id, label => '{' . $min . ", " . $max . '}' ); $g->add_edge( $id => $following ); $g->add_edge( $id => $next, style => 'dashed' ); } elsif ( $state =~ /^BOL/ ) { warn "\tBOL $next\n" if $DEBUG; $g->add_node( $id, label => '^' ); $g->add_edge( $id => $next ); } elsif ( $state =~ /^EOL/ ) { warn "\tEOL $next\n" if $DEBUG; $g->add_node( $id, label => "\$" ); $g->add_edge( $id => $next ); } elsif ( $state =~ /^NOTHING/ ) { warn "\tNOTHING $next\n" if $DEBUG; $g->add_node( $id, label => 'Match empty string' ); $g->add_edge( $id => $next ); } elsif ( $state =~ /^MINMOD/ ) { warn "\tMINMOD $next\n" if $DEBUG; $g->add_node( $id, label => 'Next operator\nnon-greedy' ); $g->add_edge( $id => $next ); } elsif ( $state =~ /^SUCCEED/ ) { warn "\tSUCCEED $next\n" if $DEBUG; $g->add_node( $id, label => 'SUCCEED' ); $done{$following}++; } elsif ( $state =~ /^UNLESSM/ ) { warn "\tUNLESSM $next\n" if $DEBUG; $g->add_node( $id, label => 'UNLESS' ); $g->add_edge( $id => $following ); $g->add_edge( $id => $next, style => 'dashed' ); } elsif ( $state =~ /^IFMATCH/ ) { warn "\tIFMATCH $next\n" if $DEBUG; $g->add_node( $id, label => 'IFMATCH' ); $g->add_edge( $id => $following ); $g->add_edge( $id => $next, style => 'dashed' ); } elsif ( $state =~ /^IFTHEN/ ) { warn "\tIFTHEN $next\n" if $DEBUG; $g->add_node( $id, label => 'IFTHEN' ); $g->add_edge( $id => $following ); $g->add_edge( $id => $next, style => 'dashed' ); } elsif ( $state =~ /^([A-Z_0-9]+)/ ) { my ($state) = ( $1, $2 ); warn "\t? $state $next\n" if $DEBUG; $g->add_node( $id, label => $state ); $g->add_edge( $id => $next ) if $next != 0; } else { $g->add_node( $id, label => $state ); } } return $g; } =head1 BUGS Note that this module relies on debugging information provided by Perl, and is known to fail on at least two versions of Perl: 5.005_03 and 5.7.1. Sorry about that - please use a more recent version of Perl if you want to use this module. =head1 AUTHOR Leon Brocard EFE =head1 COPYRIGHT Copyright (C) 2000-1, Leon Brocard This module is free software; you can redistribute it or modify it under the same terms as Perl itself. =cut 1; GraphViz-2.14/lib/GraphViz/Data000755001750001750 012047110055 15115 5ustar00ronron000000000000GraphViz-2.14/lib/GraphViz/Data/Grapher.pm000555001750001750 1052612047110055 17227 0ustar00ronron000000000000package GraphViz::Data::Grapher; use strict; use warnings; use vars qw($VERSION); use Carp; use lib '../..'; use lib '..'; use GraphViz; our $VERSION = '2.14'; =head1 NAME GraphViz::Data::Grapher - Visualise data structures as a graph =head1 SYNOPSIS use GraphViz::Data::Grapher; my $graph = GraphViz::Data::Grapher->new($structure); print $graph->as_png; =head1 DESCRIPTION This module makes it easy to visualise Perl data structures. Data structures can grow quite large and it can be hard to understand the quite how the structure fits together. Data::Dumper can help by representing the structure as a text heirarchy, but GraphViz::Data::Grapher goes a step further and visualises the structure by drawing a graph which represents the data structure. Arrays are represented by records. Scalars are represented by themselves. Array references are represented by a '@' symbol, which is linked to the array. Hash references are represented by a '%' symbol, which is linked to an array of keys, which each link to their value. Object references are represented by 'Object', which then links to the type of the object. Undef is represented by 'undef'. =head1 METHODS =head2 new This is the constructor. It takes a list, which is the data structure to be visualised. A GraphViz object is returned. my $graph = GraphViz::Data::Grapher->new([3, 4, 5], "Hello"); =cut sub new { my $proto = shift; my $class = ref($proto) || $proto; my @items = @_; my $graph = GraphViz->new( sort => 1 ); _init( $graph, @items ); return $graph; } =head2 as_* The data structure can be visualised in a number of different graphical formats. Methods include as_ps, as_hpgl, as_pcl, as_mif, as_pic, as_gd, as_gd2, as_gif, as_jpeg, as_png, as_wbmp, as_ismap, as_imap, as_vrml, as_vtx, as_mp, as_fig, as_svg. See the GraphViz documentation for more information. The two most common methods are: # Print out a PNG-format file print $graph->as_png; # Print out a PostScript-format file print $graph->as_ps; =cut sub _init { my ( $graph, @items ) = @_; my @parts; foreach my $item (@items) { push @parts, _label($item); } my $colour = 'black'; $colour = 'blue' if @parts == 1; my $source = $graph->add_node( { label => \@parts, color => $colour } ); foreach my $port ( 0 .. @items - 1 ) { my $item = $items[$port]; #warn "$port = $item\n"; next unless ref $item; my $ref = ref $item; if ( $ref eq 'SCALAR' ) { my $target = _init( $graph, $$item ); $graph->add_edge( { from => $source, from_port => $port, to => $target } ); } elsif ( $ref eq 'ARRAY' ) { my $target = _init( $graph, @$item ); $graph->add_edge( { from => $source, from_port => $port, to => $target } ); } elsif ( $ref eq 'HASH' ) { my @hash; foreach my $key ( sort keys(%$item) ) { push @hash, $key; } my $hash = $graph->add_node( { label => \@hash, color => 'brown' } ); foreach my $port ( 0 .. @hash - 1 ) { my $key = $hash[$port]; my $target = _init( $graph, $item->{$key} ); $graph->add_edge( { from => $hash, from_port => $port, to => $target } ); } $graph->add_edge( { from => $source, from_port => $port, to => $hash } ); } else { my $target = $ref; $ref =~ s/=.+$//; $graph->add_node( { name => $target, label => $ref, color => 'red' } ); $graph->add_edge( { from => $source, from_port => $port, to => $target } ); } } return $source; } sub _label { my $scalar = shift; my $ref = ref $scalar; if ( not defined $scalar ) { return 'undef'; } elsif ( $ref eq 'ARRAY' ) { return '@'; } elsif ( $ref eq 'SCALAR' ) { return '$'; } elsif ( $ref eq 'HASH' ) { return '%'; } elsif ($ref) { return 'Object'; } else { return $scalar; } } =head1 AUTHOR Leon Brocard EFE =head1 COPYRIGHT Copyright (C) 2000-1, Leon Brocard This module is free software; you can redistribute it or modify it under the same terms as Perl itself. =cut 1; GraphViz-2.14/lib/GraphViz/Parse000755001750001750 012047110055 15316 5ustar00ronron000000000000GraphViz-2.14/lib/GraphViz/Parse/Yacc.pm000555001750001750 703212047110055 16675 0ustar00ronron000000000000package GraphViz::Parse::Yacc; use strict; use warnings; use vars qw($VERSION); use Carp; use lib '../..'; use lib '..'; use GraphViz; our $VERSION = '2.14'; =head1 NAME GraphViz::Parse::Yacc - Visualise grammars =head1 SYNOPSIS use GraphViz::Parse::Yacc; # Pass in a file generated via yacc -v my $graph = GraphViz::Parse::Yacc->new('Yacc.output'); print $g->as_png; =head1 DESCRIPTION This module makes it easy to visualise Parse::Yacc grammars. Writing Parse::Yacc grammars is tricky at the best of times, and grammars almost always evolve in ways unforseen at the start. This module aims to visualise a grammar as a graph in order to make the structure clear and aid in understanding the grammar. Rules are represented as nodes, which have their name on the left of the node and their productions on the right of the node. The subrules present in the productions are represented by edges to the subrule nodes. Thus, every node (rule) should be connected to the graph - otherwise a rule is not part of the grammar. This uses the GraphViz module to draw the graph. Thanks to Damian Conway for the original idea. =head1 METHODS =head2 new This is the constructor. It takes one mandatory argument, which is a filename of the output file generated by running "yacc -v " on the grammar file. For example, if your Parse::Yacc grammar file is called "calc.yp", you would run "yacc -v calc.y" and pass in "calc.output" as an argument here. A GraphViz object is returned. # Pass in a file generated via yacc -v my $graph = GraphViz::Parse::Yacc->new('Yacc.output'); print $g->as_png; =cut sub new { my $proto = shift; my $class = ref($proto) || $proto; my $filename = shift; return _init($filename); } =head2 as_* The grammar can be visualised in a number of different graphical formats. Methods include as_ps, as_hpgl, as_pcl, as_mif, as_pic, as_gd, as_gd2, as_gif, as_jpeg, as_png, as_wbmp, as_ismap, as_imap, as_vrml, as_vtx, as_mp, as_fig, as_svg. See the GraphViz documentation for more information. The two most common methods are: # Print out a PNG-format file print $g->as_png; # Print out a PostScript-format file print $g->as_ps; =cut sub _init { my $filename = shift; my ( @links, %edges, %labels, %is_rule ); my $graph = GraphViz->new( concentrate => 1 ); open( IN, $filename ) || carp("Couldn't read file $filename"); my $rule; foreach my $line () { chomp $line; next unless $line =~ /\w/; next unless $line =~ s/^\s+\d+\s+//; if ( $line =~ s/([^ ]+) : ?// ) { $rule = $1; } $line =~ s/\|\s+//; my $text = $line; $is_rule{$rule}++; $text = "(empty)" if $text =~ /^\s*$/; my $rule_label; foreach my $item ( split ' ', $text ) { $edges{$rule}{$item}++; $rule_label .= $item . " "; } $rule_label .= '\n'; $labels{$rule} .= $rule_label; } foreach my $from ( keys %edges ) { next unless $is_rule{$from}; foreach my $to ( keys %{ $edges{$from} } ) { next unless $is_rule{$to}; $graph->add_edge( $from => $to ); } } foreach my $rule ( keys %labels ) { $graph->add_node( $rule, label => [ $rule, $labels{$rule} ] ); } close(IN); return $graph; } =head1 AUTHOR Leon Brocard EFE =head1 COPYRIGHT Copyright (C) 2001, Leon Brocard This module is free software; you can redistribute it or modify it under the same terms as Perl itself. =cut 1; GraphViz-2.14/lib/GraphViz/Parse/Yapp.pm000555001750001750 666012047110055 16735 0ustar00ronron000000000000package GraphViz::Parse::Yapp; use strict; use warnings; use vars qw($VERSION); use Carp; use lib '../..'; use lib '..'; use GraphViz; our $VERSION = '2.14'; =head1 NAME GraphViz::Parse::Yapp - Visualise grammars =head1 SYNOPSIS use GraphViz::Parse::Yapp; # Pass in a file generated via yapp -v my $g = GraphViz::Parse::Yapp->new('Yapp.output'); print $g->as_png; =head1 DESCRIPTION This module makes it easy to visualise Parse::Yapp grammars. Writing Parse::Yapp grammars is tricky at the best of times, and grammars almost always evolve in ways unforseen at the start. This module aims to visualise a grammar as a graph in order to make the structure clear and aid in understanding the grammar. Rules are represented as nodes, which have their name on the left of the node and their productions on the right of the node. The subrules present in the productions are represented by edges to the subrule nodes. Thus, every node (rule) should be connected to the graph - otherwise a rule is not part of the grammar. This uses the GraphViz module to draw the graph. Thanks to Damian Conway for the original idea. =head1 METHODS =head2 new This is the constructor. It takes one mandatory argument, which is a filename of the output file generated by running "yapp -v " on the grammar file. For example, if your Parse::Yapp grammar file is called "calc.yp", you would run "yapp -v calc.yp" and pass in "calc.output" as an argument here. A GraphViz object is returned. # Pass in a file generated via yapp -v my $graph = GraphViz::Parse::Yapp->new('Yapp.output'); print $g->as_png; =cut sub new { my $proto = shift; my $class = ref($proto) || $proto; my $filename = shift; return _init($filename); } =head2 as_* The grammar can be visualised in a number of different graphical formats. Methods include as_ps, as_hpgl, as_pcl, as_mif, as_pic, as_gd, as_gd2, as_gif, as_jpeg, as_png, as_wbmp, as_ismap, as_imap, as_vrml, as_vtx, as_mp, as_fig, as_svg. See the GraphViz documentation for more information. The two most common methods are: # Print out a PNG-format file print $g->as_png; # Print out a PostScript-format file print $g->as_ps; =cut sub _init { my $filename = shift; my ( @links, %edges, %labels, %is_rule ); my $graph = GraphViz->new(); open( IN, $filename ) || carp("Couldn't read file $filename"); foreach my $line () { chomp $line; next unless $line =~ /\w/; next unless $line =~ s/^\d+:\s+//; my ( $rule, $text ) = $line =~ /^(.+) -> (.+)$/; $is_rule{$rule}++; $text = "(empty)" if $text eq '/* empty */'; my $rule_label; foreach my $item ( split ' ', $text ) { $edges{$rule}{$item}++; $rule_label .= $item . " "; } $rule_label .= '\n'; $labels{$rule} .= $rule_label; } foreach my $from ( keys %edges ) { next unless $is_rule{$from}; foreach my $to ( keys %{ $edges{$from} } ) { next unless $is_rule{$to}; $graph->add_edge( $from => $to ); } } foreach my $rule ( keys %labels ) { $graph->add_node( $rule, label => [ $rule, $labels{$rule} ] ); } close(IN); return $graph; } =head1 AUTHOR Leon Brocard EFE =head1 COPYRIGHT Copyright (C) 2001, Leon Brocard This module is free software; you can redistribute it or modify it under the same terms as Perl itself. =cut 1; GraphViz-2.14/lib/GraphViz/Parse/RecDescent.pm000555001750001750 1420412047110055 20054 0ustar00ronron000000000000package GraphViz::Parse::RecDescent; use strict; use warnings; use vars qw($VERSION); use Carp; use lib '../..'; use lib '..'; use GraphViz; use Parse::RecDescent; our $VERSION = '2.14'; =head1 NAME GraphViz::Parse::RecDescent - Visualise grammars =head1 SYNOPSIS use GraphViz::Parse::RecDescent; # Either pass in the grammar my $graph = GraphViz::Parse::RecDescent->new($grammar); print $g->as_png; # or a Parse::RecDescent parser object my $graph = GraphViz::Parse::RecDescent->new($parser); print $g->as_ps; =head1 DESCRIPTION This module makes it easy to visualise Parse::RecDescent grammars. Writing Parse::RecDescent grammars is tricky at the best of times, and grammars almost always evolve in ways unforseen at the start. This module aims to visualise a grammar as a graph in order to make the structure clear and aid in understanding the grammar. Rules are represented as nodes, which have their name on the left of the node and their productions on the right of the node. The subrules present in the productions are represented by edges to the subrule nodes. Thus, every node (rule) should be connected to the graph - otherwise a rule is not part of the grammar. This uses the GraphViz module to draw the graph. Thanks to Damian Conway for the idea. Note that the Parse::RecDescent module should be installed. =head1 METHODS =head2 new This is the constructor. It takes one mandatory argument, which can either be the grammar text or a Parse::RecDescent parser object of the grammar to be visualised. A GraphViz object is returned. # Either pass in the grammar my $graph = GraphViz::Parse::RecDescent->new($grammar); # or a Parse::RecDescent parser object my $graph = GraphViz::Parse::RecDescent->new($parser); =cut sub new { my $proto = shift; my $class = ref($proto) || $proto; my $parser = shift; if ( ref($parser) ne 'Parse::RecDescent' ) { # We got a grammar instead, so we construct our own parser $parser = Parse::RecDescent->new($parser) or carp("Bad grammar"); } return _init($parser); } =head2 as_* The grammar can be visualised in a number of different graphical formats. Methods include as_ps, as_hpgl, as_pcl, as_mif, as_pic, as_gd, as_gd2, as_gif, as_jpeg, as_png, as_wbmp, as_ismap, as_imap, as_vrml, as_vtx, as_mp, as_fig, as_svg. See the GraphViz documentation for more information. The two most common methods are: # Print out a PNG-format file print $g->as_png; # Print out a PostScript-format file print $g->as_ps; =cut # Given a parser object, we look inside its internals and build up a # graph of the rules, productions, and items. This is a tad scary and # hopefully Parse::FastDescent will make this all much easier. sub _init { my $parser = shift; # Our wonderful graph object my $graph = GraphViz->new(); # A grammar consists of rules my %rules = %{ $parser->{rules} }; foreach my $rule ( keys %rules ) { # print "$rule:\n"; my $rule_label; # Rules consist of productions my @productions = @{ $rules{$rule}->{prods} }; foreach my $production (@productions) { my $production_text; # Productions consist of items my @items = @{ $production->{items} }; foreach my $item (@items) { my $text; my $type = ref $item; $type =~ s/^Parse::RecDescent:://; # We ignore Action rules next if $type eq 'Action'; # We could probably use a switch here ;-) if ( $type eq 'Subrule' ) { $text = $item->{subrule}; $text .= $item->{argcode} if defined( $item->{argcode} ); } elsif ( $type =~ /^(Literal|Token|InterpLit)$/ ) { # These are all literals $text = $item->{description}; } elsif ( $type eq 'Error' ) { # We make sure error messages are shown if ( $item->{msg} ) { $text = '{msg} . '>'; } else { $text = ''; } } elsif ( $type eq 'Repetition' ) { # We make sure we show the repetition specifier $text = $item->{subrule} . '(' . $item->{repspec} . ')'; } elsif ( $type eq 'Operator' ) { $text = $item->{expected}; } elsif ( $type =~ /^(Directive|UncondReject)$/ ) { $text = $item->{name}; } else { # It's something we don't know about, so complain! warn "GraphViz::Parse::RecDescent: unknown type $type found!\n"; $text = "?$type?"; } $production_text .= $text . " "; } # print " $production_text\n"; $rule_label .= $production_text . "\\n"; } # Add the node for the current rule $graph->add_node( $rule, label => [ $rule, $rule_label ] ); # Make links to the rules called foreach my $called ( @{ $rules{$rule}->{calls} } ) { $graph->add_edge( $rule => $called ); } } return $graph; } =head1 BUGS Translating the grammar to a graph is accomplished by peeking inside the internals of a parser object, which is a tad scary. A new version of Parse::RecDescent with different internals may break this module. At the moment, almost all Parse::RecDescent directives are supported. If you find one that has been missed - let me know! Unfortunately, alternations (such as the following) do not produce very pretty graphs, due to the fact that they are implicit (unamed) rules and are implemented by new long-named subrules. character: 'the' ( good | bad | ugly ) /dude/ Hopefully Parse::FastDescent will make this all much easier. =head1 AUTHOR Leon Brocard EFE =head1 COPYRIGHT Copyright (C) 2001, Leon Brocard This module is free software; you can redistribute it or modify it under the same terms as Perl itself. =cut 1; GraphViz-2.14/lib/Devel000755001750001750 012047110055 13551 5ustar00ronron000000000000GraphViz-2.14/lib/Devel/GraphVizProf.pm000444001750001750 2502712047110055 16653 0ustar00ronron000000000000package Devel::GraphVizProf; # To help the CPAN indexer to identify us our $VERSION = '2.14'; package DB; use Time::HiRes 'time'; use strict; BEGIN { $DB::drop_zeros = 0; $DB::profile = 1; if (-e '.smallprof') { do '.smallprof'; } $DB::prevf = ''; $DB::prevl = 0; my($diff,$cdiff); my($testDB) = sub { my($pkg,$filename,$line) = caller; $DB::profile || return; %DB::packages && !$DB::packages{$pkg} && return; }; # "Null time" compensation code $DB::nulltime = 0; for (1..100) { my($u,$s,$cu,$cs) = times; $DB::cstart = $u+$s+$cu+$cs; $DB::start = time; &$testDB; ($u,$s,$cu,$cs) = times; $DB::cdone = $u+$s+$cu+$cs; $DB::done = time; $diff = $DB::done - $DB::start; $DB::nulltime += $diff; } $DB::nulltime /= 100; my($u,$s,$cu,$cs) = times; $DB::cstart = $u+$s+$cu+$cs; $DB::start = time; } sub DB { my($pkg,$filename,$line) = caller; $DB::profile || return; %DB::packages && !$DB::packages{$pkg} && return; my($u,$s,$cu,$cs) = times; $DB::cdone = $u+$s+$cu+$cs; $DB::done = time; # Now save the _< array for later reference. If we don't do this here, # evals which do not define subroutines will disappear. no strict 'refs'; $DB::listings{$filename} = \@{"main::_<$filename"} if defined(@{"main::_<$filename"}); use strict 'refs'; # warn $DB::prevl . " -> " . $line . "\n"; # $DB::calls{$DB::prevf}->{$DB::prevl}->{$filename}->{$line}++; $DB::calls{$filename}->{$line}->{$DB::prevf}->{$DB::prevl}++; my($delta); $delta = $DB::done - $DB::start; $delta = ($delta > $DB::nulltime) ? $delta - $DB::nulltime : 0; $DB::profiles{$filename}->[$line]++; $DB::times{$DB::prevf}->[$DB::prevl] += $delta; $DB::ctimes{$DB::prevf}->[$DB::prevl] += ($DB::cdone - $DB::cstart); ($DB::prevf, $DB::prevl) = ($filename, $line); ($u,$s,$cu,$cs) = times; $DB::cstart = $u+$s+$cu+$cs; $DB::start = time; } END { # Get time on last line executed. my($u,$s,$cu,$cs) = times; $DB::cdone = $u+$s+$cu+$cs; $DB::done = time; my($delta); $delta = $DB::done - $DB::start; $delta = ($delta > $DB::nulltime) ? $delta - $DB::nulltime : 0; $DB::times{$DB::prevf}->[$DB::prevl] += $delta; $DB::ctimes{$DB::prevf}->[$DB::prevl] += ($DB::cdone - $DB::cstart); # Now write out the results. # open(OUT,">graphvizprof.dot"); # select OUT; my($i,$stat,$time,$ctime,$line,$file,$page); $page = 1; my %seenlabel; my $maxcalls = 1; my $maxtime = 0; foreach $file (sort keys %DB::profiles) { $- = 0; if (defined($DB::listings{$file})) { $i = -1; foreach $line (@{$DB::listings{$file}}) { ++$i or next; my $time = defined($DB::ctimes{$file}->[$i]) ? $DB::ctimes{$file}->[$i] : 0; $maxtime = $time if $time > $maxtime; foreach my $file (sort keys %{$DB::calls{$file}->{$i}}) { foreach my $j (sort {$a <=> $b} keys %{$DB::calls{$file}->{$i}->{$file}}) { my $calls = $DB::calls{$file}->{$i}->{$file}->{$j}; $maxcalls = $calls if $calls > $maxcalls; } } } } } use GraphViz; my $g = GraphViz->new(); foreach $file (sort keys %DB::profiles) { $- = 0; if (defined($DB::listings{$file})) { $i = -1; foreach $line (@{$DB::listings{$file}}) { ++$i or next; $line = "" unless defined $line; chomp($line); $stat = $DB::profiles{$file}->[$i] || 0 or !$DB::drop_zeros or next; $time = defined($DB::times{$file}->[$i]) ? $DB::times{$file}->[$i] : 0; $ctime = defined($DB::ctimes{$file}->[$i]) ? $DB::ctimes{$file}->[$i] : 0; my $label = getlabel($file . $i); my $name = getname($file, $i); foreach my $file (sort keys %{$DB::calls{$file}->{$i}}) { foreach my $j (sort {$a <=> $b} keys %{$DB::calls{$file}->{$i}->{$file}}) { my $calls = $DB::calls{$file}->{$i}->{$file}->{$j}; # next unless $calls > 2; my $fromlabel = getlabel($file . $j); my $ratio = $ctime / $maxtime; $g->add_node("$file/$name", label => $name, color => "0,1,$ratio") unless ($name =~ m|/| || $seenlabel{"$file/$name"}++); my $fromtime = defined($DB::ctimes{$file}->[$j]) ? $DB::times{$file}->[$j] : 0; $ratio = $fromtime / $maxtime; my $fromname = getname($file, $j); $g->add_node("$file/$fromname", label => $fromname, color => "0,1,$ratio") unless $seenlabel{"$file/$fromname"}++; my $ratio = $calls / $maxcalls; my $w = 100 * (1 - $ratio); $g->add_edge("$file/$fromname" => "$file/$name", color => "0,1,$ratio", w => $w, len => 2); } } } } else { # print "# The code for $file is not in the symbol table."; } } print $g->_as_debug; } sub getname { my($file, $lineno) = @_; # return "$file line $lineno"; my $line = $DB::listings{$file}->[$lineno]; $line = "" unless defined $line; chomp $line; $line =~ s|"|\\"|g; $line =~ s|^\s+||g; # return "$file: $lineno"; # return "$lineno: $line"; return $line; } { my $labelcount; my %label; sub getlabel { my $url = shift; return $label{$url} if exists $label{$url}; $labelcount++; # warn "miss $url\n"; my $label = 'n' . $labelcount; $label{$url} = $label; return $label; } } sub sub { no strict 'refs'; goto &$DB::sub unless $DB::profile; if (defined($DB::sub{$DB::sub})) { my($m,$s) = ($DB::sub{$DB::sub} =~ /.+(?=:)|[^:-]+/g); $DB::profiles{$m}->[$s]++; $DB::listings{$m} = \@{"main::_<$m"} if defined(@{"main::_<$m"}); } goto &$DB::sub; } 1; __END__ =head1 NAME Devel::GraphVizProf - per-line Perl profiler (with graph output) =head1 SYNOPSIS perl -d:GraphVizProf test.pl > test.dot dot -Tpng test.dot > test.png =head1 DESCRIPTION NOTE: This module is a hack of Devel::SmallProf by Ted Ashton. It has been modified by Leon Brocard to produce output for GraphViz, but otherwise the only thing I have done is change the name. I hope to get my patches put into the main Devel::SmallProf code eventually, or alternatively read the output of Devel::SmallProf. Anyway, the normal documentation, which you can probably ignore, follows. The Devel::GraphVizProf profiler is focused on the time taken for a program run on a line-by-line basis. It is intended to be as "small" in terms of impact on the speed and memory usage of the profiled program as possible and also in terms of being simple to use. Those statistics are placed in the file F in the following format: