Graph-Easy-0.73/0000755000076400007640000000000012150110221013260 5ustar shlomifshlomifGraph-Easy-0.73/TODO0000644000076400007640000002220611675050456014002 0ustar shlomifshlomifGraph-Easy ========== See Graph::Easy under LIMITATIONS for some hot topics. In addition: Important short-term TODO: * sort_sub is no longer used in Heap, but the Layouter uses it (find out why) * add for edges: + weight, + taillabel, taillink, tailtitle, headlabel, headlink, headtitle (or should these be startlabel, endlabel etc.?) + a method to set the direction to bidirectional/undirected * graphviz parsing roundtrip: + anon nodes lose their " " label + border-width is wrongly dropped + t/in/dot/9_edge_styles.dot is wrong + nodes with HTML-like labels lose their outer shape (the label itself can have a border on the TABLE, as well as the node outside as well as the individual TD elements) * Combining table cells goes wrong if there is a "hole" in a row of cells. We need to gather them with their coordinates and only combine cells that are next to each other. * setting "size" as class attribute doesn't work * setting "offset: -2,0;" causes problems for multi-row nodes because the offset is taken into effect before growing the node * [ a ] { label: a; } - remove the superflous label upon parsing * VCG/GDL + debug, finish the attribute remapping and add more test cases + implement support for \fn \fb \fI \fu \fB (bold underline etc.) + implement support for \f03 (colors) + implement support for \f- (hor line) + implement full color-remapping support (in both directions) + support subgraphs + support regions + support "nearedges:no" + generally handle all attribute names without "_", too + add support for "anchor" + GDL has portsharing only as attributes for the top graph, while in Graph::Easy this attribute can be set for each edge * layouter: + head/tail label/title and link are currently ignored + implement autosplit and autojoin for edges + don't build chains across groups + route multiple edges to/from a node in the proper order (shortest first) + edges without a specific start/end port should not block ports that are reserved for edges with a start/end port number + placing a node with an origin/offset inside another node results in endless loops as this condition is not checked and the placement of the grandparent node will thus always fail + last-resort placing of node should first try to place it more near to where the edge(s) are connected + allow end/start without specifying a side: "[ A ]--> { end: 0; } [ B ]" + t/in/5_joint.txt - the rendering order is C,A,B, so that the edge from A to Z comes before B to Z. And since the layouter "knows" it should not block the last port on B, it makes a bend. In this case, tho, it could just go along B, because the edges join each other anyway. + handle the special case where a node relative to another belongs to a different group than the parent/child Recursive layouter: + an empty group should consist of one cell (with the label and border) + lay out all groups first, then interlink them together * as_graphviz(): + links to/from empty groups fail + attributes should be always checked against the default attribute and output if necessary, to make setting attributes in classes work - currently doing edge { color: blue; } will be ignored + finish HTML-like labels (esp. with borders) These things seem to be actually not possible in Graphviz: + border-styles: wave, dot-dot-dash and dot-dash + edge-styles: wave, dot-dot-dash and dot-dash + text-styles: underline, overline, strike-through, italic and bold * Parser/Graphviz: + see also the section CAVEATS in Graph::Easy::Parser::Graphviz + style=filled should result in color => fillcolor, not color => fontcolor + parse input in Latin1 charset + parse "A|{ B|C }" (record shape with hor/ver nesting) + nodes with shape record, but an edge going from the aggregate node have the edges rendered in dot starting/ending *somewhere* on the node with the record shape. We always (re-)connect these edges to the first part of the autosplit node. Maybe we should balance them to use parts with as little edges as possible. (The entire feature is quite bogus, since it is not clear from the resulting image where the edge really starts/ends, at the aggregate node or at the specific part where the arrow/line ends up pointing to/from...:-/ + attributes unknown to dot, but valid under Graph::Easy (like "labelpos") cause an error instead of a warning + autosplit nodes (record) lose their attributes, these need to be carried over from the temp. node. + parse nested tables * as_ascii: + better support for different shapes (circle, box, polygon etc) + implement pod-formatted labels (*bold*, /italic/, _underline_, -l-i-n-e-t-h-r-o-u-g-h-, ~overline~, "code") + rendering of "(group)" is empty (need a recursive layouter for that, since the current layouter doesn't add any group cells if a group doesn't have any node or edge at all) * as_html: + fill on edges + v-- and --^ edges (mis-aligned arrows) (complete edge-arrow alignment in HTML) + shift arrows on hor edge end/starts (non-short) left/right, too + output of node-clusters is slightly wrong + there is no space between two nodes placed next (with filler cell) to each other. Make filler cells output a  ? + bidir. self-loops are rendered with only one arrow: [A] <--> [A] + define missing HTML edge pieces: CROSS sections with end/start points + define JOINTs with start/end pieces (6 for each joints, making 24 types) + implement HTML nodes as triangles, house, etc. using slanted edges * fix nesting with pod-formatted labels * edges between groups (ala "( 1 [A ]) -> ( 2 [B] )") or between a node and a group are missing in HTML, ASCII, BOXART and SVG. * It would be good if we could remove Node::Empty (it blocks a cell just to draw the right/bottom border pieces) (we might put these "invisible" nodes into a different "cells" field, which will be rendered, but not queried for path finding etc) Output: * selfloop edges should counter the general flow: Until done +------------+ v | +-------+ +----------------+ +-----+ | Start | --> | Main | --> | End | +-------+ +----------------+ +-----+ versus (loop still going left): Until done +------------+ v | +-----+ +----------------+ +-------+ | End | <-- | Main | <-- | Start | +-----+ +----------------+ +-------+ * support two different arrow shapes on bidirectional edges * as_txt(): + output of node clusters and node chains is not optimal + links between groups are missing * as_ascii() and others: grow cells around point-shaped nodes to intrude: ........................... : : | : : : : : | : : : : : v : : : ........................... : : : : : :-----> : * : <---- : : : : : : : ........................... (at least the edge pieces could omit their left/right spacer in ASCII) * as_boxart has some incorrect corner pieces: echo "[A|B|C||D]" | perl examples/as_boxart ┌───┐───┐───┐ │ A │ B │ C │ └───┘───┘───┘ │ D │ └───┘ echo "[A| |C||D| |E]" |perl examples/as_boxart ┌───┐ ┌───┐ │ A │ │ C │ └───┘ └───┘ │ │ │ │ │ D │ │ E │ └───┘ └───┘ Layout: * allow user to specify max graph width (in cells) to avoid overly wide graphs * auto-grow nodes to be multicelled depending on the dimensions of their label ("main page" gets 2x1, while "a \nb \nc \nd \ne \n" gets 1x2 cells) This currently causes problems and wierd layouts. * Use the seed to generate randomized layouts Rendering/Layout: * allow "align: center, middle|top|bottom" for vertical alignment of labels. * add padding attributes (especially usefull for HTML/SVG output) * add "shape" for groups: + rect + compact (the default, what it is now) + none (no background, no border, no label) * add attribute "opacity" to set alpha channel on entire objects more easily * add attribute "shrink" (yes, no) to nodes to make them as compact as poss. General: * allow multiple subclasses ala CSS: node.red { color: red; } node.green { color: green; } [ Red ] { class: red green; } -> [ Green ] { class: green red; } * Implement more class selectors: + #id (object with ID id) * implement pseudo-class "step" for animations (see POD) * add some possibility to have different fonts, sizes and colors inside one label ala (when labelstyle=pod): FG BG FS<2em|big text> Optimizing: * put framebuffer related routines into own package (Graph::Easy::As_ascii) to avoid the dilemma that we need them from both Node and Graph. Likewise, some routines used by objects (e.g. graph, node etc) should be in a super-package and inherited) * improve the after-layout optimizer * less memory: store border and edge styles as ints instead of "solid" etc Graph-Easy-0.73/lib/0000755000076400007640000000000012150110221014026 5ustar shlomifshlomifGraph-Easy-0.73/lib/Graph/0000755000076400007640000000000012150110221015067 5ustar shlomifshlomifGraph-Easy-0.73/lib/Graph/Easy/0000755000076400007640000000000012150110221015770 5ustar shlomifshlomifGraph-Easy-0.73/lib/Graph/Easy/Parser/0000755000076400007640000000000012150110221017224 5ustar shlomifshlomifGraph-Easy-0.73/lib/Graph/Easy/Parser/Graphviz.pm0000644000076400007640000015753312150071365021411 0ustar shlomifshlomif############################################################################# # Parse graphviz/dot text into a Graph::Easy object # ############################################################################# package Graph::Easy::Parser::Graphviz; $VERSION = '0.17'; use Graph::Easy::Parser; @ISA = qw/Graph::Easy::Parser/; use strict; use utf8; use constant NO_MULTIPLES => 1; use Graph::Easy::Util qw(ord_values); sub _init { my $self = shift; $self->SUPER::_init(@_); $self->{attr_sep} = '='; # remove " " from autosplit (shape=record) labels $self->{_qr_part_clean} = qr/\s*<([^>]*)>/; $self; } sub reset { my $self = shift; $self->SUPER::reset(@_); # set some default attributes on the graph object, because graphviz has # different defaults as Graph::Easy my $g = $self->{_graph}; $g->set_attribute('colorscheme','x11'); $g->set_attribute('flow','south'); $g->set_attribute('edge','arrow-style', 'filled'); $g->set_attribute('group','align', 'center'); $g->set_attribute('group','fill', 'inherit'); $self->{scope_stack} = []; # allow some temp. values during parsing $g->_allow_special_attributes( { node => { shape => [ "", [ qw/ circle diamond edge ellipse hexagon house invisible invhouse invtrapezium invtriangle octagon parallelogram pentagon point triangle trapezium septagon rect rounded none img record/ ], '', '', undef, ], }, } ); $g->{_warn_on_unknown_attributes} = 1; $self; } # map "˜" to "~" my %entities = ( 'amp' => '&', 'quot' => '"', 'lt' => '<', 'gt' => '>', 'nbsp' => ' ', # this is a non-break-space between '' here! 'iexcl' => '¡', 'cent' => '¢', 'pound' => '£', 'curren' => '¤', 'yen' => '¥', 'brvbar' => '¦', 'sect' => '§', 'uml' => '¨', 'copy' => '©', 'ordf' => 'ª', 'ordf' => 'ª', 'laquo' => '«', 'not' => '¬', 'shy' => "\x{00AD}", # soft-hyphen 'reg' => '®', 'macr' => '¯', 'deg' => '°', 'plusmn' => '±', 'sup2' => '²', 'sup3' => '³', 'acute' => '´', 'micro' => 'µ', 'para' => '¶', 'midot' => '·', 'cedil' => '¸', 'sup1' => '¹', 'ordm' => 'º', 'raquo' => '»', 'frac14' => '¼', 'frac12' => '½', 'frac34' => '¾', 'iquest' => '¿', 'Agrave' => 'À', 'Aacute' => 'Á', 'Acirc' => 'Â', 'Atilde' => 'Ã', 'Auml' => 'Ä', 'Aring' => 'Å', 'Aelig' => 'Æ', 'Ccedil' => 'Ç', 'Egrave' => 'È', 'Eacute' => 'É', 'Ecirc' => 'Ê', 'Euml' => 'Ë', 'Igrave' => 'Ì', 'Iacute' => 'Í', 'Icirc' => 'Î', 'Iuml' => 'Ï', 'ETH' => 'Ð', 'Ntilde' => 'Ñ', 'Ograve' => 'Ò', 'Oacute' => 'Ó', 'Ocirc' => 'Ô', 'Otilde' => 'Õ', 'Ouml' => 'Ö', 'times' => '×', 'Oslash' => 'Ø', 'Ugrave' => 'Ù', 'Uacute' => 'Ù', 'Ucirc' => 'Û', 'Uuml' => 'Ü', 'Yacute' => 'Ý', 'THORN' => 'Þ', 'szlig' => 'ß', 'agrave' => 'à', 'aacute' => 'á', 'acirc' => 'â', 'atilde' => 'ã', 'auml' => 'ä', 'aring' => 'å', 'aelig' => 'æ', 'ccedil' => 'ç', 'egrave' => 'è', 'eacute' => 'é', 'ecirc' => 'ê', 'euml' => 'ë', 'igrave' => 'ì', 'iacute' => 'í', 'icirc' => 'î', 'iuml' => 'ï', 'eth' => 'ð', 'ntilde' => 'ñ', 'ograve' => 'ò', 'oacute' => 'ó', 'ocirc' => 'ô', 'otilde' => 'õ', 'ouml' => 'ö', 'divide' => '÷', 'oslash' => 'ø', 'ugrave' => 'ù', 'uacute' => 'ú', 'ucirc' => 'û', 'uuml' => 'ü', 'yacute' => 'ý', 'thorn' => 'þ', 'yuml' => 'ÿ', 'Oelig' => 'Œ', 'oelig' => 'œ', 'Scaron' => 'Š', 'scaron' => 'š', 'Yuml' => 'Ÿ', 'fnof' => 'ƒ', 'circ' => '^', 'tilde' => '~', 'Alpha' => 'Α', 'Beta' => 'Β', 'Gamma' => 'Γ', 'Delta' => 'Δ', 'Epsilon'=> 'Ε', 'Zeta' => 'Ζ', 'Eta' => 'Η', 'Theta' => 'Θ', 'Iota' => 'Ι', 'Kappa' => 'Κ', 'Lambda' => 'Λ', 'Mu' => 'Μ', 'Nu' => 'Ν', 'Xi' => 'Ξ', 'Omicron'=> 'Ο', 'Pi' => 'Π', 'Rho' => 'Ρ', 'Sigma' => 'Σ', 'Tau' => 'Τ', 'Upsilon'=> 'Υ', 'Phi' => 'Φ', 'Chi' => 'Χ', 'Psi' => 'Ψ', 'Omega' => 'Ω', 'alpha' => 'α', 'beta' => 'β', 'gamma' => 'γ', 'delta' => 'δ', 'epsilon'=> 'ε', 'zeta' => 'ζ', 'eta' => 'η', 'theta' => 'θ', 'iota' => 'ι', 'kappa' => 'κ', 'lambda' => 'λ', 'mu' => 'μ', 'nu' => 'ν', 'xi' => 'ξ', 'omicron'=> 'ο', 'pi' => 'π', 'rho' => 'ρ', 'sigma' => 'σ', 'tau' => 'τ', 'upsilon'=> 'υ', 'phi' => 'φ', 'chi' => 'χ', 'psi' => 'ψ', 'omega' => 'ω', 'thetasym'=>'ϑ', 'upsih' => 'ϒ', 'piv' => 'ϖ', 'ensp' => "\x{2003}", # normal wide space 'emsp' => "\x{2004}", # wide space 'thinsp' => "\x{2009}", # very thin space 'zwnj' => "\x{200c}", # zero-width-non-joiner 'zwj' => "\x{200d}", # zero-width-joiner 'lrm' => "\x{200e}", # left-to-right 'rlm' => "\x{200f}", # right-to-left 'ndash' => '–', 'mdash' => '—', 'lsquo' => '‘', 'rsquo' => '’', 'sbquo' => '‚', 'ldquo' => '“', 'rdquo' => '”', 'bdquo' => '„', 'dagger' => '†', 'Dagger' => '‡', 'bull' => '•', 'hellip' => '…', 'permil' => '‰', 'prime' => '′', 'Prime' => '′', 'lsaquo' => '‹', 'rsaquo' => '›', 'oline' => '‾', 'frasl' => '⁄', 'euro' => '€', 'image' => 'ℑ', 'weierp' => '℘', 'real' => 'ℜ', 'trade' => '™', 'alefsym'=> 'ℵ', 'larr' => '←', 'uarr' => '↑', 'rarr' => '→', 'darr' => '↓', 'harr' => '↔', 'crarr' => '↵', 'lArr' => '⇐', 'uArr' => '⇑', 'rArr' => '⇒', 'dArr' => '⇓', 'hArr' => '⇔', 'forall' => '∀', 'part' => '∂', 'exist' => '∃', 'empty' => '∅', 'nabla' => '∇', 'isin' => '∈', 'notin' => '∉', 'ni' => '∋', 'prod' => '∏', 'sum' => '∑', 'minus' => '−', 'lowast' => '∗', 'radic' => '√', 'prop' => '∝', 'infin' => '∞', 'ang' => '∠', 'and' => '∧', 'or' => '∨', 'cap' => '∩', 'cup' => '∪', 'int' => '∫', 'there4' => '∴', 'sim' => '∼', 'cong' => '≅', 'asymp' => '≃', 'ne' => '≠', 'eq' => '=', 'le' => '≤', 'ge' => '≥', 'sub' => '⊂', 'sup' => '⊃', 'nsub' => '⊄', 'nsup' => '⊅', 'sube' => '⊆', 'supe' => '⊇', 'oplus' => '⊕', 'otimes' => '⊗', 'perp' => '⊥', 'sdot' => '⋅', 'lceil' => '⌈', 'rceil' => '⌉', 'lfloor' => '⌊', 'rfloor' => '⌋', 'lang' => '〈', 'rang' => '〉', 'roz' => '◊', 'spades' => '♠', 'clubs' => '♣', 'diamonds'=>'♦', 'hearts' => '♥', ); sub _unquote_attribute { my ($self,$name,$val) = @_; my $html_like = 0; if ($name eq 'label') { $html_like = 1 if $val =~ /^\s*<\s*' => ' ', ' < a > ' => ' a ' if ($html_like == 0 && $val =~ /\s*<(.*)>\s*\z/) { $val = $1; $val = ' ' if $val eq ''; } } my $v = $self->_unquote($val); # Now HTML labels always start with "<", while non-HTML labels # start with " <" or anything else. if ($html_like == 0) { $v = ' ' . $v if $v =~ /^ "foo bar" $name =~ s/^ "((?:\\"|[^"])*)" # "foo" \s*\+\s*"((?:\\"|[^"])*)" # followed by ' + "bar"' /"$1$2"/x while $name =~ /^ "(?:\\"|[^"])*" # "foo" \s*\+\s*"(?:\\"|[^"])*" # followed by ' + "bar"' /x; # map "&!;" to "!" $name =~ s/&(.);/$1/g; # map "&" to "&" $name =~ s/&([^;]+);/$entities{$1} || '';/eg; # "foo bar" => foo bar $name =~ s/^"\s*//; # remove left-over quotes $name =~ s/\s*"\z//; # unquote special chars $name =~ s/\\([\[\(\{\}\]\)#"])/$1/g; $name; } sub _clean_line { # do some cleanups on a line before handling it my ($self,$line) = @_; chomp($line); # collapse white space at start $line =~ s/^\s+//; # line ending in '\' means a continuation $line =~ s/\\\z//; $line; } sub _line_insert { # "a1 -> a2\na3 -> a4" => "a1 -> a2 a3 -> a4" ' '; } ############################################################################# sub _match_boolean { # not used yet, match a boolean value qr/(true|false|\d+)/; } sub _match_comment { # match the start of a comment # // comment qr#(:[^\\]|)//#; } sub _match_multi_line_comment { # match a multi line comment # /* * comment * */ qr#(?:\s*/\*.*?\*/\s*)+#; } sub _match_optional_multi_line_comment { # match a multi line comment # "/* * comment * */" or /* a */ /* b */ or "" qr#(?:(?:\s*/\*.*?\*/\s*)*|\s+)#; } sub _match_name { # Return a regexp that matches an ID in the DOT language. # See http://www.graphviz.org/doc/info/lang.html for reference. # "node", "graph", "edge", "digraph", "subgraph" and "strict" are reserved: qr/\s* ( # double quoted string "(?:\\"|[^"])*" # "foo" (?:\s*\+\s*"(?:\\"|[^"])*")* # followed by 0 or more ' + "bar"' | # number -? # optional minus sign (?: # non-capture group \.[0-9]+ # .00019 | # or [0-9]+(?:\.[0-9]*)? # 123 or 123.1 ) | # plain node name (a-z0-9_+) (?!(?i:node|edge|digraph|subgraph|graph|strict)\s)[\w]+ )/xi; } sub _match_node { # Return a regexp that matches something like '"bonn"' or 'bonn' or 'bonn:f1' my $self = shift; my $qr_n = $self->_match_name(); # Examples: "bonn", "Bonn":f1, "Bonn":"f1", "Bonn":"port":"w", Bonn:port:w qr/ $qr_n # node name (see _match_name) (?: :$qr_n (?: :(n|ne|e|se|s|sw|w|nw) )? # :port:compass_direction | :(n|ne|e|se|s|sw|w|nw) # :compass_direction )? # optional /x; } sub _match_group_start { # match a subgraph at the beginning (f.i. "graph { ") my $self = shift; my $qr_n = $self->_match_name(); qr/^\s*(?:strict\s+)?(?:(?i)digraph|subgraph|graph)\s+$qr_n\s*\{/i; } sub _match_pseudo_group_start_at_beginning { # match an anonymous group start at the beginning (aka " { ") qr/^\s*\{/; } sub _match_pseudo_group_start { # match an anonymous group start (aka " { ") qr/\s*\{/; } sub _match_group_end { # return a regexp that matches something like " }" or "} ;". qr/^\s*\}\s*;?\s*/; } sub _match_edge { # Matches an edge qr/\s*(->|--)/; } sub _match_html_regexps { # Return hash with regexps matching different parts of an HTML label. my $qr = { # BORDER="2" attribute => qr/\s*([A-Za-z]+)\s*=\s*"((?:\\"|[^"])*)"/, # BORDER="2" COLSPAN="2" attributes => qr/(?:\s+(?:[A-Za-z]+)\s*=\s*"(?:\\"|[^"])*")*/, text => qr/.*?/, tr => qr/\s*/i, tr_end => qr/\s*<\/TR>/i, td => qr/\s*]*>/i, td_tag => qr/\s* qr/\s*<\/TD>/i, table => qr/\s*]*>/i, table_tag => qr/\s* qr/\s*<\/TABLE>/i, }; $qr->{row} = qr/$qr->{tr}(?:$qr->{td}$qr->{text}$qr->{td_end})*$qr->{tr_end}/; $qr; } sub _match_html { # build a giant regular expression that matches an HTML label # label=< # # # #
port
port2port3
> my $qr = _match_html_regexps(); # < ..
> qr/<$qr->{table}(?:$qr->{row})*$qr->{table_end}\s*>/; } sub _match_single_attribute { my $qr_html = _match_html(); qr/\s*(\w+)\s*=\s* # the attribute name (label=") ( "(?:\\"|[^"])*" # "foo" (?:\s*\+\s*"(?:\\"|[^"])*")* # followed by 0 or more ' + "bar"' | $qr_html # or < ..<\/TABLE> > | <[^>]*> # or something like < a > | [^<][^,\]\}\n\s;]* # or simple 'fooobar' ) [,\]\n\}\s;]?\s*/x; # possible ",", "\n" etc. } sub _match_special_attribute { # match boolean attributes, these can appear without a value qr/\s*( center| compound| concentrate| constraint| decorate| diredgeconstraints| fixedsize| headclip| labelfloat| landscape| mosek| nojustify| normalize| overlap| pack| pin| regular| remincross| root| splines| tailclip| truecolor )[,;\s]?\s*/x; } sub _match_attributes { # return a regexp that matches something like " [ color=red; ]" and returns # the inner text without the [] my $qr_att = _match_single_attribute(); my $qr_satt = _match_special_attribute(); my $qr_cmt = _match_multi_line_comment(); qr/\s*\[\s*((?:$qr_att|$qr_satt|$qr_cmt)*)\s*\];?/; } sub _match_graph_attribute { # return a regexp that matches something like " color=red; " for attributes # that apply to a graph/subgraph qr/^\s*(\w+\s*=\s*("[^"]+"|[^;\n\s]+))([;\n\s]\s*|\z)/; } sub _match_optional_attributes { # return a regexp that matches something like " [ color=red; ]" and returns # the inner text with the [] my $qr_att = _match_single_attribute(); my $qr_satt = _match_special_attribute(); my $qr_cmt = _match_multi_line_comment(); qr/\s*(\[\s*((?:$qr_att|$qr_satt|$qr_cmt)*)\s*\])?;?/; } sub _clean_attributes { my ($self,$text) = @_; $text =~ s/^\s*\[\s*//; # remove left-over "[" and spaces $text =~ s/\s*;?\s*\]\s*\z//; # remove left-over "]" and spaces $text; } ############################################################################# sub _new_scope { # create a new scope, with attributes from current scope my ($self, $is_group) = @_; my $scope = {}; if (@{$self->{scope_stack}} > 0) { my $old_scope = $self->{scope_stack}->[-1]; # make a copy of the old scope's attributes for my $t (sort keys %$old_scope) { next if $t =~ /^_/; my $s = $old_scope->{$t}; $scope->{$t} = {} unless ref $scope->{$t}; my $sc = $scope->{$t}; for my $k (sort keys %$s) { # skip things like "_is_group" $sc->{$k} = $s->{$k} unless $k =~ /^_/; } } } $scope->{_is_group} = 1 if defined $is_group; push @{$self->{scope_stack}}, $scope; $scope; } sub _add_group_match { # register handlers for group start/end my $self = shift; my $qr_pseudo_group_start = $self->_match_pseudo_group_start_at_beginning(); my $qr_group_start = $self->_match_group_start(); my $qr_group_end = $self->_match_group_end(); my $qr_edge = $self->_match_edge(); my $qr_ocmt = $self->_match_optional_multi_line_comment(); # "subgraph G {" $self->_register_handler( $qr_group_start, sub { my $self = shift; my $graph = $self->{_graph}; my $gn = $self->_unquote($1); print STDERR "# Parser: found subcluster '$gn'\n" if $self->{debug}; push @{$self->{group_stack}}, $self->_new_group($gn); $self->_new_scope( 1 ); 1; } ); # "{ " $self->_register_handler( $qr_pseudo_group_start, sub { my $self = shift; print STDERR "# Parser: Creating new scope\n" if $self->{debug}; $self->_new_scope(); # forget the left side $self->{left_edge} = undef; $self->{left_stack} = [ ]; 1; } ); # "} -> " group/cluster/scope end with an edge $self->_register_handler( qr/$qr_group_end$qr_ocmt$qr_edge/, sub { my $self = shift; my $scope = pop @{$self->{scope_stack}}; return $self->parse_error(0) if !defined $scope; if ($scope->{_is_group} && @{$self->{group_stack}}) { print STDERR "# Parser: end subcluster '$self->{group_stack}->[-1]->{name}'\n" if $self->{debug}; pop @{$self->{group_stack}}; } else { print STDERR "# Parser: end scope\n" if $self->{debug}; } 1; }, sub { my ($self, $line) = @_; $line =~ qr/$qr_group_end$qr_edge/; $1 . ' '; } ); # "}" group/cluster/scope end $self->_register_handler( $qr_group_end, sub { my $self = shift; my $scope = pop @{$self->{scope_stack}}; return $self->parse_error(0) if !defined $scope; if ($scope->{_is_group} && @{$self->{group_stack}}) { print STDERR "# Parser: end subcluster '$self->{group_stack}->[-1]->{name}'\n" if $self->{debug}; pop @{$self->{group_stack}}; } # always reset the stack $self->{stack} = [ ]; 1; } ); } sub _edge_style { # To convert "--" or "->" we simple do nothing, since the edge style in # Graphviz can only be set via the attribute "style" my ($self, $ed) = @_; 'solid'; } sub _new_nodes { my ($self, $name, $group_stack, $att, $port, $stack) = @_; $port = '' unless defined $port; my @rc = (); # "name1" => "name1" if ($port ne '') { # create a special node $name =~ s/^"//; $name =~ s/"\z//; $port =~ s/^"//; $port =~ s/"\z//; # XXX TODO: find unique name? @rc = $self->_new_node ($self->{_graph}, "$name:$port", $group_stack, $att, $stack); my $node = $rc[0]; $node->{_graphviz_portlet} = $port; $node->{_graphviz_basename} = $name; } else { @rc = $self->_new_node ($self->{_graph}, $name, $group_stack, $att, $stack); } @rc; } sub _build_match_stack { my $self = shift; my $qr_node = $self->_match_node(); my $qr_name = $self->_match_name(); my $qr_cmt = $self->_match_multi_line_comment(); my $qr_ocmt = $self->_match_optional_multi_line_comment(); my $qr_attr = $self->_match_attributes(); my $qr_gatr = $self->_match_graph_attribute(); my $qr_oatr = $self->_match_optional_attributes(); my $qr_edge = $self->_match_edge(); my $qr_pgr = $self->_match_pseudo_group_start(); # remove multi line comments /* comment */ $self->_register_handler( qr/^$qr_cmt/, undef ); # remove single line comment // comment $self->_register_handler( qr/^\s*\/\/.*/, undef ); # simple remove the graph start, but remember that we did this $self->_register_handler( qr/^\s*((?i)strict)?$qr_ocmt((?i)digraph|graph)$qr_ocmt$qr_node$qr_ocmt\{/, sub { my $self = shift; return $self->parse_error(6) if @{$self->{scope_stack}} > 0; $self->{_graphviz_graph_name} = $3; $self->_new_scope(1); $self->{_graph}->set_attribute('type','undirected') if lc($2) eq 'graph'; 1; } ); # simple remove the graph start, but remember that we did this $self->_register_handler( qr/^\s*(strict)?$qr_ocmt(di)?graph$qr_ocmt\{/i, sub { my $self = shift; return $self->parse_error(6) if @{$self->{scope_stack}} > 0; $self->{_graphviz_graph_name} = 'unnamed'; $self->_new_scope(1); $self->{_graph}->set_attribute('type','undirected') if lc($2) ne 'di'; 1; } ); # end-of-statement $self->_register_handler( qr/^\s*;/, undef ); # cluster/subgraph "subgraph G { .. }" # scope (dummy group): "{ .. }" # scope/group/subgraph end: "}" $self->_add_group_match(); # node [ color="red" ] etc. # The "(?i)" makes the keywords match case-insensitive. $self->_register_handler( qr/^\s*((?i)node|graph|edge)$qr_ocmt$qr_attr/, sub { my $self = shift; my $type = lc($1 || ''); my $att = $self->_parse_attributes($2 || '', $type, NO_MULTIPLES ); return undef unless defined $att; # error in attributes? if ($type ne 'graph') { # apply the attributes to the current scope my $scope = $self->{scope_stack}->[-1]; $scope->{$type} = {} unless ref $scope->{$type}; my $s = $scope->{$type}; for my $k (sort keys %$att) { $s->{$k} = $att->{$k}; } } else { my $graph = $self->{_graph}; $graph->set_attributes ($type, $att); } # forget stacks $self->{stack} = []; $self->{left_edge} = undef; $self->{left_stack} = []; 1; } ); # color=red; (for graphs or subgraphs) $self->_register_attribute_handler($qr_gatr, 'parent'); # [ color=red; ] (for nodes/edges) $self->_register_attribute_handler($qr_attr); # node chain continued like "-> { ... " $self->_register_handler( qr/^$qr_edge$qr_ocmt$qr_pgr/, sub { my $self = shift; return if @{$self->{stack}} == 0; # only match this if stack non-empty my $graph = $self->{_graph}; my $eg = $1; # entire edge ("->" etc) my $edge_un = 0; $edge_un = 1 if $eg eq '--'; # undirected edge? # need to defer edge attribute parsing until the edge exists # if inside a scope, set the scope attributes, too: my $scope = $self->{scope_stack}->[-1] || {}; my $edge_atr = $scope->{edge} || {}; # create a new scope $self->_new_scope(); # remember the left side $self->{left_edge} = [ 'solid', '', $edge_atr, 0, $edge_un ]; $self->{left_stack} = $self->{stack}; # forget stack and remember the right side instead $self->{stack} = []; 1; } ); # "Berlin" $self->_register_handler( qr/^$qr_node/, sub { my $self = shift; my $graph = $self->{_graph}; # only match this inside a "{ }" (normal, non-group) scope return if exists $self->{scope_stack}->[-1]->{_is_group}; my $n1 = $1; my $port = $2; push @{$self->{stack}}, $self->_new_nodes ($n1, $self->{group_stack}, {}, $port, $self->{stack}); if (defined $self->{left_edge}) { my $e = $self->{use_class}->{edge}; my ($style, $edge_label, $edge_atr, $edge_bd, $edge_un) = @{$self->{left_edge}}; foreach my $node (@{$self->{left_stack}}) { my $edge = $e->new( { style => $style, name => $edge_label } ); # if inside a scope, set the scope attributes, too: my $scope = $self->{scope_stack}->[-1]; $edge->set_attributes($scope->{edge}) if $scope; # override with the local attributes # 'string' => [ 'string' ] # [ { hash }, 'string' ] => [ { hash }, 'string' ] my $e = $edge_atr; $e = [ $edge_atr ] unless ref($e) eq 'ARRAY'; for my $a (@$e) { if (ref $a) { $edge->set_attributes($a); } else { # deferred parsing with the object as param: my $out = $self->_parse_attributes($a, $edge, NO_MULTIPLES); return undef unless defined $out; # error in attributes? $edge->set_attributes($out); } } # "<--->": bidirectional $edge->bidirectional(1) if $edge_bd; $edge->undirected(1) if $edge_un; $graph->add_edge ( $node, $self->{stack}->[-1], $edge ); } } 1; } ); # "Berlin" [ color=red ] or "Bonn":"a" [ color=red ] $self->_register_handler( qr/^$qr_node$qr_oatr/, sub { my $self = shift; my $name = $1; my $port = $2; my $compass = $4 || ''; $port .= ":$compass" if $compass; $self->{stack} = [ $self->_new_nodes ($name, $self->{group_stack}, {}, $port ) ]; # defer attribute parsing until object exists my $node = $self->{stack}->[0]; my $a1 = $self->_parse_attributes($5||'', $node); return undef if $self->{error}; $node->set_attributes($a1); # forget left stack $self->{left_edge} = undef; $self->{left_stack} = []; 1; } ); # Things like ' "Node" ' will be consumed before, so we do not need a case # for '"Bonn" -> "Berlin"' # node chain continued like "-> "Kassel" [ ... ]" $self->_register_handler( qr/^$qr_edge$qr_ocmt$qr_node$qr_ocmt$qr_oatr/, sub { my $self = shift; return if @{$self->{stack}} == 0; # only match this if stack non-empty my $graph = $self->{_graph}; my $eg = $1; # entire edge ("->" etc) my $n = $2; # node name my $port = $3; my $compass = $4 || $5 || ''; $port .= ":$compass" if $compass; my $edge_un = 0; $edge_un = 1 if $eg eq '--'; # undirected edge? my $scope = $self->{scope_stack}->[-1] || {}; # need to defer edge attribute parsing until the edge exists my $edge_atr = [ $6||'', $scope->{edge} || {} ]; # the right side nodes: my $nodes_b = [ $self->_new_nodes ($n, $self->{group_stack}, {}, $port) ]; my $style = $self->_link_lists( $self->{stack}, $nodes_b, '--', '', $edge_atr, 0, $edge_un); # remember the left side $self->{left_edge} = [ $style, '', $edge_atr, 0, $edge_un ]; $self->{left_stack} = $self->{stack}; # forget stack and remember the right side instead $self->{stack} = $nodes_b; 1; } ); $self; } sub _add_node { # add a node to the graph, overridable by subclasses my ($self, $graph, $name) = @_; # "a -- clusterB" should not create a spurious node named "clusterB" my @groups = $graph->groups(); for my $g (@groups) { return $g if $g->{name} eq $name; } my $node = $graph->node($name); if (!defined $node) { $node = $graph->add_node($name); # add # apply attributes from the current scope (only for new nodes) my $scope = $self->{scope_stack}->[-1]; return $self->error("Scope stack is empty!") unless defined $scope; my $is_group = $scope->{_is_group}; delete $scope->{_is_group}; $node->set_attributes($scope->{node}); $scope->{_is_group} = $is_group if $is_group; } $node; } ############################################################################# # attribute remapping # undef => drop that attribute # not listed attributes will result in "x-dot-$attribute" and a warning my $remap = { 'node' => { 'distortion' => 'x-dot-distortion', 'fixedsize' => undef, 'group' => 'x-dot-group', 'height' => 'x-dot-height', # XXX TODO: ignore non-node attributes set in a scope 'dir' => undef, 'layer' => 'x-dot-layer', 'margin' => 'x-dot-margin', 'orientation' => \&_from_graphviz_node_orientation, 'peripheries' => \&_from_graphviz_node_peripheries, 'pin' => 'x-dot-pin', 'pos' => 'x-dot-pos', # XXX TODO: rank=0 should make that node the root node # 'rank' => undef, 'rects' => 'x-dot-rects', 'regular' => 'x-dot-regular', # 'root' => undef, 'sides' => 'x-dot-sides', 'shapefile' => 'x-dot-shapefile', 'shape' => \&_from_graphviz_node_shape, 'skew' => 'x-dot-skew', 'style' => \&_from_graphviz_style, 'width' => 'x-dot-width', 'z' => 'x-dot-z', }, 'edge' => { 'arrowsize' => 'x-dot-arrowsize', 'arrowhead' => \&_from_graphviz_arrow_style, 'arrowtail' => 'x-dot-arrowtail', # important for color lists like "red:red" => double edge 'color' => \&_from_graphviz_edge_color, 'constraint' => 'x-dot-constraint', 'dir' => \&_from_graphviz_edge_dir, 'decorate' => 'x-dot-decorate', 'f' => 'x-dot-f', 'headclip' => 'x-dot-headclip', 'headhref' => 'headlink', 'headurl' => 'headlink', 'headport' => \&_from_graphviz_headport, 'headlabel' => 'headlabel', 'headtarget' => 'x-dot-headtarget', 'headtooltip' => 'headtitle', 'labelangle' => 'x-dot-labelangle', 'labeldistance' => 'x-dot-labeldistance', 'labelfloat' => 'x-dot-labelfloat', 'labelfontcolor' => \&_from_graphviz_color, 'labelfontname' => 'font', 'labelfontsize' => 'font-size', 'layer' => 'x-dot-layer', 'len' => 'x-dot-len', 'lhead' => 'x-dot-lhead', 'ltail' => 'x-dot-tail', 'minlen' => \&_from_graphviz_edge_minlen, 'pos' => 'x-dot-pos', 'samehead' => 'x-dot-samehead', 'samearrowhead' => 'x-dot-samearrowhead', 'sametail' => 'x-dot-sametail', 'style' => \&_from_graphviz_edge_style, 'tailclip' => 'x-dot-tailclip', 'tailhref' => 'taillink', 'tailurl' => 'taillink', 'tailport' => \&_from_graphviz_tailport, 'taillabel' => 'taillabel', 'tailtarget' => 'x-dot-tailtarget', 'tailtooltip' => 'tailtitle', 'weight' => 'x-dot-weight', }, 'graph' => { 'damping' => 'x-dot-damping', 'K' => 'x-dot-k', 'bb' => 'x-dot-bb', 'center' => 'x-dot-center', # will be handled automatically: 'charset' => undef, 'clusterrank' => 'x-dot-clusterrank', 'compound' => 'x-dot-compound', 'concentrate' => 'x-dot-concentrate', 'defaultdist' => 'x-dot-defaultdist', 'dim' => 'x-dot-dim', 'dpi' => 'x-dot-dpi', 'epsilon' => 'x-dot-epsilon', 'esep' => 'x-dot-esep', 'fontpath' => 'x-dot-fontpath', 'labeljust' => \&_from_graphviz_graph_labeljust, 'labelloc' => \&_from_graphviz_labelloc, 'landscape' => 'x-dot-landscape', 'layers' => 'x-dot-layers', 'layersep' => 'x-dot-layersep', 'levelsgap' => 'x-dot-levelsgap', 'margin' => 'x-dot-margin', 'maxiter' => 'x-dot-maxiter', 'mclimit' => 'x-dot-mclimit', 'mindist' => 'x-dot-mindist', 'minquit' => 'x-dot-minquit', 'mode' => 'x-dot-mode', 'model' => 'x-dot-model', 'nodesep' => 'x-dot-nodesep', 'normalize' => 'x-dot-normalize', 'nslimit' => 'x-dot-nslimit', 'nslimit1' => 'x-dot-nslimit1', 'ordering' => 'x-dot-ordering', 'orientation' => 'x-dot-orientation', 'output' => 'output', 'outputorder' => 'x-dot-outputorder', 'overlap' => 'x-dot-overlap', 'pack' => 'x-dot-pack', 'packmode' => 'x-dot-packmode', 'page' => 'x-dot-page', 'pagedir' => 'x-dot-pagedir', 'pencolor' => \&_from_graphviz_color, 'quantum' => 'x-dot-quantum', 'rankdir' => \&_from_graphviz_graph_rankdir, 'ranksep' => 'x-dot-ranksep', 'ratio' => 'x-dot-ratio', 'remincross' => 'x-dot-remincross', 'resolution' => 'x-dot-resolution', 'rotate' => 'x-dot-rotate', 'samplepoints' => 'x-dot-samplepoints', 'searchsize' => 'x-dot-searchsize', 'sep' => 'x-dot-sep', 'size' => 'x-dot-size', 'splines' => 'x-dot-splines', 'start' => 'x-dot-start', 'style' => \&_from_graphviz_style, 'stylesheet' => 'x-dot-stylesheet', 'truecolor' => 'x-dot-truecolor', 'viewport' => 'x-dot-viewport', 'voro-margin' => 'x-dot-voro-margin', }, 'group' => { 'labeljust' => \&_from_graphviz_graph_labeljust, 'labelloc' => \&_from_graphviz_labelloc, 'pencolor' => \&_from_graphviz_color, 'style' => \&_from_graphviz_style, 'K' => 'x-dot-k', }, 'all' => { 'color' => \&_from_graphviz_color, 'colorscheme' => 'x-colorscheme', 'bgcolor' => \&_from_graphviz_color, 'fillcolor' => \&_from_graphviz_color, 'fontsize' => \&_from_graphviz_font_size, 'fontcolor' => \&_from_graphviz_color, 'fontname' => 'font', 'lp' => 'x-dot-lp', 'nojustify' => 'x-dot-nojustify', 'rank' => 'x-dot-rank', 'showboxes' => 'x-dot-showboxes', 'target' => 'x-dot-target', 'tooltip' => 'title', 'URL' => 'link', 'href' => 'link', }, }; sub _remap { $remap; } my $rankdir = { 'LR' => 'east', 'RL' => 'west', 'TB' => 'south', 'BT' => 'north', }; sub _from_graphviz_graph_rankdir { my ($self, $name, $dir, $object) = @_; my $d = $rankdir->{$dir} || 'east'; ('flow', $d); } my $shapes = { box => 'rect', polygon => 'rect', egg => 'rect', rectangle => 'rect', mdiamond => 'diamond', msquare => 'rect', plaintext => 'none', none => 'none', }; sub _from_graphviz_node_shape { my ($self, $name, $shape) = @_; my @rc; my $s = lc($shape); if ($s =~ /^(triple|double)/) { $s =~ s/^(triple|double)//; push @rc, ('border-style','double'); } # map the name to what Graph::Easy expects (ellipse stays as ellipse f.i.) $s = $shapes->{$s} || $s; (@rc, $name, $s); } sub _from_graphviz_style { my ($self, $name, $style, $class) = @_; my @styles = split /\s*,\s*/, $style; my $is_node = 0; $is_node = 1 if ref($class) && !$class->isa('Graph::Easy::Group'); $is_node = 1 if !ref($class) && defined $class && $class eq 'node'; my @rc; for my $s (@styles) { @rc = ('shape', 'rounded') if $s eq 'rounded'; @rc = ('shape', 'invisible') if $s eq 'invis'; @rc = ('border', 'black ' . $1) if $s =~ /^(bold|dotted|dashed)\z/; if ($is_node != 0) { @rc = ('shape', 'rect') if $s eq 'filled'; } # convert "setlinewidth(12)" => if ($s =~ /setlinewidth\((\d+|\d*\.\d+)\)/) { my $width = abs($1 || 1); my $style = ''; $style = 'wide'; # > 11 $style = 'solid' if $width < 3; $style = 'bold' if $width >= 3 && $width < 5; $style = 'broad' if $width >= 5 && $width < 11; push @rc, ('borderstyle',$style); } } @rc; } sub _from_graphviz_node_orientation { my ($self, $name, $o) = @_; my $r = int($o); return (undef,undef) if $r == 0; # 1.0 => 1 ('rotate', $r); } my $port_remap = { n => 'north', e => 'east', w => 'west', s => 'south', }; sub _from_graphviz_headport { my ($self, $name, $compass) = @_; # XXX TODO # handle "port:compass" too # one of "n","ne","e","se","s","sw","w","nw # "ne => n" my $c = $port_remap->{ substr(lc($compass),0,1) } || 'east'; ('end', $c); } sub _from_graphviz_tailport { my ($self, $name, $compass) = @_; # XXX TODO # handle "port:compass" too # one of "n","ne","e","se","s","sw","w","nw # "ne => n" => "north" my $c = $port_remap->{ substr(lc($compass),0,1) } || 'east'; ('start', $c); } sub _from_graphviz_node_peripheries { my ($self, $name, $cnt) = @_; return (undef,undef) if $cnt < 2; # peripheries = 2 => double border ('border-style', 'double'); } sub _from_graphviz_edge_minlen { my ($self, $name, $len) = @_; # 1 => 1, 2 => 3, 3 => 5 etc $len = $len * 2 - 1; ($name, $len); } sub _from_graphviz_font_size { my ($self, $f, $size) = @_; # 20 => 20px $size = $size . 'px' if $size =~ /^\d+(\.\d+)?\z/; ('fontsize', $size); } sub _from_graphviz_labelloc { my ($self, $name, $loc) = @_; my $l = 'top'; $l = 'bottom' if $loc =~ /^b/; ('labelpos', $l); } sub _from_graphviz_edge_dir { my ($self, $name, $dir, $edge) = @_; # Modify the edge, depending on dir if (ref($edge)) { # "forward" is the default and ignored $edge->flip() if $dir eq 'back'; $edge->bidirectional(1) if $dir eq 'both'; $edge->undirected(1) if $dir eq 'none'; } (undef, undef); } sub _from_graphviz_edge_style { my ($self, $name, $style, $object) = @_; # input: solid dashed dotted bold invis $style = 'invisible' if $style eq 'invis'; # although "normal" is not documented, it occurs in the wild $style = 'solid' if $style eq 'normal'; # convert "setlinewidth(12)" => if ($style =~ /setlinewidth\((\d+|\d*\.\d+)\)/) { my $width = abs($1 || 1); $style = 'wide'; # > 11 $style = 'solid' if $width < 3; $style = 'bold' if $width >= 3 && $width < 5; $style = 'broad' if $width >= 5 && $width < 11; } ($name, $style); } sub _from_graphviz_arrow_style { my ($self, $name, $shape, $object) = @_; my $style = 'open'; $style = 'closed' if $shape =~ /^(empty|onormal)\z/; $style = 'filled' if $shape eq 'normal' || $shape eq 'normalnormal'; $style = 'open' if $shape eq 'vee' || $shape eq 'veevee'; $style = 'none' if $shape eq 'none' || $shape eq 'nonenone'; ('arrow-style', $style); } my $color_atr_map = { fontcolor => 'color', bgcolor => 'background', fillcolor => 'fill', pencolor => 'bordercolor', labelfontcolor => 'labelcolor', color => 'color', }; sub _from_graphviz_color { # Remap the color name and value my ($self, $name, $color) = @_; # "//red" => "red" $color =~ s/^\/\///; my $colorscheme = 'x11'; if ($color =~ /^\//) { # "/set9/red" => "red" $color =~ s/^\/([^\/]+)\///; $colorscheme = $1; # map the color to the right color according to the colorscheme $color = Graph::Easy->color_value($color,$colorscheme) || 'black'; } # "#AA BB CC => "#AABBCC" $color =~ s/\s+//g if $color =~ /^#/; # "0.1 0.4 0.5" => "hsv(0.1,0.4,0.5)" $color =~ s/\s+/,/g if $color =~ /\s/; $color = 'hsv(' . $color . ')' if $color =~ /,/; ($color_atr_map->{$name}, $color); } sub _from_graphviz_edge_color { # remap the color name and value my ($self, $name, $color) = @_; my @colors = split /:/, $color; for my $c (@colors) { $c = Graph::Easy::Parser::Graphviz::_from_graphviz_color($self,$name,$c); } my @rc; if (@colors > 1) { # 'red:blue' => "style: double; color: red" push @rc, 'style', 'double'; } (@rc, $color_atr_map->{$name}, $colors[0]); } sub _from_graphviz_graph_labeljust { my ($self, $name, $l) = @_; # input: "l" "r" or "c", output "left", "right" or "center" my $a = 'center'; $a = 'left' if $l eq 'l'; $a = 'right' if $l eq 'r'; ('align', $a); } ############################################################################# sub _remap_attributes { my ($self, $att, $object, $r) = @_; if ($self->{debug}) { my $o = ''; $o = " for $object" if $object; print STDERR "# remapping attributes '$att'$o\n"; require Data::Dumper; print STDERR "#" , Data::Dumper::Dumper($att),"\n"; } $r = $self->_remap() unless defined $r; $self->{_graph}->_remap_attributes($object, $att, $r, 'noquote', undef, undef); } ############################################################################# my $html_remap = { 'table' => { 'align' => 'align', 'balign' => undef, 'bgcolor' => 'fill', 'border' => 'border', # XXX TODO 'cellborder' => 'border', 'cellspacing' => undef, 'cellpadding' => undef, 'fixedsize' => undef, 'height' => undef, 'href' => 'link', 'port' => undef, 'target' => undef, 'title' => 'title', 'tooltip' => 'title', 'valign' => undef, 'width' => undef, }, 'td' => { 'align' => 'align', 'balign' => undef, 'bgcolor' => 'fill', 'border' => 'border', 'cellspacing' => undef, 'cellpadding' => undef, 'colspan' => 'columns', 'fixedsize' => undef, 'height' => undef, 'href' => 'link', 'port' => undef, 'rowspan' => 'rows', 'target' => undef, 'title' => 'title', 'tooltip' => 'title', 'valign' => undef, 'width' => undef, }, }; sub _parse_html_attributes { my ($self, $text, $qr, $tag) = @_; # " $row =~ s/^\s*$qr->{tr}\s*//; # remove $row =~ s/\s*$qr->{tr_end}\s*\z//; my $first = 1; while ($row ne '') { # remove one TD from the current row text $row =~ s/^($qr->{td})($qr->{text})$qr->{td_end}//; return $self->error ("Cannot parse HTML-like row: '$row'") unless defined $1; my $node_label = $2; my $attr_txt = $1; # convert "
" etc. to line breaks # XXX TODO apply here the default of BALIGN $node_label =~ s//\\n/gi; # if the font covers the entire node, set "font" attribute my $font_face = undef; if ($node_label =~ /^[ ]*(.*)<\/FONT>[ ]*\z/i) { $node_label = $2; $font_face = $1; } # XXX TODO if not, allow inline font changes $node_label =~ s/]+>(.*)<\/FONT>/$1/ig; my $node_name = $base_name . '.' . $idx; # if it doesn't exist, add it, otherwise retrieve node object to $node my $node = $graph->node($node_name); if (!defined $node) { # create node object from the correct class $node = $class->new($node_name); $graph->add_node($node); $node->set_attributes($raw_attributes); $node->{autosplit_portname} = $idx; # some sensible default } # apply the default attributes from the table $node->set_attributes($table_attr); # if found a global font attribute, override the font attribute with it $node->set_attribute('font',$font_face) if defined $font_face; # parse the attributes and apply them to the node $self->_html_per_node( $self->_parse_html_attributes($attr_txt,$qr,'td'), $node ); # print STDERR "# Created $node_name\n"; $node->{autosplit_label} = $node_label; $node->{autosplit_basename} = $base_name; push @rc, $node; if (@rc == 1) { # for correct as_txt output $node->{autosplit} = $org_label; $node->{autosplit} =~ s/\s+\z//; # strip trailing spaces $node->{autosplit} =~ s/^\s+//; # strip leading spaces $first_in_row = $node; } else { # second, third etc. get previous as origin my ($sx,$sy) = (1,0); my $origin = $rc[-2]; # the first node in one row is relative to the first node in the # prev row if ($first == 1) { ($sx,$sy) = (0,1); $origin = $first_in_row; $first_in_row = $node; $first = 0; } $node->relative_to($origin,$sx,$sy); # suppress as_txt output for other parts $node->{autosplit} = undef; } # nec. for border-collapse $node->{autosplit_xy} = "$x,$y"; $idx++; # next node ID $x++; } # next row $y++; } # return created nodes @rc; } ############################################################################# sub _parser_cleanup { # After initial parsing, do cleanup, e.g. autosplit nodes with shape record, # parse HTML-like labels, re-connect edges to the parts etc. my ($self) = @_; print STDERR "# Parser cleanup pass\n" if $self->{debug}; my $g = $self->{_graph}; my @nodes = $g->nodes(); # For all nodes that have a shape of "record", break down their label into # parts and create these as autosplit nodes. # For all nodes that have a label starting with "<", parse it as HTML. # keep a record of all nodes to be deleted later: my $delete = {}; my $html_regexps = $self->_match_html_regexps(); my $graph_flow = $g->attribute('flow'); for my $n (@nodes) { my $label = $n->label(1); # we can get away with a direct lookup, since DOT does not have classes my $shape = $n->{att}->{shape} || 'rect'; if ($shape ne 'record' && $label =~ /^<\s*<.*>\z/) { print STDERR "# HTML-like label found: $label\n" if $self->{debug}; my @nodes = $self->_parse_html($n, $html_regexps); # remove the temp. and spurious node $delete->{$n->{name}} = undef; my @edges = $n->edges(); # reconnect the found edges to the new autosplit parts for my $e (@edges) { # XXX TODO: connect to better suited parts based on flow? $e->start_at($nodes[0]) if ($e->{from} == $n); $e->end_at($nodes[0]) if ($e->{to} == $n); } $g->del_node($n); next; } if ($shape eq 'record' && $label =~ /\|/) { my $att = {}; # create basename only when node name differes from label $att->{basename} = $n->{name}; if ($n->{name} ne $label) { $att->{basename} = $n->{name}; } # XXX TODO: autosplit needs to handle nesting like "{}". # Replace "{ ... | ... | ... }" with "...|| ... || ...." as a cheat # to fix some common cases if ($label =~ /^\s*\{[^\{\}]+\}\s*\z/) { $label =~ s/[\{\}]//g; # {..|..} => ..|.. # if flow up/down: {A||B} => "[ A|| || B ]" $label =~ s/\|/\|\| /g # ..|.. => ..|| .. if ($graph_flow =~ /^(east|west)/); # if flow left/right: {A||B} => "[ A| |B ]" $label =~ s/\|\|/\| \|/g # ..|.. => ..| |.. if ($graph_flow =~ /^(north|south)/); } my @rc = $self->_autosplit_node($g, $label, $att, 0 ); my $group = $n->group(); $n->del_attribute('label'); my $qr_clean = $self->{_qr_part_clean}; # clean the base name of ports: # " test | test" => "test|test" $rc[0]->{autosplit} =~ s/(^|\|)$qr_clean/$1/g; $rc[0]->{att}->{basename} =~ s/(^|\|)$qr_clean/$1/g; $rc[0]->{autosplit} =~ s/^\s*//; $rc[0]->{att}->{basename} =~ s/^\s*//; # '| |' => '| |' to avoid empty parts via as_txt() => as_ascii() $rc[0]->{autosplit} =~ s/\|\s\|/\| \|/g; $rc[0]->{att}->{basename} =~ s/\|\s\|/\| \|/g; $rc[0]->{autosplit} =~ s/\|\s\|/\| \|/g; $rc[0]->{att}->{basename} =~ s/\|\s\|/\| \|/g; delete $rc[0]->{att}->{basename} if $rc[0]->{att}->{basename} eq $rc[0]->{autosplit}; for my $n1 (@rc) { $n1->add_to_group($group) if $group; $n1->set_attributes($n->{att}); # remove the temp. "shape=record" $n1->del_attribute('shape'); } # If the helper node has edges, reconnect them to the first # part of the autosplit node (dot seems to render them arbitrarily # on the autosplit node): for my $e (ord_values( $n->{edges} )) { $e->start_at($rc[0]) if $e->{from} == $n; $e->end_at($rc[0]) if $e->{to} == $n; } # remove the temp. and spurious node $delete->{$n->{name}} = undef; $g->del_node($n); } } # During parsing, "bonn:f1" -> "berlin:f2" results in "bonn:f1" and # "berlin:f2" as nodes, plus an edge connecting them # We find all of these nodes, move the edges to the freshly created # autosplit parts above, then delete the superflous temporary nodes. # if we looked up "Bonn:f1", remember it here to save time: my $node_cache = {}; my @edges = $g->edges(); @nodes = $g->nodes(); # get a fresh list of nodes after split for my $e (@edges) { # do this for both the "from" and "to" side of the edge: for my $side ('from','to') { my $n = $e->{$side}; next unless defined $n->{_graphviz_portlet}; my $port = $n->{_graphviz_portlet}; my $base = $n->{_graphviz_basename}; my $compass = ''; if ($port =~ s/:(n|ne|e|se|s|sw|w|nw)\z//) { $compass = $1; } # "Bonn:w" is port "w", and only "west" when that port doesnt exist # look it up in the cache first my $node = $node_cache->{"$base:$port"}; my $p = undef; if (!defined $node) { # go thru all nodes and for see if we find one with the right port name for my $na (@nodes) { next unless exists $na->{autosplit_portname} && exists $na->{autosplit_basename}; next unless $na->{autosplit_basename} eq $base; next unless $na->{autosplit_portname} eq $port; # cache result $node_cache->{"$base:$port"} = $na; $node = $na; $p = $port_remap->{substr($compass,0,1)} if $compass; # ne => n => north } } if (!defined $node) { # Still not defined? # port looks like a compass node? if ($port =~ /^(n|ne|e|se|s|sw|w|nw)\z/) { # get the first node matching the base for my $na (@nodes) { #print STDERR "# evaluating $na ($na->{name} $na->{autosplit_basename}) ($base)\n"; next unless exists $na->{autosplit_basename}; next unless $na->{autosplit_basename} eq $base; # cache result $node_cache->{"$base:$port"} = $na; $node = $na; } if (!defined $node) { return $self->error("Cannot find autosplit node for $base:$port on edge $e->{id}"); } $p = $port_remap->{substr($port,0,1)}; # ne => n => north } else { # uhoh... return $self->error("Cannot find autosplit node for $base:$port on edge $e->{id}"); } } if ($side eq 'from') { $delete->{$e->{from}->{name}} = undef; print STDERR "# Setting new edge start point to $node->{name}\n" if $self->{debug}; $e->start_at($node); print STDERR "# Setting new edge end point to start at $p\n" if $self->{debug} && $p; $e->set_attribute('start', $p) if $p; } else { $delete->{$e->{to}->{name}} = undef; print STDERR "# Setting new edge end point to $node->{name}\n" if $self->{debug}; $e->end_at($node); print STDERR "# Setting new edge end point to end at $p\n" if $self->{debug} && $p; $e->set_attribute('end', $p) if $p; } } # end for side "from" and "to" # we have reconnected this edge } # after reconnecting all edges, we can delete temp. nodes: for my $n (@nodes) { next unless exists $n->{_graphviz_portlet}; # "c:w" => "c" my $name = $n->{name}; $name =~ s/:.*?\z//; # add "c" unless we should delete the base node (this deletes record # and autosplit nodes, but keeps loners like "c:w" around as "c": $g->add_node($name) unless exists $delete->{$name}; # delete "c:w" $g->del_node($n); } # if the graph doesn't have a title, set the graph name as title $g->set_attribute('title', $self->{_graphviz_graph_name}) unless defined $g->raw_attribute('title'); # cleanup if there are no groups if ($g->groups() == 0) { $g->del_attribute('group', 'align'); $g->del_attribute('group', 'fill'); } $g->{_warn_on_unknown_attributes} = 0; # reset to die again $self; } 1; __END__ =head1 NAME Graph::Easy::Parser::Graphviz - Parse Graphviz text into Graph::Easy =head1 SYNOPSIS # creating a graph from a textual description use Graph::Easy::Parser::Graphviz; my $parser = Graph::Easy::Parser::Graphviz->new(); my $graph = $parser->from_text( "digraph MyGraph { \n" . " Bonn -> \"Berlin\" \n }" ); print $graph->as_ascii(); print $parser->from_file('mygraph.dot')->as_ascii(); =head1 DESCRIPTION C parses the text format from the DOT language use by Graphviz and constructs a C object from it. The resulting object can than be used to layout and output the graph in various formats. Please see the Graphviz manual for a full description of the syntax rules of the DOT language. =head2 Output The output will be a L object (unless overrriden with C), see the documentation for Graph::Easy what you can do with it. =head2 Attributes Attributes will be remapped to the proper Graph::Easy attribute names and values, as much as possible. Anything else will be converted to custom attributes starting with "x-dot-". So "ranksep: 2" will become "x-dot-ranksep: 2". =head1 METHODS C supports the same methods as its parent class C: =head2 new() use Graph::Easy::Parser::Graphviz; my $parser = Graph::Easy::Parser::Graphviz->new(); Creates a new parser object. There are two valid parameters: debug fatal_errors Both take either a false or a true value. my $parser = Graph::Easy::Parser::Graphviz->new( debug => 1 ); $parser->from_text('digraph G { A -> B }'); =head2 reset() $parser->reset(); Reset the status of the parser, clear errors etc. Automatically called when you call any of the C methods below. =head2 use_class() $parser->use_class('node', 'Graph::Easy::MyNode'); Override the class to be used to constructs objects while parsing. See L for further information. =head2 from_text() my $graph = $parser->from_text( $text ); Create a L object from the textual description in C<$text>. Returns undef for error, you can find out what the error was with L. This method will reset any previous error, and thus the C<$parser> object can be re-used to parse different texts by just calling C multiple times. =head2 from_file() my $graph = $parser->from_file( $filename ); my $graph = Graph::Easy::Parser->from_file( $filename ); Creates a L object from the textual description in the file C<$filename>. The second calling style will create a temporary parser object, parse the file and return the resulting C object. Returns undef for error, you can find out what the error was with L when using the first calling style. =head2 error() my $error = $parser->error(); Returns the last error, or the empty string if no error occured. =head2 parse_error() $parser->parse_error( $msg_nr, @params); Sets an error message from a message number and replaces embedded templates like C<##param1##> with the passed parameters. =head1 CAVEATS The parser has problems with the following things: =over 12 =item encoding and charset attribute The parser assumes the input to be C. Input files in Latin1 are not parsed properly, even when they have the charset attribute set. =item shape=record Nodes with shape record are only parsed properly when the label does not contain groups delimited by "{" and "}", so the following is parsed wrongly: node1 [ shape=record, label="A|{B|C}" ] =item default shape The default shape for a node is 'rect', opposed to 'circle' as dot renders nodes. =item attributes Some attributes are B remapped properly to what Graph::Easy expects, thus losing information, either because Graph::Easy doesn't support this feature yet, or because the mapping is incomplete. Some attributes meant only for nodes or edges etc. might be incorrectly applied to other objects, resulting in unnec. warnings while parsing. Attributes not valid in the original DOT language are silently ignored by dot, but result in a warning when parsing under Graph::Easy. This helps catching all these pesky misspellings, but it's not yet possible to disable these warnings. =item comments Comments written in the source code itself are discarded. If you want to have comments on the graph, clusters, nodes or edges, use the attribute C. These are correctly read in and stored, and then output into the different formats, too. =back =head1 EXPORT Exports nothing. =head1 SEE ALSO L, L. =head1 AUTHOR Copyright (C) 2005 - 2007 by Tels L See the LICENSE file for information. =cut Graph-Easy-0.73/lib/Graph/Easy/Parser/VCG.pm0000644000076400007640000007235012147674735020247 0ustar shlomifshlomif############################################################################# # Parse VCG text into a Graph::Easy object # ############################################################################# package Graph::Easy::Parser::VCG; $VERSION = '0.06'; use Graph::Easy::Parser::Graphviz; @ISA = qw/Graph::Easy::Parser::Graphviz/; use strict; use utf8; use constant NO_MULTIPLES => 1; use Encode qw/decode/; sub _init { my $self = shift; $self->SUPER::_init(@_); $self->{attr_sep} = '='; $self; } my $vcg_color_by_name = {}; my $vcg_colors = [ white => 'white', blue => 'blue', red => 'red', green => 'green', yellow => 'yellow', magenta => 'magenta', cyan => 'cyan', darkgrey => 'rgb(85,85,85)', darkblue => 'rgb(0,0,128)', darkred => 'rgb(128,0,0)', darkgreen => 'rgb(0,128,0)', darkyellow => 'rgb(128,128,0)', darkmagenta => 'rgb(128,0,128)', darkcyan => 'rgb(0,128,128)', gold => 'rgb(255,215,0)', lightgrey => 'rgb(170,170,170)', lightblue => 'rgb(128,128,255)', lightred => 'rgb(255,128,128)', lightgreen => 'rgb(128,255,128)', lightyellow => 'rgb(255,255,128)', lightmagenta => 'rgb(255,128,255)', lightcyan => 'rgb(128,255,255)', lilac => 'rgb(238,130,238)', turquoise => 'rgb(64,224,208)', aquamarine => 'rgb(127,255,212)', khaki => 'rgb(240,230,140)', purple => 'rgb(160,32,240)', yellowgreen => 'rgb(154,205,50)', pink => 'rgb(255,192,203)', orange => 'rgb(255,165,0)', orchid => 'rgb(218,112,214)', black => 'black', ]; { for (my $i = 0; $i < @$vcg_colors; $i+=2) { $vcg_color_by_name->{$vcg_colors->[$i]} = $vcg_colors->[$i+1]; } } sub reset { my $self = shift; Graph::Easy::Parser::reset($self, @_); my $g = $self->{_graph}; $self->{scope_stack} = []; $g->{_vcg_color_map} = []; for (my $i = 0; $i < @$vcg_colors; $i+=2) { # set the first 32 colors as the default push @{$g->{_vcg_color_map}}, $vcg_colors->[$i+1]; } $g->{_vcg_class_names} = {}; # allow some temp. values during parsing $g->_allow_special_attributes( { edge => { source => [ "", undef, '', '', undef, ], target => [ "", undef, '', '', undef, ], }, } ); $g->{_warn_on_unknown_attributes} = 1; # a hack to support multiline labels $self->{_in_vcg_multi_line_label} = 0; # set some default attributes on the graph object, because GDL has # some different defaults as Graph::Easy $g->set_attribute('flow', 'south'); $g->set_attribute('edge', 'arrow-style', 'filled'); $g->set_attribute('node', 'align', 'left'); $self; } sub _vcg_color_map_entry { my ($self, $index, $color) = @_; $color =~ /([0-9]+)\s+([0-9]+)\s+([0-9]+)/; $self->{_graph}->{_vcg_color_map}->[$index] = "rgb($1,$2,$3)"; } sub _unquote { my ($self, $name) = @_; $name = '' unless defined $name; # "foo bar" => foo bar # we need to use "[ ]" here, because "\s" also matches 0x0c, and # these color codes need to be kept intact: $name =~ s/^"[ ]*//; # remove left-over quotes $name =~ s/[ ]*"\z//; # unquote special chars $name =~ s/\\([\[\(\{\}\]\)#"])/$1/g; $name; } ############################################################################# sub _match_commented_line { # matches only empty lines qr/^\s*\z/; } sub _match_multi_line_comment { # match a multi line comment # /* * comment * */ qr#^\s*/\*.*?\*/\s*#; } sub _match_optional_multi_line_comment { # match a multi line comment # "/* * comment * */" or /* a */ /* b */ or "" qr#(?:(?:\s*/\*.*?\*/\s*)*|\s+)#; } sub _match_classname { # Return a regexp that matches something like classname 1: "foo" my $self = shift; qr/^\s*classname\s([0-9]+)\s*:\s*"((\\"|[^"])*)"/; } sub _match_node { # Return a regexp that matches a node at the start of the buffer my $self = shift; my $attr = $self->_match_attributes(); # Examples: "node: { title: "a" }" qr/^\s*node:\s*$attr/; } sub _match_edge { # Matches an edge at the start of the buffer my $self = shift; my $attr = $self->_match_attributes(); # Examples: "edge: { sourcename: "a" targetname: "b" }" # "backedge: { sourcename: "a" targetname: "b" }" qr/^\s*(|near|bentnear|back)edge:\s*$attr/; } sub _match_single_attribute { qr/\s*( energetic\s\w+ # "energetic attraction" etc. | \w+ # a word | border\s(?:x|y) # "border x" or "border y" | colorentry\s+[0-9]{1,2} # colorentry )\s*:\s* ( "(?:\\"|[^"])*" # "foo" | [0-9]{1,3}\s+[0-9]{1,3}\s+[0-9]{1,3} # "128 128 64" for color entries | \{[^\}]+\} # or {..} | [^<][^,\]\}\n\s;]* # or simple 'fooobar' ) \s*/x; # possible trailing whitespace } sub _match_class_attribute { # match something like "edge.color: 10" qr/\s*(edge|node)\.(\w+)\s*:\s* # the attribute name (label:") ( "(?:\\"|[^"])*" # "foo" | [^<][^,\]\}\n\s]* # or simple 'fooobar' ) \s*/x; # possible whitespace } sub _match_attributes { # return a regexp that matches something like " { color=red; }" and returns # the inner text without the {} my $qr_att = _match_single_attribute(); my $qr_cmt = _match_multi_line_comment(); qr/\s*\{\s*((?:$qr_att|$qr_cmt)*)\s*\}/; } sub _match_graph_attribute { # return a regexp that matches something like " color: red " for attributes # that apply to a graph/subgraph qr/^\s*( ( colorentry\s+[0-9]{1,2}:\s+[0-9]+\s+[0-9]+\s+[0-9]+ | (?!(node|edge|nearedge|bentnearedge|graph)) # not one of these \w+\s*:\s*("(?:\\"|[^"])*"|[^\n\s]+) ) )([\n\s]\s*|\z)/x; } sub _clean_attributes { my ($self,$text) = @_; $text =~ s/^\s*\{\s*//; # remove left-over "{" and spaces $text =~ s/\s*;?\s*\}\s*\z//; # remove left-over "}" and spaces $text; } sub _match_group_end { # return a regexp that matches something like " }" at the beginning qr/^\s*\}\s*/; } sub _match_group_start { # return a regexp that matches something like "graph {" at the beginning qr/^\s*graph:\s+\{\s*/; } sub _clean_line { # do some cleanups on a line before handling it my ($self,$line) = @_; chomp($line); # collapse white space at start $line =~ s/^\s+//; if ($self->{_in_vcg_multi_line_label}) { if ($line =~ /\"[^\"]*\z/) { # '"\n' $self->{_in_vcg_multi_line_label} = 0; # restore the match stack $self->{match_stack} = $self->{_match_stack}; delete $self->{_match_stack}; } else { # hack: convert "a" to \"a\" to fix faulty inputs $line =~ s/([^\\])\"/$1\\\"/g; } } # a line ending in 'label: "...\n' means a multi-line label elsif ($line =~ /(^|\s)label:\s+\"[^\"]*\z/) { $self->{_in_vcg_multi_line_label} = 1; # swap out the match stack since we just wait for the end of the label $self->{_match_stack} = $self->{match_stack}; delete $self->{match_stack}; } $line; } sub _line_insert { # What to insert between two lines. my ($self) = @_; print STDERR "in multiline\n" if $self->{_in_vcg_multi_line_label} && $self->{debug}; # multiline labels => '\n' return '\\n' if $self->{_in_vcg_multi_line_label}; # the default is ' ' ' '; } ############################################################################# sub _new_scope { # create a new scope, with attributes from current scope my ($self, $is_group) = @_; my $scope = {}; if (@{$self->{scope_stack}} > 0) { my $old_scope = $self->{scope_stack}->[-1]; # make a copy of the old scope's attribtues for my $t (sort keys %$old_scope) { next if $t =~ /^_/; my $s = $old_scope->{$t}; $scope->{$t} = {} unless ref $scope->{$t}; my $sc = $scope->{$t}; for my $k (sort keys %$s) { # skip things like "_is_group" $sc->{$k} = $s->{$k} unless $k =~ /^_/; } } } $scope->{_is_group} = 1 if defined $is_group; push @{$self->{scope_stack}}, $scope; $scope; } sub _edge_style { # To convert "--" or "->" we simple do nothing, since the edge style in # VCG can only be set via the attributes (if at all) my ($self, $ed) = @_; 'solid'; } sub _build_match_stack { my $self = shift; my $qr_cn = $self->_match_classname(); my $qr_node = $self->_match_node(); my $qr_cmt = $self->_match_multi_line_comment(); my $qr_ocmt = $self->_match_optional_multi_line_comment(); my $qr_attr = $self->_match_attributes(); my $qr_gatr = $self->_match_graph_attribute(); my $qr_oatr = $self->_match_optional_attributes(); my $qr_edge = $self->_match_edge(); my $qr_class = $self->_match_class_attribute(); my $qr_group_end = $self->_match_group_end(); my $qr_group_start = $self->_match_group_start(); # "graph: {" $self->_register_handler( $qr_group_start, sub { my $self = shift; # the main graph if (@{$self->{scope_stack}} == 0) { print STDERR "# Parser: found main graph\n" if $self->{debug}; $self->{_vcg_graph_name} = 'unnamed'; $self->_new_scope(1); } else { print STDERR "# Parser: found subgraph\n" if $self->{debug}; # a new subgraph push @{$self->{group_stack}}, $self->_new_group(); } 1; } ); # graph or subgraph end "}" $self->_register_handler( $qr_group_end, sub { my $self = shift; print STDERR "# Parser: found end of (sub-)graph\n" if $self->{debug}; my $scope = pop @{$self->{scope_stack}}; return $self->parse_error(0) if !defined $scope; 1; } ); # classname 1: "foo" $self->_register_handler( $qr_cn, sub { my $self = shift; my $class = $1; my $name = $2; print STDERR "# Found classname '$name' for class '$class'\n" if $self->{debug} > 1; $self->{_graph}->{_vcg_class_names}->{$class} = $name; 1; } ); # node: { ... } $self->_register_handler( $qr_node, sub { my $self = shift; my $att = $self->_parse_attributes($1 || '', 'node', NO_MULTIPLES ); return undef unless defined $att; # error in attributes? my $name = $att->{title}; delete $att->{title}; print STDERR "# Found node with name $name\n" if $self->{debug} > 1; my $node = $self->_new_node($self->{_graph}, $name, $self->{group_stack}, $att, []); # set attributes from scope my $scope = $self->{scope_stack}->[-1] || {}; $node->set_attributes ($scope->{node}) if keys %{$scope->{node}} != 0; # override with local attributes $node->set_attributes ($att) if keys %$att != 0; 1; } ); # "edge: { ... }" $self->_register_handler( $qr_edge, sub { my $self = shift; my $type = $1 || 'edge'; my $txt = $2 || ''; $type = "edge" if $type =~ /edge/; # bentnearedge => edge my $att = $self->_parse_attributes($txt, 'edge', NO_MULTIPLES ); return undef unless defined $att; # error in attributes? my $from = $att->{source}; delete $att->{source}; my $to = $att->{target}; delete $att->{target}; print STDERR "# Found edge ($type) from $from to $to\n" if $self->{debug} > 1; my $edge = $self->{_graph}->add_edge ($from, $to); # set attributes from scope my $scope = $self->{scope_stack}->[-1] || {}; $edge->set_attributes ($scope->{edge}) if keys %{$scope->{edge}} != 0; # override with local attributes $edge->set_attributes ($att) if keys %$att != 0; 1; } ); # color: red (for graphs or subgraphs) $self->_register_attribute_handler($qr_gatr, 'parent'); # edge.color: 10 $self->_register_handler( $qr_class, sub { my $self = shift; my $type = $1; my $name = $2; my $val = $3; print STDERR "# Found color definition $type $name $val\n" if $self->{debug} > 2; my $att = $self->_remap_attributes( { $name => $val }, $type, $self->_remap()); # store the attributes in the current scope my $scope = $self->{scope_stack}->[-1]; $scope->{$type} = {} unless ref $scope->{$type}; my $s = $scope->{$type}; for my $k (sort keys %$att) { $s->{$k} = $att->{$k}; } #$self->{_graph}->set_attributes ($type, $att); 1; }); # remove multi line comments /* comment */ $self->_register_handler( $qr_cmt, undef ); # remove single line comment // comment $self->_register_handler( qr/^\s*\/\/.*/, undef ); $self; } sub _new_node { # add a node to the graph, overridable by subclasses my ($self, $graph, $name, $group_stack, $att, $stack) = @_; # print STDERR "add_node $name\n"; my $node = $graph->node($name); if (!defined $node) { $node = $graph->add_node($name); # add # apply attributes from the current scope (only for new nodes) my $scope = $self->{scope_stack}->[-1]; return $self->error("Scope stack is empty!") unless defined $scope; my $is_group = $scope->{_is_group}; delete $scope->{_is_group}; $node->set_attributes($scope->{node}); $scope->{_is_group} = $is_group if $is_group; my $group = $self->{group_stack}->[-1]; $node->add_to_group($group) if $group; } $node; } ############################################################################# # attribute remapping # undef => drop that attribute # not listed attributes are simple copied unmodified my $vcg_remap = { 'node' => { iconfile => 'x-vcg-iconfile', info1 => 'x-vcg-info1', info2 => 'x-vcg-info2', info3 => 'x-vcg-info3', invisible => \&_invisible_from_vcg, importance => 'x-vcg-importance', focus => 'x-vcg-focus', margin => 'x-vcg-margin', textmode => \&_textmode_from_vcg, textcolor => \&_node_color_from_vcg, color => \&_node_color_from_vcg, bordercolor => \&_node_color_from_vcg, level => 'rank', horizontal_order => \&_horizontal_order_from_vcg, shape => \&_vcg_node_shape, vertical_order => \&_vertical_order_from_vcg, }, 'edge' => { anchor => 'x-vcg-anchor', right_anchor => 'x-vcg-right_anchor', left_anchor => 'x-vcg-left_anchor', arrowcolor => 'x-vcg-arrowcolor', arrowsize => 'x-vcg-arrowsize', # XXX remap this arrowstyle => 'x-vcg-arrowstyle', backarrowcolor => 'x-vcg-backarrowcolor', backarrowsize => 'x-vcg-backarrowsize', backarrowstyle => 'x-vcg-backarrowstyle', class => \&_edge_class_from_vcg, color => \&_edge_color_from_vcg, horizontal_order => 'x-vcg-horizontal_order', linestyle => 'style', priority => 'x-vcg-priority', source => 'source', sourcename => 'source', target => 'target', targetname => 'target', textcolor => \&_edge_color_from_vcg, thickness => 'x-vcg-thickness', # remap to broad etc. }, 'graph' => { color => \&_node_color_from_vcg, bordercolor => \&_node_color_from_vcg, textcolor => \&_node_color_from_vcg, x => 'x-vcg-x', y => 'x-vcg-y', xmax => 'x-vcg-xmax', ymax => 'x-vcg-ymax', xspace => 'x-vcg-xspace', yspace => 'x-vcg-yspace', xlspace => 'x-vcg-xlspace', ylspace => 'x-vcg-ylspace', xbase => 'x-vcg-xbase', ybase => 'x-vcg-ybase', xlraster => 'x-vcg-xlraster', xraster => 'x-vcg-xraster', yraster => 'x-vcg-yraster', amax => 'x-vcg-amax', bmax => 'x-vcg-bmax', cmax => 'x-vcg-cmax', cmin => 'x-vcg-cmin', smax => 'x-vcg-smax', pmax => 'x-vcg-pmax', pmin => 'x-vcg-pmin', rmax => 'x-vcg-rmax', rmin => 'x-vcg-rmin', splines => 'x-vcg-splines', focus => 'x-vcg-focus', hidden => 'x-vcg-hidden', horizontal_order => 'x-vcg-horizontal_order', iconfile => 'x-vcg-iconfile', inport_sharing => \&_inport_sharing_from_vcg, importance => 'x-vcg-importance', ignore_singles => 'x-vcg-ignore_singles', invisible => 'x-vcg-invisible', info1 => 'x-vcg-info1', info2 => 'x-vcg-info2', info3 => 'x-vcg-info3', infoname1 => 'x-vcg-infoname1', infoname2 => 'x-vcg-infoname2', infoname3 => 'x-vcg-infoname3', level => 'x-vcg-level', loc => 'x-vcg-loc', layout_algorithm => 'x-vcg-layout_algorithm', # also allow this variant: layoutalgorithm => 'x-vcg-layout_algorithm', layout_downfactor => 'x-vcg-layout_downfactor', layout_upfactor => 'x-vcg-layout_upfactor', layout_nearfactor => 'x-vcg-layout_nearfactor', linear_segments => 'x-vcg-linear_segments', margin => 'x-vcg-margin', manhattan_edges => \&_manhattan_edges_from_vcg, near_edges => 'x-vcg-near_edges', nearedges => 'x-vcg-nearedges', node_alignment => 'x-vcg-node_alignment', port_sharing => \&_port_sharing_from_vcg, priority_phase => 'x-vcg-priority_phase', outport_sharing => \&_outport_sharing_from_vcg, shape => 'x-vcg-shape', smanhattan_edges => 'x-vcg-smanhattan_edges', state => 'x-vcg-state', splines => 'x-vcg-splines', splinefactor => 'x-vcg-splinefactor', spreadlevel => 'x-vcg-spreadlevel', title => 'label', textmode => \&_textmode_from_vcg, useractioncmd1 => 'x-vcg-useractioncmd1', useractioncmd2 => 'x-vcg-useractioncmd2', useractioncmd3 => 'x-vcg-useractioncmd3', useractioncmd4 => 'x-vcg-useractioncmd4', useractionname1 => 'x-vcg-useractionname1', useractionname2 => 'x-vcg-useractionname2', useractionname3 => 'x-vcg-useractionname3', useractionname4 => 'x-vcg-useractionname4', vertical_order => 'x-vcg-vertical_order', display_edge_labels => 'x-vcg-display_edge_labels', edges => 'x-vcg-edges', nodes => 'x-vcg-nodes', icons => 'x-vcg-icons', iconcolors => 'x-vcg-iconcolors', view => 'x-vcg-view', subgraph_labels => 'x-vcg-subgraph_labels', arrow_mode => 'x-vcg-arrow_mode', arrowmode => 'x-vcg-arrowmode', crossing_optimization => 'x-vcg-crossing_optimization', crossing_phase2 => 'x-vcg-crossing_phase2', crossing_weight => 'x-vcg-crossing_weight', equal_y_dist => 'x-vcg-equal_y_dist', equalydist => 'x-vcg-equalydist', finetuning => 'x-vcg-finetuning', fstraight_phase => 'x-vcg-fstraight_phase', straight_phase => 'x-vcg-straight_phase', import_sharing => 'x-vcg-import_sharing', late_edge_labels => 'x-vcg-late_edge_labels', treefactor => 'x-vcg-treefactor', orientation => \&_orientation_from_vcg, attraction => 'x-vcg-attraction', 'border x' => 'x-vcg-border-x', 'border y' => 'x-vcg-border-y', 'energetic' => 'x-vcg-energetic', 'energetic attraction' => 'x-vcg-energetic-attraction', 'energetic border' => 'x-vcg-energetic-border', 'energetic crossing' => 'x-vcg-energetic-crossing', 'energetic gravity' => 'x-vcg-energetic gravity', 'energetic overlapping' => 'x-vcg-energetic overlapping', 'energetic repulsion' => 'x-vcg-energetic repulsion', fdmax => 'x-vcg-fdmax', gravity => 'x-vcg-gravity', magnetic_field1 => 'x-vcg-magnetic_field1', magnetic_field2 => 'x-vcg-magnetic_field2', magnetic_force1 => 'x-vcg-magnetic_force1', magnetic_force2 => 'x-vcg-magnetic_force2', randomfactor => 'x-vcg-randomfactor', randomimpulse => 'x-vcg-randomimpulse', randomrounds => 'x-vcg-randomrounds', repulsion => 'x-vcg-repulsion', tempfactor => 'x-vcg-tempfactor', tempmax => 'x-vcg-tempmax', tempmin => 'x-vcg-tempmin'. tempscheme => 'x-vcg-tempscheme'. temptreshold => 'x-vcg-temptreshold', dirty_edge_labels => 'x-vcg-dirty_edge_labels', fast_icons => 'x-vcg-fast_icons', }, 'group' => { # graph attributes will be added here automatically title => \&_group_name_from_vcg, status => 'x-vcg-status', }, 'all' => { loc => 'x-vcg-loc', folding => 'x-vcg-folding', scaling => 'x-vcg-scaling', shrink => 'x-vcg-shrink', stretch => 'x-vcg-stretch', width => 'x-vcg-width', height => 'x-vcg-height', fontname => 'font', }, }; { # add all graph attributes to group, too my $group = $vcg_remap->{group}; my $graph = $vcg_remap->{graph}; for my $k (sort keys %$graph) { $group->{$k} = $graph->{$k}; } } sub _remap { $vcg_remap; } my $vcg_edge_color_remap = { textcolor => 'labelcolor', }; my $vcg_node_color_remap = { textcolor => 'color', color => 'fill', }; sub _vertical_order_from_vcg { # remap "vertical_order: 5" to "rank: 5" my ($graph, $name, $value) = @_; my $rank = $value; # insert a really really high rank $rank = '1000000' if $value eq 'maxdepth'; # save the original value, too ('x-vcg-vertical_order', $value, 'rank', $rank); } sub _horizontal_order_from_vcg { # remap "horizontal_order: 5" to "rank: 5" my ($graph, $name, $value) = @_; my $rank = $value; # insert a really really high rank $rank = '1000000' if $value eq 'maxdepth'; # save the original value, too ('x-vcg-horizontal_order', $value, 'rank', $rank); } sub _invisible_from_vcg { # remap "invisible: yes" to "shape: invisible" my ($graph, $name, $value) = @_; return (undef,undef) if $value ne 'yes'; ('shape', 'invisible'); } sub _manhattan_edges_from_vcg { # remap "manhattan_edges: yes" for graphs my ($graph, $name, $value) = @_; if ($value eq 'yes') { $graph->set_attribute('edge','start','front'); $graph->set_attribute('edge','end','back'); } # store the value for proper VCG output ('x-vcg-' . $name, $value); } sub _textmode_from_vcg { # remap "textmode: left_justify" to "align: left;" my ($graph, $name, $align) = @_; $align =~ s/_.*//; # left_justify => left ('align', lc($align)); } sub _edge_color_from_vcg { # remap "darkyellow" to "rgb(128 128 0)" my ($graph, $name, $color) = @_; # print STDERR "edge $name $color\n"; # print STDERR ($vcg_edge_color_remap->{$name} || $name, " ", $vcg_color_by_name->{$color} || $color), "\n"; my $c = $vcg_color_by_name->{$color} || $color; $c = $graph->{_vcg_color_map}->[$c] if $c =~ /^[0-9]+\z/ && $c < 256; ($vcg_edge_color_remap->{$name} || $name, $c); } sub _edge_class_from_vcg { # remap "1" to "edgeclass1" to create a valid class name my ($graph, $name, $class) = @_; $class = $graph->{_vcg_class_names}->{$class} || ('edgeclass' . $class) if $class =~ /^[0-9]+\z/; #$class = 'edgeclass' . $class if $class !~ /^[a-zA-Z]/; ('class', $class); } my $vcg_orientation = { top_to_bottom => 'south', bottom_to_top => 'north', left_to_right => 'east', right_to_left => 'west', }; sub _orientation_from_vcg { my ($graph, $name, $value) = @_; ('flow', $vcg_orientation->{$value} || 'south'); } sub _port_sharing_from_vcg { # if we see this, add autojoin/autosplit my ($graph, $name, $value) = @_; $value = ($value =~ /yes/i) ? 'yes' : 'no'; ('autojoin', $value, 'autosplit', $value); } sub _inport_sharing_from_vcg { # if we see this, add autojoin/autosplit my ($graph, $name, $value) = @_; $value = ($value =~ /yes/i) ? 'yes' : 'no'; ('autojoin', $value); } sub _outport_sharing_from_vcg { # if we see this, add autojoin/autosplit my ($graph, $name, $value) = @_; $value = ($value =~ /yes/i) ? 'yes' : 'no'; ('autosplit', $value); } sub _node_color_from_vcg { # remap "darkyellow" to "rgb(128 128 0)" my ($graph, $name, $color) = @_; my $c = $vcg_color_by_name->{$color} || $color; $c = $graph->{_vcg_color_map}->[$c] if $c =~ /^[0-9]+\z/ && $c < 256; ($vcg_node_color_remap->{$name} || $name, $c); } my $shapes = { box => 'rect', rhomb => 'diamond', triangle => 'triangle', ellipse => 'ellipse', circle => 'circle', hexagon => 'hexagon', trapeze => 'trapezium', uptrapeze => 'invtrapezium', lparallelogram => 'invparallelogram', rparallelogram => 'parallelogram', }; sub _vcg_node_shape { my ($self, $name, $shape) = @_; my @rc; my $s = lc($shape); # map the name to what Graph::Easy expects (ellipse stays as ellipse but # everything unknown gets converted to rect) $s = $shapes->{$s} || 'rect'; (@rc, $name, $s); } sub _group_name_from_vcg { my ($self, $attr, $name, $object) = @_; print STDERR "# Renaming anon group '$object->{name}' to '$name'\n" if $self->{debug} > 0; $self->rename_group($object, $name); # name was set, so drop the "title: name" pair (undef, undef); } ############################################################################# sub _remap_attributes { my ($self, $att, $object, $r) = @_; # print STDERR "# Remapping attributes\n"; # use Data::Dumper; print Dumper($att); # handle the "colorentry 00" entries: for my $key (sort keys %$att) { if ($key =~ /^colorentry\s+([0-9]{1,2})/) { # put the color into the current color map $self->_vcg_color_map_entry($1, $att->{$key}); delete $att->{$key}; next; } # remap \fi065 to 'A' $att->{$key} =~ s/(\x0c|\\f)i([0-9]{3})/ decode('iso-8859-1', chr($2)); /eg; # XXX TDOO: support inline colorations # remap \f65 to '' $att->{$key} =~ s/(\x0c|\\f)([0-9]{2})//g; # remap \c09 to color 09: TODO for now remove $att->{$key} =~ s/(\x0c|\\f)([0-9]{2})//g; # XXX TODO: support real hor lines # insert a fake
$att->{$key} =~ s/(\x0c|\\f)-/\\c ---- \\n /g; } $self->SUPER::_remap_attributes($att,$object,$r); } ############################################################################# sub _parser_cleanup { # After initial parsing, do cleanup. my ($self) = @_; my $g = $self->{_graph}; $g->{_warn_on_unknown_attributes} = 0; # reset to die again delete $g->{_vcg_color_map}; delete $g->{_vcg_class_names}; $self; } 1; __END__ =head1 NAME Graph::Easy::Parser::VCG - Parse VCG or GDL text into Graph::Easy =head1 SYNOPSIS # creating a graph from a textual description use Graph::Easy::Parser::VCG; my $parser = Graph::Easy::Parser::VCG->new(); my $graph = $parser->from_text( "graph: { \n" . " node: { title: "Bonn" }\n" . " node: { title: "Berlin" }\n" . " edge: { sourcename: "Bonn" targetname: "Berlin" }\n" . "}\n" ); print $graph->as_ascii(); print $parser->from_file('mygraph.vcg')->as_ascii(); =head1 DESCRIPTION C parses the text format from the VCG or GDL (Graph Description Language) use by tools like GCC and AiSee, and constructs a C object from it. The resulting object can then be used to layout and output the graph in various formats. =head2 Output The output will be a L object (unless overrriden with C), see the documentation for Graph::Easy what you can do with it. =head2 Attributes Attributes will be remapped to the proper Graph::Easy attribute names and values, as much as possible. Anything else will be converted to custom attributes starting with "x-vcg-". So "dirty_edge_labels: yes" will become "x-vcg-dirty_edge_labels: yes". =head1 METHODS C supports the same methods as its parent class C: =head2 new() use Graph::Easy::Parser::VCG; my $parser = Graph::Easy::Parser::VCG->new(); Creates a new parser object. There are two valid parameters: debug fatal_errors Both take either a false or a true value. my $parser = Graph::Easy::Parser::VCG->new( debug => 1 ); $parser->from_text('graph: { }'); =head2 reset() $parser->reset(); Reset the status of the parser, clear errors etc. Automatically called when you call any of the C methods below. =head2 use_class() $parser->use_class('node', 'Graph::Easy::MyNode'); Override the class to be used to constructs objects while parsing. See L for further information. =head2 from_text() my $graph = $parser->from_text( $text ); Create a L object from the textual description in C<$text>. Returns undef for error, you can find out what the error was with L. This method will reset any previous error, and thus the C<$parser> object can be re-used to parse different texts by just calling C multiple times. =head2 from_file() my $graph = $parser->from_file( $filename ); my $graph = Graph::Easy::Parser::VCG->from_file( $filename ); Creates a L object from the textual description in the file C<$filename>. The second calling style will create a temporary parser object, parse the file and return the resulting C object. Returns undef for error, you can find out what the error was with L when using the first calling style. =head2 error() my $error = $parser->error(); Returns the last error, or the empty string if no error occured. =head2 parse_error() $parser->parse_error( $msg_nr, @params); Sets an error message from a message number and replaces embedded templates like C<##param1##> with the passed parameters. =head1 CAVEATS The parser has problems with the following things: =over 12 =item attributes Some attributes are B remapped properly to what Graph::Easy expects, thus losing information, either because Graph::Easy doesn't support this feature yet, or because the mapping is incomplete. =item comments Comments written in the source code itself are discarded. If you want to have comments on the graph, clusters, nodes or edges, use the attribute C. These are correctly read in and stored, and then output into the different formats, too. =back =head1 EXPORT Exports nothing. =head1 SEE ALSO L, L. =head1 AUTHOR Copyright (C) 2005 - 2008 by Tels L See the LICENSE file for information. =cut Graph-Easy-0.73/lib/Graph/Easy/As_graphviz.pm0000644000076400007640000010077712150071567020642 0ustar shlomifshlomif############################################################################# # output the graph in dot-format text # ############################################################################# package Graph::Easy::As_graphviz; $VERSION = '0.31'; ############################################################################# ############################################################################# package Graph::Easy; use strict; use Graph::Easy::Util qw(ord_values); my $remap = { node => { 'align' => undef, 'background' => undef, # need a way to simulate that on non-rect nodes 'basename' => undef, 'bordercolor' => \&_remap_color, 'borderstyle' => \&_graphviz_remap_border_style, 'borderwidth' => undef, 'border' => undef, 'color' => \&_remap_color, 'fill' => \&_remap_color, 'label' => \&_graphviz_remap_label, 'pointstyle' => undef, 'pointshape' => undef, 'rotate' => \&_graphviz_remap_node_rotate, 'shape' => \&_graphviz_remap_node_shape, 'title' => 'tooltip', 'rows' => undef, 'columns' => undef, }, edge => { 'align' => undef, 'arrowstyle' => \&_graphviz_remap_arrow_style, 'background' => undef, 'color' => \&_graphviz_remap_edge_color, 'end' => \&_graphviz_remap_port, 'headtitle' => 'headtooltip', 'headlink' => 'headURL', 'labelcolor' => \&_graphviz_remap_label_color, 'start' => \&_graphviz_remap_port, 'style' => \&_graphviz_remap_edge_style, 'tailtitle' => 'tailtooltip', 'taillink' => 'tailURL', 'title' => 'tooltip', 'minlen' => \&_graphviz_remap_edge_minlen, }, graph => { align => \&_graphviz_remap_align, background => undef, bordercolor => \&_remap_color, borderstyle => \&_graphviz_remap_border_style, borderwidth => undef, color => \&_remap_color, fill => \&_remap_color, gid => undef, label => \&_graphviz_remap_label, labelpos => 'labelloc', output => undef, type => undef, }, group => { align => \&_graphviz_remap_align, background => undef, bordercolor => \&_remap_color, borderstyle => \&_graphviz_remap_border_style, borderwidth => undef, color => \&_remap_color, fill => \&_remap_color, labelpos => 'labelloc', rank => undef, title => 'tooltip', }, all => { arrowshape => undef, autolink => undef, autotitle => undef, autolabel => undef, class => undef, colorscheme => undef, flow => undef, fontsize => \&_graphviz_remap_fontsize, font => \&_graphviz_remap_font, format => undef, group => undef, link => \&_graphviz_remap_link, linkbase => undef, textstyle => undef, textwrap => undef, }, always => { node => [ qw/borderstyle label link rotate color fill/ ], 'node.anon' => [ qw/bordercolor borderstyle label link rotate color/ ], edge => [ qw/labelcolor label link color/ ], graph => [ qw/labelpos borderstyle label link color/ ], }, # this routine will handle all custom "x-dot-..." attributes x => \&_remap_custom_dot_attributes, }; sub _remap_custom_dot_attributes { my ($self, $name, $value) = @_; # drop anything that is not starting with "x-dot-..." return (undef,undef) unless $name =~ /^x-dot-/; $name =~ s/^x-dot-//; # "x-dot-foo" => "foo" ($name,$value); } my $color_remap = { bordercolor => 'color', color => 'fontcolor', fill => 'fillcolor', }; sub _remap_color { # remap one color value my ($self, $name, $color, $object) = @_; # guard against always doing the remap even when the attribute is not set return (undef,undef) unless defined $color; if (!ref($object) && $object eq 'graph') { # 'fill' => 'bgcolor'; $name = 'bgcolor' if $name eq 'fill'; } $name = $color_remap->{$name} || $name; $color = $self->_color_as_hex_or_hsv($object,$color); ($name, $color); } sub _color_as_hex_or_hsv { # Given a color in hex, hsv, hsl or rgb, will return either a hex or hsv # color to preserve as much precision as possible: my ($graph, $self, $color) = @_; if ($color !~ /^#/) { # HSV colors with an alpha channel are not supported by graphviz, and # hence converted to RGB here: if ($color =~ /^hsv\(([0-9\.]+),([0-9\.]+),([0-9\.]+)\)/) { # hsv(1.0,1.0,1.0) => 1.0 1.0 1.0 $color = "$1 $2 $3"; } else { my $cs = ref($self) ? $self->attribute('colorscheme') : $graph->attribute($self,'colorscheme'); # red => hex $color = $graph->color_as_hex($color, $cs); } } $color; } sub _graphviz_remap_align { my ($self, $name, $style) = @_; my $s = lc(substr($style,0,1)); # 'l', 'r', or 'c' ('labeljust', $s); } sub _graphviz_remap_edge_minlen { my ($self, $name, $len) = @_; $len = int(($len + 1) / 2); ($name, $len); } sub _graphviz_remap_edge_color { my ($self, $name, $color, $object) = @_; my $style = ref($object) ? $object->attribute('style') : $self->attribute('edge','style'); if (!defined $color) { $color = ref($object) ? $object->attribute('color') : $self->attribute('edge','color'); } $color = '#000000' unless defined $color; $color = $self->_color_as_hex_or_hsv($object, $color); $color = $color . ':' . $color # 'red:red' if $style =~ /^double/; ($name, $color); } sub _graphviz_remap_edge_style { my ($self, $name, $style) = @_; # valid output styles are: solid dashed dotted bold invis $style = 'solid' unless defined $style; $style = 'dotted' if $style =~ /^dot-/; # dot-dash, dot-dot-dash $style = 'dotted' if $style =~ /^wave/; # wave # double lines will be handled in the color attribute as "color:color" $style = 'solid' if $style eq 'double'; # double $style = 'dashed' if $style =~ /^double-dash/; $style = 'invis' if $style eq 'invisible'; # invisible # XXX TODO: These should be (2, 0.5em, 1em) instead of 2,5,11 $style = 'setlinewidth(2), dashed' if $style =~ /^bold-dash/; $style = 'setlinewidth(5)' if $style =~ /^broad/; $style = 'setlinewidth(11)' if $style =~ /^wide/; return (undef, undef) if $style eq 'solid'; # default style can be suppressed ($name, $style); } sub _graphviz_remap_node_rotate { my ($graph, $name, $angle, $self) = @_; # do this only for objects, not classes return (undef,undef) unless ref($self) && defined $angle; return (undef,undef) if $angle == 0; # despite what the manual says, dot rotates counter-clockwise, so fix that $angle = 360 - $angle; ('orientation', $angle); } sub _graphviz_remap_port { my ($graph, $name, $side, $self) = @_; # do this only for objects, not classes return (undef,undef) unless ref($self) && defined $side; # XXX TODO # remap relative ports (front etc) to "south" etc # has a specific port, aka shared a port with another edge return (undef, undef) if $side =~ /,/; $side = $graph->_flow_as_side($self->flow(),$side); $side = substr($side,0,1); # "south" => "s" my $n = 'tailport'; $n = 'headport' if $name eq 'end'; ($n, $side); } sub _graphviz_remap_font { # Remap the font names my ($self, $name, $style) = @_; # XXX TODO: "times" => "Times.ttf" ? ('fontname', $style); } sub _graphviz_remap_fontsize { # make sure the fontsize is in pixel or percent my ($self, $name, $style) = @_; # XXX TODO: This should be actually 1 em my $fs = '11'; if ($style =~ /^([\d\.]+)em\z/) { $fs = $1 * 11; } elsif ($style =~ /^([\d\.]+)%\z/) { $fs = ($1 / 100) * 11; } # this is discouraged: elsif ($style =~ /^([\d\.]+)px\z/) { $fs = $1; } else { $self->_croak("Illegal font-size '$style'"); } # font-size => fontsize ('fontsize', $fs); } sub _graphviz_remap_border_style { my ($self, $name, $style, $node) = @_; my $shape = ''; $shape = ($node->attribute('shape') || '') if ref($node); # some shapes don't need a border: return (undef,undef) if $shape =~ /^(none|invisible|img|point)\z/; $style = $node->attribute('borderstyle') unless defined $style; # valid styles are: solid dashed dotted bold invis $style = '' unless defined $style; $style = 'dotted' if $style =~ /^dot-/; # dot-dash, dot-dot-dash $style = 'dashed' if $style =~ /^double-/; # double-dash $style = 'dotted' if $style =~ /^wave/; # wave # borderstyle double will be handled extra with peripheries=2 later $style = 'solid' if $style eq 'double'; # XXX TODO: These should be (2, 0.5em, 1em) instead of 2,5,11 $style = 'setlinewidth(2)' if $style =~ /^bold/; $style = 'setlinewidth(5)' if $style =~ /^broad/; $style = 'setlinewidth(11)' if $style =~ /^wide/; # "solid 0px" => "none" my $w = 0; $w = $node->attribute('borderwidth') if (ref($node) && $style ne 'none'); $style = 'none' if $w == 0; my @rc; if ($style eq 'none') { my $fill = 'white'; $fill = $node->color_attribute('fill') if ref($node); $style = 'filled'; @rc = ('color', $fill); } # default style can be suppressed return (undef, undef) if $style =~ /^(|solid)\z/ && $shape ne 'rounded'; # for graphviz v2.4 and up $style = 'filled' if $style eq 'solid'; $style = 'filled,'.$style unless $style eq 'filled'; $style = 'rounded,'.$style if $shape eq 'rounded' && $style ne 'none'; $style =~ s/,\z//; # "rounded," => "rounded" push @rc, 'style', $style; @rc; } sub _graphviz_remap_link { my ($self, $name, $l, $object) = @_; # do this only for objects, not classes return (undef,undef) unless ref($object); $l = $object->link() unless defined $l; ('URL', $l); } sub _graphviz_remap_label_color { my ($graph, $name, $color, $self) = @_; # do this only for objects, not classes return (undef,undef) unless ref($self); # no label => no color nec. return (undef, $color) if ($self->label()||'') eq ''; $color = $self->raw_attribute('labelcolor') unless defined $color; # the label color falls back to the edge color $color = $self->attribute('color') unless defined $color; $color = $graph->_color_as_hex_or_hsv($self,$color); ('fontcolor', $color); } sub _graphviz_remap_node_shape { my ($self, $name, $style, $object) = @_; # img needs no shape, and rounded is handled as style return (undef,undef) if $style =~ /^(img|rounded)\z/; # valid styles are: solid dashed dotted bold invis my $s = $style; $s = 'plaintext' if $style =~ /^(invisible|none|point)\z/; if (ref($object)) { my $border = $object->attribute('borderstyle'); $s = 'plaintext' if $border eq 'none'; } ($name, $s); } sub _graphviz_remap_arrow_style { my ($self, $name, $style) = @_; my $s = 'normal'; $s = $style if $style =~ /^(none|open)\z/; $s = 'empty' if $style eq 'closed'; my $n = 'arrowhead'; $n = 'arrowtail' if $self->{_flip_edges}; ($n, $s); } sub _graphviz_remap_label { my ($self, $name, $label, $node) = @_; my $s = $label; # call label() to handle thinks like "autolabel: 15" properly $s = $node->label() if ref($node); if (ref($node)) { # remap all "\n" and "\c" to either "\l" or "\r", depending on align my $align = $node->attribute('align'); my $next_line = '\n'; # the align of the line-ends counts for the line _before_ them, so # add one more to fix the last line $next_line = '\l', $s .= '\l' if $align eq 'left'; $next_line = '\r', $s .= '\r' if $align eq 'right'; $s =~ s/(^|[^\\])\\n/$1$next_line/g; # \n => align } $s =~ s/(^|[^\\])\\c/$1\\n/g; # \c => \n (for center) my $shape = 'rect'; $shape = ($node->attribute('shape') || '') if ref($node); # only for nodes and when they have a "shape: img" if ($shape eq 'img') { my $s = '<
" => " ..." $text =~ s/^$qr->{td_tag}//; $text =~ s/\s*>\z//; my $attr = {}; while ($text ne '') { return $self->error("HTML-like attribute '$text' doesn't look valid to me.") unless $text =~ s/^($qr->{attribute})//; my $name = lc($2); my $value = $3; $self->_unquote($value); $value = lc($value) if $name eq 'align'; $self->error ("Unknown attribute '$name' in HTML-like label") unless exists $html_remap->{$tag}->{$name}; # filter out attributes we do not yet support $attr->{$name} = $value if defined $html_remap->{$tag}->{$name}; } $attr; } sub _html_per_table { # take the HTML-like attributes found per TABLE and create a hash with them # so they can be applied as default to each node my ($self, $attributes) = @_; $self->_remap_attributes($attributes,'table',$html_remap); } sub _html_per_node { # take the HTML-like attributes found per TD and apply them to the node my ($self, $attr, $node) = @_; my $c = $attr->{colspan} || 1; $node->set_attribute('columns',$c) if $c != 1; my $r = $attr->{rowspan} || 1; $node->set_attribute('rows',$r) if $r != 1; $node->{autosplit_portname} = $attr->{port} if exists $attr->{port}; for my $k (qw/port colspan rowspan/) { delete $attr->{$k}; } my $att = $self->_remap_attributes($attr,$node,$html_remap); $node->set_attributes($att); $self; } sub _parse_html { # Given an HTML label, parses that into the individual parts. Returns a # list of nodes. my ($self, $n, $qr) = @_; my $graph = $self->{_graph}; my $label = $n->label(1); $label = '' unless defined $label; my $org_label = $label; # print STDERR "# 1 HTML-like label is now: $label\n"; # "unquote" the HTML-like label $label =~ s/^<\s*//; $label =~ s/\s*>\z//; # print STDERR "# 2 HTML-like label is now: $label\n"; # remove the table end (at the end) $label =~ s/$qr->{table_end}\s*\z//; # print STDERR "# 2.a HTML-like label is now: $label\n"; # remove the table start $label =~ s/($qr->{table})//; # print STDERR "# 3 HTML-like label is now: $label\n"; my $table_tag = $1 || ''; $table_tag =~ /$qr->{table_tag}(.*?)>/; my $table_attr = $self->_parse_html_attributes($1 || '', $qr, 'table'); # use Data::Dumper; # print STDERR "# 3 HTML-like table-tag attributes are: ", Dumper($table_attr),"\n"; # generate the base name from the actual graphviz node name to allow links to # it my $base_name = $n->{name}; my $class = $self->{use_class}->{node}; my $raw_attributes = $n->raw_attributes(); delete $raw_attributes->{label}; delete $raw_attributes->{shape}; my @rc; my $first_in_row; my $x = 0; my $y = 0; my $idx = 0; while ($label ne '') { $label =~ s/^\s*($qr->{row})//; return $self->error ("Cannot parse HTML-like label: '$label'") unless defined $1; # we now got one row: my $row = $1; # print STDERR "# 3 HTML-like row is $row\n"; # remove
>'; my $url = $node->label(); $url =~ s/\s/\+/g; # space $url =~ s/'/%27/g; # replace quotation marks $s =~ s/##url##/$url/g; } ($name, $s); } ############################################################################# sub _att_as_graphviz { # convert a hash with attribute => value mappings to a string my ($self, $out) = @_; my $att = ''; for my $atr (sort keys %$out) { my $v = $out->{$atr}; $v =~ s/\n/\\n/g; $v = '"' . $v . '"' if $v !~ /^[a-z0-9A-Z]+\z/; # quote if nec. # convert "x-dot-foo" to "foo". Special case "K": my $name = $atr; $name =~ s/^x-dot-//; $name = 'K' if $name eq 'k'; $att .= " $name=$v,\n"; } $att =~ s/,\n\z/ /; # remove last "," if ($att ne '') { # the following makes short, single definitions to fit on one line if ($att !~ /\n.*\n/ && length($att) < 40) { $att =~ s/\n/ /; $att =~ s/( )+/ /g; } else { $att =~ s/\n/\n /g; $att = "\n $att"; } } $att; } use Graph::Easy::Util qw(first_kv); sub _generate_group_edge { # Given an edge (from/to at least one group), generate the graphviz code my ($self, $e, $indent) = @_; my $edge_att = $e->attributes_as_graphviz(); my $a = ''; my $b = ''; my $from = $e->{from}; my $to = $e->{to}; ($from,$to) = ($to,$from) if $self->{_flip_edges}; if ($from->isa('Graph::Easy::Group')) { # find an arbitray node inside the group my ($n, $v) = first_kv($from->{nodes}); $a = 'ltail="cluster' . $from->{id}.'"'; # ltail=cluster0 $from = $v; } # XXX TODO: # this fails for empty groups if ($to->isa('Graph::Easy::Group')) { # find an arbitray node inside the group my ($n, $v) = first_kv($to->{nodes}); $b = 'lhead="cluster' . $to->{id}.'"'; # lhead=cluster0 $to = $v; } my $other = $to->_graphviz_point(); my $first = $from->_graphviz_point(); $e->{_p} = undef; # mark as processed my $att = $a; $att .= ', ' . $b if $b ne ''; $att =~ s/^,//; if ($att ne '') { if ($edge_att eq '') { $edge_att = " [ $att ]"; } else { $edge_att =~ s/ \]/, $att \]/; } } "$indent$first $self->{edge_type} $other$edge_att\n"; # return edge text } sub _insert_edge_attribute { # insert an additional attribute into an edge attribute string my ($self, $att, $new_att) = @_; return '[ $new_att ]' if $att eq ''; # '' => '[ ]' # remove any potential old attribute with the same name my $att_name = $new_att; $att_name =~ s/=.*//; $att =~ s/$att_name=("[^"]+"|[^\s]+)//; # insert the new attribute at the end $att =~ s/\s?\]/,$new_att ]/; $att; } sub _suppress_edge_attribute { # remove the named attribute from the edge attribute string my ($self, $att, $sup_att) = @_; $att =~ s/$sup_att=("(\\"|[^"])*"|[^\s\n,;]+)[,;]?//; $att; } sub _generate_edge { # Given an edge, generate the graphviz code for it my ($self, $e, $indent) = @_; # skip links from/to groups, these will be done later return '' if $e->{from}->isa('Graph::Easy::Group') || $e->{to}->isa('Graph::Easy::Group'); my $invis = $self->{_graphviz_invis}; # attributes for invisible helper nodes (the color will be filled in from the edge color) my $inv = ' [ label="",shape=none,style=filled,height=0,width=0,fillcolor="'; my $other = $e->{to}->_graphviz_point(); my $first = $e->{from}->_graphviz_point(); my $edge_att = $e->attributes_as_graphviz(); my $txt = ''; my $modify_edge = 0; my $suppress_start = (!$self->{_flip_edges} ? 'arrowtail=none' : 'arrowhead=none'); my $suppress_end = ( $self->{_flip_edges} ? 'arrowtail=none' : 'arrowhead=none'); my $suppress; # if the edge has a shared start/end port if ($e->has_ports()) { my @edges = (); my ($side,@port) = $e->port('start'); @edges = $e->{from}->edges_at_port('start',$side,@port) if defined $side && @port > 0; if (@edges > 1) # has strict port { # access the invisible node my $sp = $e->port('start'); my $key = "$e->{from}->{name},start,$sp"; my $invis_id = $invis->{$key}; $suppress = $suppress_start; if (!defined $invis_id) { # create the invisible helper node # find a name for it, carefully avoiding names of other nodes: $self->{_graphviz_invis_id}++ while (defined $self->node($self->{_graphviz_invis_id})); $invis_id = $self->{_graphviz_invis_id}++; # output the helper node my $e_color = $e->color_attribute('color'); $txt .= $indent . "$invis_id$inv$e_color\" ]\n"; my $e_att = $self->_insert_edge_attribute($edge_att,$suppress_end); $e_att = $self->_suppress_edge_attribute($e_att,'label'); my $before = ''; my $after = ''; my $i = $indent; if ($e->{group}) { $before = $indent . 'subgraph "cluster' . $e->{group}->{id} . "\" {\n"; $after = $indent . "}\n"; $i = $indent . $indent; } if ($self->{_flip_edges}) { $txt .= $before . $i . "$invis_id $self->{_edge_type} $first$e_att\n" . $after; } else { $txt .= $before . $i . "$first $self->{_edge_type} $invis_id$e_att\n" . $after; } $invis->{$key} = $invis_id; # mark as created } # "joint0" etc $first = $invis_id; $modify_edge++; } ($side,@port) = $e->port('end'); @edges = (); @edges = $e->{to}->edges_at_port('end',$side,@port) if defined $side && @port > 0; if (@edges > 1) { my $ep = $e->port('end'); my $key = "$e->{to}->{name},end,$ep"; my $invis_id = $invis->{$key}; $suppress = $suppress_end; if (!defined $invis_id) { # create the invisible helper node # find a name for it, carefully avoiding names of other nodes: $self->{_graphviz_invis_id}++ while (defined $self->node($self->{_graphviz_invis_id})); $invis_id = $self->{_graphviz_invis_id}++; my $e_att = $self->_insert_edge_attribute($edge_att,$suppress_start); # output the helper node my $e_color = $e->color_attribute('color'); $txt .= $indent . "$invis_id$inv$e_color\" ]\n"; my $before = ''; my $after = ''; my $i = $indent; if ($e->{group}) { $before = $indent . 'subgraph "cluster' . $e->{group}->{id} . "\" {\n"; $after = $indent . "}\n"; $i = $indent . $indent; } if ($self->{_flip_edges}) { $txt .= $before . $i . "$other $self->{_edge_type} $invis_id$e_att\n" . $after; } else { $txt .= $before . $i . "$invis_id $self->{_edge_type} $other$e_att\n" . $after; } $invis->{$key} = $invis_id; # mark as output } # "joint1" etc $other = $invis_id; $modify_edge++; } } ($other,$first) = ($first,$other) if $self->{_flip_edges}; $e->{_p} = undef; # mark as processed $edge_att = $self->_insert_edge_attribute($edge_att,$suppress) if $modify_edge; $txt . "$indent$first $self->{_edge_type} $other$edge_att\n"; # return edge text } sub _order_group { my ($self,$group) = @_; $group->{_order}++; for my $sg (ord_values( $group->{groups})) { $self->_order_group($sg); } } sub _as_graphviz_group { my ($self,$group) = @_; my $txt = ''; # quote special chars in group name my $name = $group->{name}; $name =~ s/([\[\]\(\)\{\}\#"])/\\$1/g; return if $group->{_p}; # output group attributes first my $indent = ' ' x ($group->{_order}); $txt .= $indent."subgraph \"cluster$group->{id}\" {\n${indent}label=\"$name\";\n"; for my $sg (ord_values ( $group->{groups} )) { #print '--'.$sg->{name}."\n"; $txt .= $self->_as_graphviz_group($sg,$indent); $sg->{_p} = 1; } # Make a copy of the attributes, including our class attributes: my $copy = {}; my $attribs = $group->get_attributes(); for my $key (sort keys %$attribs) { $copy->{$key} = $attribs->{$key}; } # set some defaults $copy->{'borderstyle'} = 'solid' unless defined $copy->{'borderstyle'}; my $out = $self->_remap_attributes( $group->class(), $copy, $remap, 'noquote'); # Set some defaults: $out->{fillcolor} = '#a0d0ff' unless defined $out->{fillcolor}; $out->{labeljust} = 'l' unless defined $out->{labeljust}; my $att = ''; # we need to output style first ("filled" and "color" need come later) for my $atr (reverse sort keys %$out) { my $v = $out->{$atr}; $v = '"' . $v . '"' if $v !~ /^[a-z0-9A-Z]+\z/; # quote if nec. # convert "x-dot-foo" to "foo". Special case "K": my $name = $atr; $name =~ s/^x-dot-//; $name = 'K' if $name eq 'k'; $att .= $indent."$name=$v;\n"; } $txt .= $att . "\n" if $att ne ''; # output nodes (w/ or w/o attributes) in that group for my $n ($group->sorted_nodes()) { # skip nodes that are relativ to others (these are done as part # of the HTML-like label of their parent) next if $n->{origin}; my $att = $n->attributes_as_graphviz(); $n->{_p} = undef; # mark as processed $txt .= $indent . $n->as_graphviz_txt() . $att . "\n"; } # output node connections in this group for my $e (ord_values $group->{edges}) { next if exists $e->{_p}; $txt .= $self->_generate_edge($e, $indent); } $txt .= $indent."}\n"; return $txt; } sub _as_graphviz { my ($self) = @_; # convert the graph to a textual representation # does not need a layout() beforehand! my $name = "GRAPH_" . ($self->{gid} || '0'); my $type = $self->attribute('type'); $type = $type eq 'directed' ? 'digraph' : 'graph'; # directed or undirected? $self->{_edge_type} = $type eq 'digraph' ? '->' : '--'; # "a -- b" vs "a -> b" my $txt = "$type $name {\n\n" . " // Generated by Graph::Easy $Graph::Easy::VERSION" . " at " . scalar localtime() . "\n\n"; my $flow = $self->attribute('graph','flow'); $flow = 'east' unless defined $flow; $flow = Graph::Easy->_direction_as_number($flow); # for LR, BT layouts $self->{_flip_edges} = 0; $self->{_flip_edges} = 1 if $flow == 270 || $flow == 0; my $groups = $self->groups(); # to keep track of invisible helper nodes $self->{_graphviz_invis} = {}; # name for invisible helper nodes $self->{_graphviz_invis_id} = 'joint0'; # generate the class attributes first my $atts = $self->{att}; # It is not possible to set attributes for groups in the DOT language that way for my $class (qw/edge graph node/) { next if $class =~ /\./; # skip subclasses my $out = $self->_remap_attributes( $class, $atts->{$class}, $remap, 'noquote'); # per default, our nodes are rectangular, white, filled boxes if ($class eq 'node') { $out->{shape} = 'box' unless $out->{shape}; $out->{style} = 'filled' unless $out->{style}; $out->{fontsize} = '11' unless $out->{fontsize}; $out->{fillcolor} = 'white' unless $out->{fillcolor}; } elsif ($class eq 'graph') { $out->{rankdir} = 'LR' if $flow == 90 || $flow == 270; $out->{labelloc} = 'top' if defined $out->{label} && !defined $out->{labelloc}; $out->{style} = 'filled' if $groups > 0; } elsif ($class eq 'edge') { $out->{dir} = 'back' if $flow == 270 || $flow == 0; my ($name,$style) = $self->_graphviz_remap_arrow_style('', $self->attribute('edge','arrowstyle') ); $out->{$name} = $style; } my $att = $self->_att_as_graphviz($out); $txt .= " $class [$att];\n" if $att ne ''; } $txt .= "\n" if $txt ne ''; # insert newline ########################################################################### # output groups as subgraphs # insert the edges into the proper group $self->_edges_into_groups() if $groups > 0; # output the groups (aka subclusters) for my $group (ord_values $self->{groups}) { $self->_order_group($group); } for my $group (sort { $a->{_order} cmp $b->{_order} } values %{$self->{groups}}) { $txt .= $self->_as_graphviz_group($group) || ''; } my $root = $self->attribute('root'); $root = '' unless defined $root; my $count = 0; # output nodes with attributes first, sorted by their name for my $n (sort { $a->{name} cmp $b->{name} } values %{$self->{nodes}}) { next if exists $n->{_p}; # skip nodes that are relativ to others (these are done as part # of the HTML-like label of their parent) next if $n->{origin}; my $att = $n->attributes_as_graphviz($root); if ($att ne '') { $n->{_p} = undef; # mark as processed $count++; $txt .= " " . $n->as_graphviz_txt() . $att . "\n"; } } $txt .= "\n" if $count > 0; # insert a newline my @nodes = $self->sorted_nodes(); # output the edges foreach my $n (@nodes) { my @out = $n->successors(); my $first = $n->as_graphviz_txt(); if ((@out == 0) && ( (scalar $n->predecessors() || 0) == 0)) { # single node without any connections (unless already output) $txt .= " " . $first . "\n" unless exists $n->{_p} || $n->{origin}; } # for all outgoing connections foreach my $other (reverse @out) { # in case there is more than one edge going from N to O my @edges = $n->edges_to($other); foreach my $e (@edges) { next if exists $e->{_p}; $txt .= $self->_generate_edge($e, ' '); } } } # insert now edges between groups (clusters/subgraphs) foreach my $e (ord_values $self->{edges}) { $txt .= $self->_generate_group_edge($e, ' ') if $e->{from}->isa('Graph::Easy::Group') || $e->{to}->isa('Graph::Easy::Group'); } # clean up for my $n ( ord_values( $self->{nodes}), ord_values( $self->{edges} )) { delete $n->{_p}; } delete $self->{_graphviz_invis}; # invisible helper nodes for joints delete $self->{_flip_edges}; delete $self->{_edge_type}; $txt . "\n}\n"; # close the graph } package Graph::Easy::Node; sub attributes_as_graphviz { # return the attributes of this node as text description my ($self, $root) = @_; $root = '' unless defined $root; my $att = ''; my $class = $self->class(); return '' unless ref $self->{graph}; my $g = $self->{graph}; # get all attributes, excluding the class attributes my $a = $self->raw_attributes(); # add the attributes that are listed under "always": my $attr = $self->{att}; my $base_class = $class; $base_class =~ s/\..*//; my $list = $remap->{always}->{$class} || $remap->{always}->{$base_class}; for my $name (@$list) { # for speed, try to look it up directly # look if we have a code ref: if ( ref($remap->{$base_class}->{$name}) || ref($remap->{all}->{$name}) ) { $a->{$name} = $self->raw_attribute($name); if (!defined $a->{$name}) { my $b_attr = $g->get_attribute($base_class,$name); my $c_attr = $g->get_attribute($class,$name); if (defined $b_attr && defined $c_attr && $b_attr ne $c_attr) { $a->{$name} = $c_attr; $a->{$name} = $b_attr unless defined $a->{$name}; } } } else { $a->{$name} = $attr->{$name}; $a->{$name} = $self->attribute($name) unless defined $a->{$name} && $a->{$name} ne 'inherit'; } } $a = $g->_remap_attributes( $self, $a, $remap, 'noquote'); # do not needlessly output labels: delete $a->{label} if !$self->isa('Graph::Easy::Edge') && # not an edge exists $a->{label} && $a->{label} eq $self->{name}; # generate HTML-like labels for nodes with children, but do so only # for the node which is not itself a child if (!$self->{origin} && $self->{children} && keys %{$self->{children}} > 0) { #print "Generating HTML-like label for $self->{name}\n"; $a->{label} = $self->_html_like_label(); # make Graphviz avoid the outer border $a->{shape} = 'none'; } # bidirectional and undirected edges if ($self->{bidirectional}) { delete $a->{dir}; my ($n,$s) = Graph::Easy::_graphviz_remap_arrow_style( $self,'', $self->attribute('arrowstyle')); $a->{arrowhead} = $s; $a->{arrowtail} = $s; } if ($self->{undirected}) { delete $a->{dir}; $a->{arrowhead} = 'none'; $a->{arrowtail} = 'none'; } if (!$self->isa_cell()) { # borderstyle: double: my $style = $self->attribute('borderstyle'); my $w = $self->attribute('borderwidth'); $a->{peripheries} = 2 if $style =~ /^double/ && $w > 0; } # For nodes with shape plaintext, set the fillcolor to the background of # the graph/group my $shape = $a->{shape} || 'rect'; if ($class =~ /node/ && $shape eq 'plaintext') { my $p = $self->parent(); $a->{fillcolor} = $p->attribute('fill'); $a->{fillcolor} = 'white' if $a->{fillcolor} eq 'inherit'; } $shape = $self->attribute('shape') unless $self->isa_cell(); # for point-shaped nodes, include the point as label and set width/height if ($shape eq 'point') { require Graph::Easy::As_ascii; # for _u8 and point-style my $style = $self->_point_style( $self->attribute('pointshape'), $self->attribute('pointstyle') ); $a->{label} = $style; # for point-shaped invisible nodes, set height/width = 0 $a->{width} = 0, $a->{height} = 0 if $style eq ''; } if ($shape eq 'invisible') { $a->{label} = ' '; } $a->{rank} = '0' if $root ne '' && $root eq $self->{name}; # create the attributes as text: for my $atr (sort keys %$a) { my $v = $a->{$atr}; $v =~ s/"/\\"/g; # '2"' => '2\"' # don't quote labels like "<_do_place(0,0, { cells => $cells, cache => {} } ); #
Name2
Somewhere
test1
test
my $label = '<'; my $old_y = 0; my $old_x = 0; # go through all children, and sort them by Y then X coordinate my @cells = (); for my $cell (sort { my ($ax,$ay) = split /,/,$a; my ($bx,$by) = split /,/,$b; $ay <=> $by or $ax <=> $bx; } keys %$cells ) { #print "cell $cell\n"; my ($x,$y) = split /,/, $cell; if ($y > $old_y) { $label .= ''; $old_x = 0; } my $n = $cells->{$cell}; my $l = $n->label(); $l =~ s/\\n//g; my $portname = $n->{autosplit_portname}; $portname = $n->label() unless defined $portname; my $name = $self->{name}; $portname =~ s/\"/\\"/g; # quote " $name =~ s/\"/\\"/g; # quote " # store the "nodename:portname" combination for potential edges $n->{_graphviz_portname} = '"' . $name . '":"' . $portname . '"'; if (($x - $old_x) > 0) { # need some spacers $label .= ''; } $label .= ''; $old_y = $y + $n->{cy}; $old_x = $x + $n->{cx}; } # return "<>" $label . '
' . $l . '
>'; } sub _graphviz_point { # return the node as the target/source of an edge # either "name", or "name:port" my ($n) = @_; return $n->{_graphviz_portname} if exists $n->{_graphviz_portname}; $n->as_graphviz_txt(); } sub as_graphviz_txt { # return the node itself (w/o attributes) as graphviz representation my $self = shift; my $name = $self->{name}; # escape special chars in name (including doublequote!) $name =~ s/([\[\]\(\)\{\}"])/\\$1/g; # quote if necessary: # 2, A, A2, "2A", "2 A" etc $name = '"' . $name . '"' if $name !~ /^([a-zA-Z_]+|\d+)\z/ || $name =~ /^(subgraph|graph|node|edge|strict)\z/i; # reserved keyword $name; } 1; __END__ =head1 NAME Graph::Easy::As_graphviz - Generate graphviz description from graph object =head1 SYNOPSIS use Graph::Easy; my $graph = Graph::Easy->new(); my $bonn = Graph::Easy::Node->new( name => 'Bonn', ); my $berlin = Graph::Easy::Node->new( name => 'Berlin', ); $graph->add_edge ($bonn, $berlin); print $graph->as_graphviz(); # prints something like: # digraph NAME { Bonn -> Berlin } =head1 DESCRIPTION C contains just the code for converting a L object to a textual description suitable for feeding it to Graphviz programs like C. =head1 EXPORT Exports nothing. =head1 SEE ALSO L, L. =head1 AUTHOR Copyright (C) 2004 - 2008 by Tels L See the LICENSE file for information. =cut Graph-Easy-0.73/lib/Graph/Easy/Util.pm0000644000076400007640000000133512150106353017260 0ustar shlomifshlomifpackage Graph::Easy::Util; use strict; use warnings; use base 'Exporter'; our @EXPORT_OK = (qw(first_kv ord_values)); use List::Util qw(minstr); =head1 FUNCTIONS =head2 first_kv($hash_ref) The first key value pair from a hash reference - lexicographically. =cut sub first_kv { my $href = shift; my $n = minstr( keys(%$href) ); my $v = $href->{$n}; return ($n, $v); } =head2 ord_values($hash_ref) The values of the hash ordered by a lexicographical keyname. =cut sub ord_values { my $href = shift; if ((!defined $href) || (! %$href)) { return (wantarray ? () : 0); } else { return (wantarray ? @{$href}{sort keys( %$href )} : scalar(keys(%$href))); } } 1; Graph-Easy-0.73/lib/Graph/Easy/As_graphml.pm0000644000076400007640000002446312150075302020426 0ustar shlomifshlomif############################################################################# # Output an Graph::Easy object as GraphML text # ############################################################################# package Graph::Easy::As_graphml; $VERSION = '0.03'; ############################################################################# ############################################################################# package Graph::Easy; use strict; use Graph::Easy::Attributes; # map the Graph::Easy attribute types to a GraphML name: my $attr_type_to_name = { ATTR_STRING() => 'string', ATTR_COLOR() => 'string', ATTR_ANGLE() => 'double', ATTR_PORT() => 'string', ATTR_UINT() => 'integer', ATTR_URL() => 'string', ATTR_LIST() => 'string', ATTR_LCTEXT() => 'string', ATTR_TEXT() => 'string', }; sub _graphml_attr_keys { my ($self, $tpl, $tpl_no_default, $class, $att, $ids, $id) = @_; my $base_class = $class; $base_class =~ s/\..*//; $base_class = 'graph' if $base_class =~ /group/; $ids->{$base_class} = {} unless ref $ids->{$base_class}; my $txt = ''; for my $name (sort keys %$att) { my $entry = $self->_attribute_entry($class,$name); # get a fresh template my $t = $tpl; $t = $tpl_no_default unless defined $entry->[ ATTR_DEFAULT_SLOT ]; # only keep it once next if exists $ids->{$base_class}->{$name}; $t =~ s/##id##/$$id/; # node.foo => node, group.bar => graph $t =~ s/##class##/$base_class/; $t =~ s/##name##/$name/; $t =~ s/##type##/$attr_type_to_name->{ $entry->[ ATTR_TYPE_SLOT ] || ATTR_COLOR }/eg; # will only be there and thus replaced if we have a default if ($t =~ /##default##/) { my $def = $entry->[ ATTR_DEFAULT_SLOT ]; # not a simple value? $def = $self->default_attribute($name) if ref $def; $t =~ s/##default##/$def/; } # remember name => ID $ids->{$base_class}->{$name} = $$id; $$id++; # append the definition $txt .= $t; } $txt; } # yED example: # # # # # # 1 # # # use Graph::Easy::Util qw(ord_values); sub _as_graphml { my $self = shift; my $args = $_[0]; $args = { name => $_[0] } if ref($args) ne 'HASH' && @_ == 1; $args = { @_ } if ref($args) ne 'HASH' && @_ > 1; $args->{format} = 'graph-easy' unless defined $args->{format}; if ($args->{format} !~ /^(graph-easy|Graph::Easy|yED)\z/i) { return $self->error("Format '$args->{format}' not understood by as_graphml."); } my $format = $args->{format}; # Convert the graph to a textual representation - does not need layout(). my $schema = "http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd"; $schema = "http://www.yworks.com/xml/schema/graphml/1.0/ygraphml.xsd" if $format eq 'yED'; my $y_schema = ''; $y_schema = "\n xmlns:y=\"http://www.yworks.com/xml/graphml\"" if $format eq 'yED'; my $txt = < EOF ; $txt =~ s/##DATE##/scalar localtime()/e; $txt =~ s/##VERSION##/$Graph::Easy::VERSION/; $txt =~ s/##SCHEMA##/$schema/; $txt =~ s/##Y##/$y_schema/; # # yellow # # # First gather all possible attributes, then add defines for them. This # avoids lengthy re-definitions of attributes that aren't used: my %keys; my $tpl = ' ' ."\n ##default##\n" ." \n"; my $tpl_no_default = ' '."\n"; # for yED: # # # # # # we need to remember the mapping between attribute name and ID: my $ids = {}; my $id = 'd0'; ########################################################################### # first the class attributes for my $class (sort keys %{$self->{att}}) { my $att = $self->{att}->{$class}; $txt .= $self->_graphml_attr_keys( $tpl, $tpl_no_default, $class, $att, $ids, \$id); } my @nodes = $self->sorted_nodes('name','id'); ########################################################################### # now the attributes on the objects: for my $o (@nodes, ord_values ( $self->{edges} )) { $txt .= $self->_graphml_attr_keys( $tpl, $tpl_no_default, $o->class(), $o->raw_attributes(), $ids, \$id); } $txt .= "\n" unless $id eq 'd0'; my $indent = ' '; $txt .= $indent . '\n"; # output graph attributes: $txt .= $self->_attributes_as_graphml($self,' ',$ids->{graph}); # output groups recursively my @groups = $self->groups_within(0); foreach my $g (@groups) { $txt .= $g->as_graphml($indent.' ',$ids); # marks nodes as processed if nec. } $indent = ' '; foreach my $n (@nodes) { next if $n->{group}; # already done in a group $txt .= $n->as_graphml($indent,$ids); # } $txt .= "\n"; foreach my $n (@nodes) { next if $n->{group}; # already done in a group my @out = $n->sorted_successors(); # for all outgoing connections foreach my $other (@out) { # in case there exists more than one edge from $n --> $other my @edges = $n->edges_to($other); for my $edge (sort { $a->{id} <=> $b->{id} } @edges) { $txt .= $edge->as_graphml($indent,$ids); # } } } $txt .= " \n\n"; $txt; } sub _safe_xml { # make a text XML safe my ($self,$txt) = @_; $txt =~ s/&/&/g; # quote & $txt =~ s/>/>/g; # quote > $txt =~ s/##value##\n"; my $att = $self->get_attributes(); my $txt = ''; for my $n (sort keys %$att) { next unless exists $ids->{$n}; my $def = $self->default_attribute($n); next if defined $def && $def eq $att->{$n}; my $t = $tpl; $t =~ s/##id##/$ids->{$n}/; $t =~ s/##value##/$graph->_safe_xml($att->{$n})/e; $txt .= $t; } $txt; } ############################################################################# package Graph::Easy::Group; use strict; use Graph::Easy::Util qw(ord_values); sub as_graphml { my ($self, $indent, $ids) = @_; my $txt = $indent . '\n"; $txt .= $self->{graph}->_attributes_as_graphml($self, $indent, $ids->{graph}); foreach my $n (ord_values ( $self->{nodes} )) { my @out = $n->sorted_successors(); $txt .= $n->as_graphml($indent.' ', $ids); # # for all outgoing connections foreach my $other (@out) { # in case there exists more than one edge from $n --> $other my @edges = $n->edges_to($other); for my $edge (sort { $a->{id} <=> $b->{id} } @edges) { $txt .= $edge->as_graphml($indent.' ',$ids); } $txt .= "\n" if @edges > 0; } } # output groups recursively my @groups = $self->groups_within(0); foreach my $g (@groups) { $txt .= $g->_as_graphml($indent.' ',$ids); # marks nodes as processed if nec. } # XXX TODO: edges from/to this group # close this group $txt .= $indent . ""; $txt; } ############################################################################# package Graph::Easy::Node; use strict; sub as_graphml { my ($self, $indent, $ids) = @_; my $g = $self->{graph}; my $txt = $indent . '\n"; $txt .= $g->_attributes_as_graphml($self, $indent, $ids->{node}); $txt .= "$indent\n"; return $txt; } ############################################################################# package Graph::Easy::Edge; use strict; sub as_graphml { my ($self, $indent, $ids) = @_; my $g = $self->{graph}; my $txt = $indent . '\n"; $txt .= $g->_attributes_as_graphml($self, $indent, $ids->{edge}); $txt .= "$indent\n"; $txt; } 1; __END__ =head1 NAME Graph::Easy::As_graphml - Generate a GraphML text from a Graph::Easy object =head1 SYNOPSIS use Graph::Easy; my $graph = Graph::Easy->new(); $graph->add_edge ('Bonn', 'Berlin'); print $graph->as_graphml(); =head1 DESCRIPTION C contains just the code for converting a L object to a GraphML text. =head2 Attributes Attributes are output in the format that C specifies. More details about the valid attributes and their default values can be found in the Graph::Easy online manual: L. =head1 EXPORT Exports nothing. =head1 SEE ALSO L, L. =head1 AUTHOR Copyright (C) 2004 - 2008 by Tels L See the LICENSE file for information. =cut Graph-Easy-0.73/lib/Graph/Easy/Layout/0000755000076400007640000000000012150110221017245 5ustar shlomifshlomifGraph-Easy-0.73/lib/Graph/Easy/Layout/Path.pm0000644000076400007640000006032712147701362020531 0ustar shlomifshlomif############################################################################# # Path and cell management for Graph::Easy. # ############################################################################# package Graph::Easy::Layout::Path; $VERSION = '0.16'; ############################################################################# ############################################################################# package Graph::Easy::Node; use strict; use Graph::Easy::Edge::Cell qw/ EDGE_END_E EDGE_END_N EDGE_END_S EDGE_END_W /; sub _shuffle_dir { # take a list with four entries and shuffle them around according to $dir my ($self, $e, $dir) = @_; # $dir: 0 => north, 90 => east, 180 => south, 270 => west $dir = 90 unless defined $dir; # default is east return [ @$e ] if $dir == 90; # default is no shuffling my @shuffle = (0,1,2,3); # the default @shuffle = (1,2,0,3) if $dir == 180; # south @shuffle = (2,3,1,0) if $dir == 270; # west @shuffle = (3,0,2,1) if $dir == 0; # north [ $e->[ $shuffle[0] ], $e->[ $shuffle[1] ], $e->[ $shuffle[2] ], $e->[ $shuffle[3] ], ]; } sub _shift { # get a flow shifted by X° to $dir my ($self, $turn) = @_; my $dir = $self->flow(); $dir += $turn; $dir += 360 if $dir < 0; $dir -= 360 if $dir > 360; $dir; } sub _near_places { # Take a node and return a list of possible placements around it and # prune out already occupied cells. $d is the distance from the node # border and defaults to two (for placements). Set it to one for # adjacent cells. # If defined, $type contains four flags for each direction. If undef, # two entries (x,y) will be returned for each pos, instead of (x,y,type). # If $loose is true, no checking whether the returned fields are free # is done. my ($n, $cells, $d, $type, $loose, $dir) = @_; my $cx = $n->{cx} || 1; my $cy = $n->{cy} || 1; $d = 2 unless defined $d; # default is distance = 2 my $flags = $type; if (ref($flags) ne 'ARRAY') { $flags = [ EDGE_END_W, EDGE_END_N, EDGE_END_E, EDGE_END_S, ]; } $dir = $n->flow() unless defined $dir; my $index = $n->_shuffle_dir( [ 0,3,6,9], $dir); my @places = (); # single-celled node if ($cx + $cy == 2) { my @tries = ( $n->{x} + $d, $n->{y}, $flags->[0], # right $n->{x}, $n->{y} + $d, $flags->[1], # down $n->{x} - $d, $n->{y}, $flags->[2], # left $n->{x}, $n->{y} - $d, $flags->[3], # up ); for my $i (0..3) { my $idx = $index->[$i]; my ($x,$y,$t) = ($tries[$idx], $tries[$idx+1], $tries[$idx+2]); # print STDERR "# Considering place $x, $y \n"; # This quick check does not take node clusters or multi-celled nodes # into account. These are handled in $node->_do_place() later. next if !$loose && exists $cells->{"$x,$y"}; push @places, $x, $y; push @places, $t if defined $type; } return @places; } # Handle a multi-celled node. For a 3x2 node: # A B C # J [00][10][20] D # I [10][11][21] E # H G F # we have 10 (3 * 2 + 2 * 2) places to consider my $nx = $n->{x}; my $ny = $n->{y}; my ($px,$py); my $idx = 0; my @results = ( [], [], [], [] ); $cy--; $cx--; my $t = $flags->[$idx++]; # right $px = $nx + $cx + $d; for my $y (0 .. $cy) { $py = $y + $ny; next if exists $cells->{"$px,$py"} && !$loose; push @{$results[0]}, $px, $py; push @{$results[0]}, $t if defined $type; } # below $py = $ny + $cy + $d; $t = $flags->[$idx++]; for my $x (0 .. $cx) { $px = $x + $nx; next if exists $cells->{"$px,$py"} && !$loose; push @{$results[1]}, $px, $py; push @{$results[1]}, $t if defined $type; } # left $px = $nx - $d; $t = $flags->[$idx++]; for my $y (0 .. $cy) { $py = $y + $ny; next if exists $cells->{"$px,$py"} && !$loose; push @{$results[2]}, $px, $py; push @{$results[2]}, $t if defined $type; } # top $py = $ny - $d; $t = $flags->[$idx]; for my $x (0 .. $cx) { $px = $x + $nx; next if exists $cells->{"$px,$py"} && !$loose; push @{$results[3]}, $px, $py; push @{$results[3]}, $t if defined $type; } # accumulate the results in the requested, shuffled order for my $i (0..3) { my $idx = $index->[$i] / 3; push @places, @{$results[$idx]}; } @places; } sub _allowed_places { # given a list of potential positions, and a list of allowed positions, # return the valid ones (e.g. that are in both lists) my ($self, $places, $allowed, $step) = @_; print STDERR "# calculating allowed places for $self->{name} from " . @$places . " positions and " . scalar @$allowed . " allowed ones:\n" if $self->{graph}->{debug}; $step ||= 2; # default: "x,y" my @good; my $i = 0; while ($i < @$places) { my ($x,$y) = ($places->[$i], $places->[$i+1]); my $allow = 0; my $j = 0; while ($j < @$allowed) { my ($m,$n) = ($allowed->[$j], $allowed->[$j+1]); $allow++ and last if ($m == $x && $n == $y); } continue { $j += 2; } next unless $allow; push @good, $places->[$i + $_ -1] for (1..$step); } continue { $i += $step; } print STDERR "# left with " . ((scalar @good) / $step) . " position(s)\n" if $self->{graph}->{debug}; @good; } sub _allow { # return a list of places, depending on the start/end atribute: # "south" - any place south # "south,0" - first place south # "south,-1" - last place south # XXX TODO: # "south,0..2" - first three places south # "south,0,1,-1" - first, second and last place south my ($self, $dir, @pos) = @_; # for relative direction, get the absolute flow from the node if ($dir =~ /^(front|forward|back|left|right)\z/) { # get the flow at the node $dir = $self->flow(); } my $place = { 'south' => [ 0,0, 0,1, 'cx', 1,0 ], 'north' => [ 0,-1, 0,0, 'cx', 1,0 ], 'east' => [ 0,0, 1,0, 'cy', 0,1 ], 'west' => [ -1,0, 0,0, 'cy', 0,1 ] , 180 => [ 0,0, 0,1, 'cx', 1,0 ], 0 => [ 0,-1, 0,0, 'cx', 1,0 ], 90 => [ 0,0, 1,0, 'cy', 0,1 ], 270 => [ -1,0, 0,0, 'cy', 0,1 ] , }; my $p = $place->{$dir}; return [] unless defined $p; # start pos my $x = $p->[0] + $self->{x} + $p->[2] * $self->{cx}; my $y = $p->[1] + $self->{y} + $p->[3] * $self->{cy}; my @allowed; push @pos, '' if @pos == 0; my $c = $p->[4]; if (@pos == 1 && $pos[0] eq '') { # allow all of them for (1 .. $self->{$c}) { push @allowed, $x, $y; $x += $p->[5]; $y += $p->[6]; } } else { # allow only the given position my $ps = $pos[0]; # limit to 0..$self->{cx}-1 $ps = $self->{$c} + $ps if $ps < 0; $ps = 0 if $ps < 0; $ps = $self->{$c} - 1 if $ps >= $self->{$c}; $x += $p->[5] * $ps; $y += $p->[6] * $ps; push @allowed, $x, $y; } \@allowed; } package Graph::Easy; use strict; use Graph::Easy::Node::Cell; use Graph::Easy::Edge::Cell qw/ EDGE_HOR EDGE_VER EDGE_CROSS EDGE_TYPE_MASK EDGE_HOLE /; sub _clear_tries { # Take a list of potential positions for a node, and then remove the # ones that are immidiately near any other node. # Returns a list of "good" positions. Afterwards $node->{x} is undef. my ($self, $node, $cells, $tries) = @_; my $src = 0; my @new; print STDERR "# clearing ", scalar @$tries / 2, " tries for $node->{name}\n" if $self->{debug}; my $node_grandpa = $node->find_grandparent(); while ($src < scalar @$tries) { # check the current position # temporary place node here my $x = $tries->[$src]; my $y = $tries->[$src+1]; # print STDERR "# checking $x,$y\n" if $self->{debug}; $node->{x} = $x; $node->{y} = $y; my @near = $node->_near_places($cells, 1, undef, 1); # push also the four corner cells to avoid placing nodes corner-to-corner push @near, $x-1, $y-1, # upperleft corner $x-1, $y+($node->{cy}||1), # lowerleft corner $x+($node->{cx}||1), $y+($node->{cy}||1), # lowerright corner $x+($node->{cx}||1), $y-1; # upperright corner # check all near places to be free from nodes (except our children) my $j = 0; my $g = 0; while ($j < @near) { my $xy = $near[$j]. ',' . $near[$j+1]; # print STDERR "# checking near-place: $xy: " . ref($cells->{$xy}) . "\n" if $self->{debug}; my $cell = $cells->{$xy}; # skip, unless we are a children of node, or the cell is our children next unless ref($cell) && $cell->isa('Graph::Easy::Node'); my $grandpa = $cell->find_grandparent(); # this cell is our children # this cell is our grandpa # has the same grandpa as node next if $grandpa == $node || $cell == $node_grandpa || $grandpa == $node_grandpa; $g++; last; } continue { $j += 2; } if ($g == 0) { push @new, $tries->[$src], $tries->[$src+1]; } $src += 2; } $node->{x} = undef; @new; } my $flow_shift = { 270 => [ 0, -1 ], 90 => [ 0, 1 ], 0 => [ 1, 0 ], 180 => [ -1, 0 ], }; sub _placed_shared { # check whether one of the nodes from the list of shared was already placed my ($self) = shift; my $placed; for my $n (@_) { $placed = [$n->{x}, $n->{y}] and last if defined $n->{x}; } $placed; } use Graph::Easy::Util qw(first_kv); sub _find_node_place { # Try to place a node (or node cluster). Return score (usually 0). my ($self, $node, $try, $parent, $edge) = @_; $try ||= 0; print STDERR "# Finding place for $node->{name}, try #$try\n" if $self->{debug}; print STDERR "# Parent node is '$parent->{name}'\n" if $self->{debug} && ref $parent; print STDERR "# called from ". join (" ", caller) . "\n" if $self->{debug}; # If the node has a user-set rank, see if we already placed another node in that # row/column if ($node->{rank} >= 0) { my $r = abs($node->{rank}); # print STDERR "# User-set rank for $node->{name} (rank $r)\n"; my $c = $self->{_rank_coord}; # use Data::Dumper; print STDERR "# rank_pos: \n", Dumper($self->{_rank_pos}); if (exists $self->{_rank_pos}->{ $r }) { my $co = { x => 0, y => 0 }; $co->{$c} = $self->{_rank_pos}->{ $r }; while (1 < 3) { # print STDERR "# trying to force placement of '$node->{name}' at $co->{x} $co->{y}\n"; return 0 if $node->_do_place($co->{x},$co->{y},$self); $co->{$c} += 2; } } } my $cells = $self->{cells}; # local $self->{debug} = 1; my $min_dist = 2; # minlen = 0 => min_dist = 2, # minlen = 1 => min_dist = 2, # minlen = 2 => min_dist = 3, etc $min_dist = $edge->attribute('minlen') + 1 if ref($edge); # if the node has outgoing edges (which might be shared) if (!ref($edge)) { (undef,$edge) = first_kv($node->{edges}) if keys %{$node->{edges}} > 0; } my $dir = undef; $dir = $edge->flow() if ref($edge); my @tries; # if (ref($parent) && defined $parent->{x}) if (keys %{$node->{edges}} > 0) { my $src_node = $parent; $src_node = $edge->{from} if ref($edge) && !ref($parent); print STDERR "# from $src_node->{name} to $node->{name}: edge $edge dir $dir\n" if $self->{debug}; # if there are more than one edge to this node, and they share a start point, # move the node at least 3 cells away to create space for the joints my ($s_p, @ss_p); ($s_p, @ss_p) = $edge->port('start') if ref($edge); my ($from,$to); if (ref($edge)) { $from = $edge->{from}; $to = $edge->{to}; } my @shared_nodes; @shared_nodes = $from->nodes_sharing_start($s_p,@ss_p) if defined $s_p && @ss_p > 0; print STDERR "# Edge from '$src_node->{name}' shares an edge start with ", scalar @shared_nodes, " other nodes\n" if $self->{debug}; if (@shared_nodes > 1) { $min_dist = 3 if $min_dist < 3; # make space $min_dist++ if $edge->label() ne ''; # make more space for the label # if we are the first shared node to be placed my $placed = $self->_placed_shared(@shared_nodes); if (defined $placed) { # we are not the first, so skip the placement below # instead place on the same column/row as already placed node(s) my ($bx, $by) = @$placed; my $flow = $node->flow(); print STDERR "# One of the shared nodes was already placed at ($bx,$by) with flow $flow\n" if $self->{debug}; my $ofs = 2; # start with a distance of 2 my ($mx, $my) = @{ ($flow_shift->{$flow} || [ 0, 1 ]) }; while (1) { my $x = $bx + $mx * $ofs; my $y = $by + $my * $ofs; print STDERR "# Trying to place $node->{name} at ($x,$y)\n" if $self->{debug}; next if $self->_clear_tries($node, $cells, [ $x,$y ]) == 0; last if $node->_do_place($x,$y,$self); } continue { $ofs += 2; } return 0; # found place already } # end we-are-the-first-to-be-placed } # shared end point? ($s_p, @ss_p) = $edge->port('end') if ref($edge); @shared_nodes = $to->nodes_sharing_end($s_p,@ss_p) if defined $s_p && @ss_p > 0; print STDERR "# Edge from '$src_node->{name}' shares an edge end with ", scalar @shared_nodes, " other nodes\n" if $self->{debug}; if (@shared_nodes > 1) { $min_dist = 3 if $min_dist < 3; $min_dist++ if $edge->label() ne ''; # make more space for the label # if the node to be placed is not in the list to be placed, it is the end-point # see if we are the first shared node to be placed my $placed = $self->_placed_shared(@shared_nodes); # print STDERR "# "; for (@shared_nodes) { print $_->{name}, " "; } print "\n"; if ((grep( $_ == $node, @shared_nodes)) && defined $placed) { # we are not the first, so skip the placement below # instead place on the same column/row as already placed node(s) my ($bx, $by) = @$placed; my $flow = $node->flow(); print STDERR "# One of the shared nodes was already placed at ($bx,$by) with flow $flow\n" if $self->{debug}; my $ofs = 2; # start with a distance of 2 my ($mx, $my) = @{ ($flow_shift->{$flow} || [ 0, 1 ]) }; while (1) { my $x = $bx + $mx * $ofs; my $y = $by + $my * $ofs; print STDERR "# Trying to place $node->{name} at ($x,$y)\n" if $self->{debug}; next if $self->_clear_tries($node, $cells, [ $x,$y ]) == 0; last if $node->_do_place($x,$y,$self); } continue { $ofs += 2; } return 0; # found place already } # end we-are-the-first-to-be-placed } } if (ref($parent) && defined $parent->{x}) { @tries = $parent->_near_places($cells, $min_dist, undef, 0, $dir); print STDERR "# Trying chained placement of $node->{name} with min distance $min_dist from parent $parent->{name}\n" if $self->{debug}; # weed out positions that are unsuitable @tries = $self->_clear_tries($node, $cells, \@tries); splice (@tries,0,$try) if $try > 0; # remove the first N tries print STDERR "# Left with " . scalar @tries . " tries for node $node->{name}\n" if $self->{debug}; while (@tries > 0) { my $x = shift @tries; my $y = shift @tries; print STDERR "# Trying to place $node->{name} at $x,$y\n" if $self->{debug}; return 0 if $node->_do_place($x,$y,$self); } # for all trial positions } print STDERR "# Trying to place $node->{name} at 0,0\n" if $try == 0 && $self->{debug}; # Try to place node at upper left corner (the very first node to be # placed will usually end up there). return 0 if $try == 0 && $node->_do_place(0,0,$self); # try to place node near the predecessor(s) my @pre_all = $node->predecessors(); print STDERR "# Predecessors of $node->{name} " . scalar @pre_all . "\n" if $self->{debug}; # find all already placed predecessors my @pre; for my $p (@pre_all) { push @pre, $p if defined $p->{x}; print STDERR "# Placed predecessors of $node->{name}: $p->{name} at $p->{x},$p->{y}\n" if $self->{debug} && defined $p->{x}; } # sort predecessors on their rank (to try first the higher ranking ones on placement) @pre = sort { $b->{rank} <=> $a->{rank} } @pre; print STDERR "# Number of placed predecessors of $node->{name}: " . scalar @pre . "\n" if $self->{debug}; if (@pre <= 2 && @pre > 0) { if (@pre == 1) { # only one placed predecessor, so place $node near it print STDERR "# placing $node->{name} near predecessor\n" if $self->{debug}; @tries = ( $pre[0]->_near_places($cells, $min_dist), $pre[0]->_near_places($cells,$min_dist+2) ); } else { # two placed predecessors, so place at crossing point of both of them # compute difference between the two nodes my $dx = ($pre[0]->{x} - $pre[1]->{x}); my $dy = ($pre[0]->{y} - $pre[1]->{y}); # are both nodes NOT on a straight line? if ($dx != 0 && $dy != 0) { # ok, so try to place at the crossing point @tries = ( $pre[0]->{x}, $pre[1]->{y}, $pre[0]->{y}, $pre[1]->{x}, ); } else { # two nodes on a line, try to place node in the middle if ($dx == 0) { @tries = ( $pre[1]->{x}, $pre[1]->{y} + int($dy / 2) ); } else { @tries = ( $pre[1]->{x} + int($dx / 2), $pre[1]->{y} ); } } # XXX TODO BUG: shouldnt we also try this if we have more than 2 placed # predecessors? # In addition, we can also try to place the node around the # different nodes: foreach my $n (@pre) { push @tries, $n->_near_places($cells, $min_dist); } } } my @suc_all = $node->successors(); # find all already placed successors my @suc; for my $s (@suc_all) { push @suc, $s if defined $s->{x}; } print STDERR "# Number of placed successors of $node->{name}: " . scalar @suc . "\n" if $self->{debug}; foreach my $s (@suc) { # for each successors (especially if there is only one), try to place near push @tries, $s->_near_places($cells, $min_dist); push @tries, $s->_near_places($cells, $min_dist + 2); } # weed out positions that are unsuitable @tries = $self->_clear_tries($node, $cells, \@tries); print STDERR "# Left with " . scalar @tries . " for node $node->{name}\n" if $self->{debug}; splice (@tries,0,$try) if $try > 0; # remove the first N tries while (@tries > 0) { my $x = shift @tries; my $y = shift @tries; print STDERR "# Trying to place $node->{name} at $x,$y\n" if $self->{debug}; return 0 if $node->_do_place($x,$y,$self); } # for all trial positions ############################################################################## # all simple possibilities exhausted, try a generic approach print STDERR "# No more simple possibilities for node $node->{name}\n" if $self->{debug}; # XXX TODO: # find out which sides of the node predecessor node(s) still have free # ports/slots. With increasing distances, try to place the node around these. # If no predecessors/incoming edges, try to place in column 0, otherwise # considered the node's rank, too my $col = 0; $col = $node->{rank} * 2 if @pre > 0; $col = $pre[0]->{x} if @pre > 0; # find the first free row my $y = 0; $y +=2 while (exists $cells->{"$col,$y"}); $y += 1 if exists $cells->{"$col," . ($y-1)}; # leave one cell spacing # now try to place node (or node cluster) while (1) { next if $self->_clear_tries($node, $cells, [ $col,$y ]) == 0; last if $node->_do_place($col,$y,$self); } continue { $y += 2; } $node->{x} = $col; 0; # success, score 0 } sub _trace_path { # find a free way from $src to $dst (both need to be placed beforehand) my ($self, $src, $dst, $edge) = @_; print STDERR "# Finding path from '$src->{name}' to '$dst->{name}'\n" if $self->{debug}; print STDERR "# src: $src->{x}, $src->{y} dst: $dst->{x}, $dst->{y}\n" if $self->{debug}; my $coords = $self->_find_path ($src, $dst, $edge); # found no path? if (!defined $coords) { print STDERR "# Unable to find path from $src->{name} ($src->{x},$src->{y}) to $dst->{name} ($dst->{x},$dst->{y})\n" if $self->{debug}; return undef; } # path is empty, happens for sharing edges with only a joint return 1 if scalar @$coords == 0; # Create all cells from the returned list and score path (lower score: better) my $i = 0; my $score = 0; while ($i < scalar @$coords) { my $type = $coords->[$i+2]; $self->_create_cell($edge,$coords->[$i],$coords->[$i+1],$type); $score ++; # each element: one point $type &= EDGE_TYPE_MASK; # mask flags # edge bend or cross: one point extra $score ++ if $type != EDGE_HOR && $type != EDGE_VER; $score += 3 if $type == EDGE_CROSS; # crossings are doubleplusungood $i += 3; } $score; } sub _create_cell { my ($self,$edge,$x,$y,$type) = @_; my $cells = $self->{cells}; my $xy = "$x,$y"; if (ref($cells->{$xy}) && $cells->{$xy}->isa('Graph::Easy::Edge')) { $cells->{$xy}->_make_cross($edge,$type & EDGE_FLAG_MASK); # insert a EDGE_HOLE into the cells of the edge (but not into the list of # to-be-rendered cells). This cell will be removed by the optimizer later on. Graph::Easy::Edge::Cell->new( type => EDGE_HOLE, edge => $edge, x => $x, y => $y ); return; } my $path = Graph::Easy::Edge::Cell->new( type => $type, edge => $edge, x => $x, y => $y ); $cells->{$xy} = $path; # store in cells } sub _path_is_clear { # For all points (x,y pairs) in the path, check that the cell is still free # $path points to a list of [ x,y,type, x,y,type, ...] my ($self,$path) = @_; my $cells = $self->{cells}; my $i = 0; while ($i < scalar @$path) { my $x = $path->[$i]; my $y = $path->[$i+1]; # my $t = $path->[$i+2]; $i += 3; return 0 if exists $cells->{"$x,$y"}; # obstacle hit } 1; # path is clear } 1; __END__ =head1 NAME Graph::Easy::Layout::Path - Path management for Manhattan-style grids =head1 SYNOPSIS use Graph::Easy; my $graph = Graph::Easy->new(); my $bonn = Graph::Easy::Node->new( name => 'Bonn', ); my $berlin = Graph::Easy::Node->new( name => 'Berlin', ); $graph->add_edge ($bonn, $berlin); $graph->layout(); print $graph->as_ascii( ); # prints: # +------+ +--------+ # | Bonn | --> | Berlin | # +------+ +--------+ =head1 DESCRIPTION C contains just the actual path-managing code for L, e.g. to create/destroy/maintain paths, node placement etc. =head1 EXPORT Exports nothing. =head1 SEE ALSO L. =head1 METHODS into Graph::Easy This module injects the following methods into C: =head2 _path_is_clear() $graph->_path_is_clear($path); For all points (x,y pairs) in the path, check that the cell is still free. C<$path> points to a list x,y,type pairs as in C<< [ [x,y,type], [x,y,type], ...] >>. =head2 _create_cell() my $cell = $graph->($edge,$x,$y,$type); Create a cell at C<$x,$y> coordinates with type C<$type> for the specified edge. =head2 _path_is_clear() $graph->_path_is_clear(); For all points (x,y pairs) in the path, check that the cell is still free. C<$path> points to a list of C<[ x,y,type, x,y,type, ...]>. Returns true when the path is clear, false otherwise. =head2 _trace_path() my $path = my $graph->_trace_path($src,$dst,$edge); Find a free way from source node/group to destination node/group for the specified edge. Both source and destination need to be placed beforehand. =head1 METHODS in Graph::Easy::Node This module injects the following methods into C: =head2 _near_places() my $node->_near_places(); Take a node and return a list of possible placements around it and prune out already occupied cells. $d is the distance from the node border and defaults to two (for placements). Set it to one for adjacent cells. =head2 _shuffle_dir() my $dirs = $node->_shuffle_dir( [ 0,1,2,3 ], $dir); Take a ref to an array with four entries and shuffle them around according to C<$dir>. =head2 _shift() my $dir = $node->_shift($degrees); Return a the C direction shifted by X degrees to C<$dir>. =head1 AUTHOR Copyright (C) 2004 - 2007 by Tels L. See the LICENSE file for information. =cut Graph-Easy-0.73/lib/Graph/Easy/Layout/Force.pm0000644000076400007640000001267212150071727020672 0ustar shlomifshlomif############################################################################# # Force-based layouter for Graph::Easy. # # (c) by Tels 2004-2007. ############################################################################# package Graph::Easy::Layout::Force; $VERSION = '0.01'; ############################################################################# ############################################################################# package Graph::Easy; use strict; use Graph::Easy::Util qw(ord_values); sub _layout_force { # Calculate for each node the force on it, then move them accordingly. # When things have settled, stop. my ($self) = @_; # For each node, calculate the force actiing on it, seperated into two # components along the X and Y axis: # XXX TODO: replace with all contained nodes + groups my @nodes = $self->nodes(); return if @nodes == 0; my $root = $self->root_node(); if (!defined $root) { # find a suitable root node $root = $nodes[0]; } # this node never moves $root->{_pinned} = undef; $root->{x} = 0; $root->{y} = 0; # get the "gravity" force my $gx = 0; my $gy = 0; my $flow = $self->flow(); if ($flow == 0) { $gx = 1; } elsif ($flow == 90) { $gy = -1; } elsif ($flow == 270) { $gy = 1; } else # ($flow == 180) { $gx = -1; } my @particles; # set initial positions for my $n (@nodes) { # the net force on this node is the gravity $n->{_x_force} = $gx; $n->{_y_force} = $gy; if ($root == $n || defined $n->{origin}) { # nodes that are relative to another are "pinned" $n->{_pinned} = undef; } else { $n->{x} = rand(100); $n->{y} = rand(100); push @particles, $n; } } my $energy = 1; while ($energy > 0.1) { $energy = 0; for my $n (@particles) { # reset forces on this node $n->{_x_force} = 0; $n->{_y_force} = 0; # Add forces of all other nodes. We need to include pinned nodes here, # too, since a moving node might get near a pinned one and get repelled. for my $n2 (@nodes) { next if $n2 == $n; # don't repel yourself my $dx = ($n->{x} - $n2->{x}); my $dy = ($n->{y} - $n2->{y}); my $r = $dx * $dx + $dy * $dy; $r = 0.01 if $r < 0.01; # too small? if ($r < 4) { # not too big $n->{_x_force} += 1 / $dx * $dx; $n->{_y_force} += 1 / $dy * $dy; my $dx2 = 1 / $dx * $dx; my $dy2 = 1 / $dy * $dy; print STDERR "# Force between $n->{name} and $n2->{name}: fx $dx2, fy $dy2\n"; } } # for all edges connected at this node for my $e (ord_values ( $n->{edges} )) { # exclude self-loops next if $e->{from} == $n && $e->{to} == $n; # get the other end-point of this edge my $n2 = $e->{from}; $n2 = $e->{to} if $n2 == $n; # XXX TODO # we should "connect" the edges to the appropriate port so that # they excert an off-center force my $dx = -($n->{x} - $n2->{x}) / 2; my $dy = -($n->{y} - $n2->{y}) / 2; print STDERR "# Spring force between $n->{name} and $n2->{name}: fx $dx, fy $dy\n"; $n->{_x_force} += $dx; $n->{_y_force} += $dy; } print STDERR "# $n->{name}: Summed force: fx $n->{_x_force}, fy $n->{_y_force}\n"; # for grid-like layouts, add a small force drawing this node to the gridpoint # 0.7 => 1 - 0.7 => 0.3 # 1.2 => 1 - 1.2 => -0.2 my $dx = int($n->{x} + 0.5) - $n->{x}; $n->{_x_force} += $dx; my $dy = int($n->{y} + 0.5) - $n->{y}; $n->{_y_force} += $dy; print STDERR "# $n->{name}: Final force: fx $n->{_x_force}, fy $n->{_y_force}\n"; $energy += $n->{_x_force} * $n->{_x_force} + $n->{_x_force} * $n->{_y_force}; print STDERR "# Net energy: $energy\n"; } # after having calculated all forces, move the nodes for my $n (@particles) { my $dx = $n->{_x_force}; $dx = 5 if $dx > 5; # limit it $n->{x} += $dx; my $dy = $n->{_y_force}; $dy = 5 if $dy > 5; # limit it $n->{y} += $dy; print STDERR "# $n->{name}: Position $n->{x}, $n->{y}\n"; } sleep(1); print STDERR "\n"; } for my $n (@nodes) { delete $n->{_x_force}; delete $n->{_y_force}; } $self; } 1; __END__ =head1 NAME Graph::Easy::Layout::Force - Force-based layouter for Graph::Easy =head1 SYNOPSIS use Graph::Easy; my $graph = Graph::Easy->new(); $graph->add_edge ('Bonn', 'Berlin'); $graph->add_edge ('Bonn', 'Ulm'); $graph->add_edge ('Ulm', 'Berlin'); $graph->layout( type => 'force' ); print $graph->as_ascii( ); # prints: # +------------------------+ # | v # +------+ +-----+ +--------+ # | Bonn | --> | Ulm | --> | Berlin | # +------+ +-----+ +--------+ =head1 DESCRIPTION C contains routines that calculate a force-based layout for a graph. Nodes repell each other, while edges connecting them draw them together. The layouter calculates the forces on each node, then moves them around according to these forces until things have settled down. Used automatically by Graph::Easy. =head1 EXPORT Exports nothing. =head1 SEE ALSO L. =head1 METHODS This module injects the following methods into Graph::Easy: =head2 _layout_force() Calculates the node position with a force-based method. =head1 AUTHOR Copyright (C) 2004 - 2007 by Tels L. See the LICENSE file for information. =cut Graph-Easy-0.73/lib/Graph/Easy/Layout/Chain.pm0000644000076400007640000003243312150071742020650 0ustar shlomifshlomif############################################################################# # One chain of nodes in a Graph::Easy - used internally for layouts. # # (c) by Tels 2004-2006. Part of Graph::Easy ############################################################################# package Graph::Easy::Layout::Chain; use Graph::Easy::Base; $VERSION = '0.09'; @ISA = qw/Graph::Easy::Base/; use strict; use Graph::Easy::Util qw(ord_values); use constant { _ACTION_NODE => 0, # place node somewhere _ACTION_TRACE => 1, # trace path from src to dest _ACTION_CHAIN => 2, # place node in chain (with parent) _ACTION_EDGES => 3, # trace all edges (shortes connect. first) }; ############################################################################# sub _init { # Generic init routine, to be overriden in subclasses. my ($self,$args) = @_; foreach my $k (sort keys %$args) { if ($k !~ /^(start|graph)\z/) { require Carp; Carp::confess ("Invalid argument '$k' passed to __PACKAGE__->new()"); } $self->{$k} = $args->{$k}; } $self->{end} = $self->{start}; # store chain at node (to lookup node => chain info) $self->{start}->{_chain} = $self; $self->{start}->{_next} = undef; $self->{len} = 1; $self; } sub start { # return first node in the chain my $self = shift; $self->{start}; } sub end { # return last node in the chain my $self = shift; $self->{end}; } sub add_node { # add a node at the end of the chain my ($self, $node) = @_; # store at end $self->{end}->{_next} = $node; $self->{end} = $node; # store chain at node (to lookup node => chain info) $node->{_chain} = $self; $node->{_next} = undef; $self->{len} ++; $self; } sub length { # Return the length of the chain in nodes. Takes optional # node from where to calculate length. my ($self, $node) = @_; return $self->{len} unless defined $node; my $len = 0; while (defined $node) { $len++; $node = $node->{_next}; } $len; } sub nodes { # return all the nodes in the chain as a list, in order. my $self = shift; my @nodes = (); my $n = $self->{start}; while (defined $n) { push @nodes, $n; $n = $n->{_next}; } @nodes; } sub layout { # Return an action stack containing the nec. actions to # lay out the nodes in the chain, plus any connections between # them. my ($self, $edge) = @_; # prevent doing it twice return [] if $self->{_done}; $self->{_done} = 1; my @TODO = (); my $g = $self->{graph}; # first, layout all the nodes in the chain: # start with first node my $pre = $self->{start}; my $n = $pre->{_next}; if (exists $pre->{_todo}) { # edges with a flow attribute must be handled differently # XXX TODO: the test for attribute('flow') might be wrong (raw_attribute()?) if ($edge && ($edge->{to} == $pre) && ($edge->attribute('flow') || $edge->has_ports())) { push @TODO, $g->_action( _ACTION_CHAIN, $pre, 0, $edge->{from}, $edge); } else { push @TODO, $g->_action( _ACTION_NODE, $pre, 0, $edge ); } } print STDERR "# Stack after first:\n" if $g->{debug}; $g->_dump_stack(@TODO) if $g->{debug}; while (defined $n) { if (exists $n->{_todo}) { # CHAIN means if $n isn't placed yet, it will be done with # $pre as parent: # in case there are multiple edges to the target node, use the first # one to determine the flow: my @edges = $g->edge($pre,$n); push @TODO, $g->_action( _ACTION_CHAIN, $n, 0, $pre, $edges[0] ); } $pre = $n; $n = $n->{_next}; } print STDERR "# Stack after chaining:\n" if $g->{debug}; $g->_dump_stack(@TODO) if $g->{debug}; # link from each node to the next $pre = $self->{start}; $n = $pre->{_next}; while (defined $n) { # first do edges going from P to N #for my $e (sort { $a->{to}->{name} cmp $b->{to}->{name} } values %{$pre->{edges}}) for my $e (ord_values ( $pre->{edges})) { # skip selfloops and backward links, these will be done later next if $e->{to} != $n; next unless exists $e->{_todo}; # skip links from/to groups next if $e->{to}->isa('Graph::Easy::Group') || $e->{from}->isa('Graph::Easy::Group'); # # skip edges with a flow # next if exists $e->{att}->{start} || exist $e->{att}->{end}; push @TODO, [ _ACTION_TRACE, $e ]; delete $e->{_todo}; } } continue { $pre = $n; $n = $n->{_next}; } print STDERR "# Stack after chain-linking:\n" if $g->{debug}; $g->_dump_stack(@TODO) if $g->{debug}; # Do all other links inside the chain (backwards, going forward more than # one node etc) $n = $self->{start}; while (defined $n) { my @edges; my @count; print STDERR "# inter-chain link from $n->{name}\n" if $g->{debug}; # gather all edges starting at $n, but do the ones with a flow first # for my $e (sort { $a->{to}->{name} cmp $b->{to}->{name} } values %{$n->{edges}}) for my $e (ord_values ( $n->{edges})) { # skip selfloops, these will be done later next if $e->{to} == $n; next if !ref($e->{to}->{_chain}); next if !ref($e->{from}->{_chain}); next if $e->has_ports(); # skip links from/to groups next if $e->{to}->isa('Graph::Easy::Group') || $e->{from}->isa('Graph::Easy::Group'); print STDERR "# inter-chain link from $n->{name} to $e->{to}->{name}\n" if $g->{debug}; # leaving the chain? next if $e->{to}->{_chain} != $self; # print STDERR "# trying for $n->{name}:\t $e->{from}->{name} to $e->{to}->{name}\n"; next unless exists $e->{_todo}; # calculate for this edge, how far it goes my $count = 0; my $curr = $n; while (defined $curr && $curr != $e->{to}) { $curr = $curr->{_next}; $count ++; } if (!defined $curr) { # edge goes backward # start at $to $curr = $e->{to}; $count = 0; while (defined $curr && $curr != $e->{from}) { $curr = $curr->{_next}; $count ++; } $count = 100000 if !defined $curr; # should not happen } push @edges, [ $count, $e ]; push @count, [ $count, $e->{from}->{name}, $e->{to}->{name} ]; } # use Data::Dumper; print STDERR "count\n", Dumper(@count); # do edges, shortest first for my $e (sort { $a->[0] <=> $b->[0] } @edges) { push @TODO, [ _ACTION_TRACE, $e->[1] ]; delete $e->[1]->{_todo}; } $n = $n->{_next}; } # also do all selfloops on $n $n = $self->{start}; while (defined $n) { # for my $e (sort { $a->{to}->{name} cmp $b->{to}->{name} } values %{$n->{edges}}) for my $e (ord_values $n->{edges}) { next unless exists $e->{_todo}; # print STDERR "# $e->{from}->{name} to $e->{to}->{name} on $n->{name}\n"; # print STDERR "# ne $e->{to} $n $e->{id}\n" # if $e->{from} != $n || $e->{to} != $n; # no selfloop? next if $e->{from} != $n || $e->{to} != $n; # no selfloop? push @TODO, [ _ACTION_TRACE, $e ]; delete $e->{_todo}; } $n = $n->{_next}; } print STDERR "# Stack after self-loops:\n" if $g->{debug}; $g->_dump_stack(@TODO) if $g->{debug}; # XXX TODO # now we should do any links that start or end at this chain, recursively $n = $self->{start}; while (defined $n) { # all chains that start at this node for my $e (sort { $a->{to}->{name} cmp $b->{to}->{name} } values %{$n->{edges}}) { my $to = $e->{to}; # skip links to groups next if $to->isa('Graph::Easy::Group'); # print STDERR "# chain-tracking to: $to->{name} $to->{_chain}\n"; next unless exists $to->{_chain} && ref($to->{_chain}) =~ /Chain/; my $chain = $to->{_chain}; next if $chain->{_done}; # print STDERR "# chain-tracking to: $to->{name}\n"; # pass the edge along, in case it has a flow # my @pass = (); # push @pass, $e if $chain->{_first} && $e->{to} == $chain->{_first}; push @TODO, @{ $chain->layout($e) } unless $chain->{_done}; # link the edges to $to next unless exists $e->{_todo}; # was already done above? # next if $e->has_ports(); push @TODO, [ _ACTION_TRACE, $e ]; delete $e->{_todo}; } $n = $n->{_next}; } \@TODO; } sub dump { # dump the chain to STDERR my ($self, $indent) = @_; $indent = '' unless defined $indent; print STDERR "#$indent chain id $self->{id} (len $self->{len}):\n"; print STDERR "#$indent is empty\n" and return if $self->{len} == 0; my $n = $self->{start}; while (defined $n) { print STDERR "#$indent $n->{name} (chain id: $n->{_chain}->{id})\n"; $n = $n->{_next}; } $self; } sub merge { # take another chain, and merge it into ourselves. If $where is defined, # absorb only the nodes from $where onwards (instead of all of them). my ($self, $other, $where) = @_; my $g = $self->{graph}; print STDERR "# panik: ", join(" \n",caller()),"\n" if !defined $other; print STDERR "# Merging chain $other->{id} (len $other->{len}) into $self->{id} (len $self->{len})\n" if $g->{debug}; print STDERR "# Merging from $where->{name} onwards\n" if $g->{debug} && ref($where); # cannot merge myself into myself (without allocating infinitely memory) return if $self == $other; # start at start as default $where = undef unless ref($where) && exists $where->{_chain} && $where->{_chain} == $other; $where = $other->{start} unless defined $where; # make all nodes from chain #1 belong to it (to detect loops) my $n = $self->{start}; while (defined $n) { $n->{_chain} = $self; $n = $n->{_next}; } print STDERR "# changed nodes\n" if $g->{debug}; $self->dump() if $g->{debug}; # terminate at $where $self->{end}->{_next} = $where; $self->{end} = $other->{end}; # start at joiner $n = $where; while (ref($n)) { $n->{_chain} = $self; my $pre = $n; $n = $n->{_next}; # sleep(1); # print "# at $n->{name} $n->{_chain}\n" if ref($n); if (ref($n) && defined $n->{_chain} && $n->{_chain} == $self) # already points into ourself? { # sleep(1); # print "# pre $pre->{name} $pre->{_chain}\n"; $pre->{_next} = undef; # terminate $self->{end} = $pre; last; } } # could speed this up $self->{len} = 0; $n = $self->{start}; while (defined $n) { $self->{len}++; $n = $n->{_next}; } # print "done merging, dumping result:\n"; # $self->dump(); sleep(10); if (defined $other->{start} && $where == $other->{start}) { # we absorbed the other chain completely, so drop it $other->{end} = undef; $other->{start} = undef; $other->{len} = 0; # caller is responsible for cleaning it up } print STDERR "# after merging\n" if $g->{debug}; $self->dump() if $g->{debug}; $self; } 1; __END__ =head1 NAME Graph::Easy::Layout::Chain - Chain of nodes for layouter =head1 SYNOPSIS # used internally, do not use directly use Graph::Easy; use Graph::Easy::Layout::Chain; my $graph = Graph::Easy->new( ); my ($node, $node2) = $graph->add_edge( 'A', 'B' ); my $chain = Graph::Easy::Layout::Chain->new( start => $node, graph => $graph, ); $chain->add_node( $node2 ); =head1 DESCRIPTION A C object represents a chain of nodes for the layouter. =head1 METHODS =head2 new() my $chain = Graph::Easy::Layout::Chain->new( start => $node ); Create a new chain and set its starting node to C<$node>. =head2 length() my $len = $chain->length(); Return the length of the chain, in nodes. my $len = $chain->length( $node ); Given an optional C<$node> as argument, returns the length from that node onwards. For the chain with the three nodes A, B and C would return 3, 2, and 1 for A, B and C, respectively. Returns 0 if the passed node is not part of this chain. =head2 nodes() my @nodes = $chain->nodes(); Return all the node objects in the chain as list, in order. =head2 add_node() $chain->add_node( $node ); Add C<$node> to the end of the chain. =head2 start() my $node = $chain->start(); Return first node in the chain. =head2 end() my $node = $chain->end(); Return last node in the chain. =head2 layout() my $todo = $chain->layout(); Return an action stack as array ref, containing the nec. actions to layout the chain (nodes, plus interlinks in the chain). Will recursively traverse all chains linked to this chain. =head2 merge() my $chain->merge ( $other_chain ); my $chain->merge ( $other_chain, $where ); Merge the other chain into ourselves, adding its nodes at our end. The other chain is emptied and must be deleted by the caller. If C<$where> is defined and a member of C<$other_chain>, absorb only the nodes from C<$where> onwards, instead of all of them. =head2 error() $last_error = $node->error(); $node->error($error); # set new messags $node->error(''); # clear error Returns the last error message, or '' for no error. =head2 dump() $chain->dump(); Dump the chain to STDERR, to aid debugging. =head1 EXPORT None by default. =head1 SEE ALSO L, L. =head1 AUTHOR Copyright (C) 2004 - 2006 by Tels L. See the LICENSE file for more details. =cut Graph-Easy-0.73/lib/Graph/Easy/Layout/Repair.pm0000644000076400007640000004474112150074156021057 0ustar shlomifshlomif############################################################################# # Layout directed graphs on a flat plane. Part of Graph::Easy. # # Code to repair spliced layouts (after group cells have been inserted). # ############################################################################# package Graph::Easy::Layout::Repair; $VERSION = '0.08'; ############################################################################# ############################################################################# # for layouts with groups: package Graph::Easy; use strict; use Graph::Easy::Util qw(ord_values); sub _edges_into_groups { my $self = shift; # Put all edges between two nodes with the same group in the group as well for my $edge (ord_values $self->{edges}) { my $gf = $edge->{from}->group(); my $gt = $edge->{to}->group(); $gf->_add_edge($edge) if defined $gf && defined $gt && $gf == $gt; } $self; } sub _repair_nodes { # Splicing the rows/columns to add filler cells will have torn holes into # multi-edges nodes, so we insert additional filler cells. my ($self) = @_; my $cells = $self->{cells}; # Make multi-celled nodes occupy the proper double space due to splicing # in group cell has doubled the layout in each direction: for my $n ($self->nodes()) { # 1 => 1, 2 => 3, 3 => 5, 4 => 7 etc $n->{cx} = $n->{cx} * 2 - 1; $n->{cy} = $n->{cy} * 2 - 1; } # We might get away with not inserting filler cells if we just mark the # cells as used (e.g. use only one global filler cell) since filler cells # aren't actually rendered, anyway. for my $cell (ord_values $cells) { next unless $cell->isa('Graph::Easy::Node::Cell'); # we have "[ empty ] [ filler ]" (unless cell is on the same column as node) if ($cell->{x} > $cell->{node}->{x}) { my $x = $cell->{x} - 1; my $y = $cell->{y}; # print STDERR "# inserting filler at $x,$y for $cell->{node}->{name}\n"; $cells->{"$x,$y"} = Graph::Easy::Node::Cell->new(node => $cell->{node}, x => $x, y => $y ); } # we have " [ empty ] " # " [ filler ] " (unless cell is on the same row as node) if ($cell->{y} > $cell->{node}->{y}) { my $x = $cell->{x}; my $y = $cell->{y} - 1; # print STDERR "# inserting filler at $x,$y for $cell->{node}->{name}\n"; $cells->{"$x,$y"} = Graph::Easy::Node::Cell->new(node => $cell->{node}, x => $x, y => $y ); } } } sub _repair_cell { my ($self, $type, $edge, $x, $y, $after, $before) = @_; # already repaired? return if exists $self->{cells}->{"$x,$y"}; # print STDERR "# Insert edge cell at $x,$y (type $type) for edge $edge->{from}->{name} --> $edge->{to}->{name}\n"; $self->{cells}->{"$x,$y"} = Graph::Easy::Edge::Cell->new( type => $type, edge => $edge, x => $x, y => $y, before => $before, after => $after ); } sub _splice_edges { # Splicing the rows/columns to add filler cells might have torn holes into # edges, so we splice these together again. my ($self) = @_; my $cells = $self->{cells}; print STDERR "# Reparing spliced layout\n" if $self->{debug}; # Edge end/start points inside groups are not handled here, but in # _repair_group_edge() # go over the old layout, because the new cells were inserted into odd # rows/columns and we do not care for these: for my $cell (sort { $a->{x} <=> $b->{x} || $a->{y} <=> $b->{y} } values %$cells) { next unless $cell->isa('Graph::Easy::Edge::Cell'); my $edge = $cell->{edge}; ######################################################################### # check for "[ JOINT ] [ empty ] [ edge ]" my $x = $cell->{x} + 2; my $y = $cell->{y}; my $type = $cell->{type} & EDGE_TYPE_MASK; # left is a joint and right exists if ( ($type == EDGE_S_E_W || $type == EDGE_N_E_W || $type == EDGE_E_N_S) && exists $cells->{"$x,$y"}) { my $right = $cells->{"$x,$y"}; # print STDERR "# at $x,$y\n"; # |-> [ empty ] [ node ] if ($right->isa('Graph::Easy::Edge::Cell')) { # when the left one is a joint, the right one must be an edge $self->error("Found non-edge piece ($right->{type} $right) right to a joint ($type)") unless $right->isa('Graph::Easy::Edge::Cell'); # print STDERR "splicing in HOR piece to the right of joint at $x, $y ($edge $right $right->{edge})\n"; # insert the new piece before the first part of the edge after the joint $self->_repair_cell(EDGE_HOR(), $right->{edge},$cell->{x}+1,$y,0) if $edge != $right->{edge}; } } ######################################################################### # check for "[ edge ] [ empty ] [ joint ]" $x = $cell->{x} - 2; $y = $cell->{y}; # right is a joint and left exists if ( ($type == EDGE_S_E_W || $type == EDGE_N_E_W || $type == EDGE_W_N_S) && exists $cells->{"$x,$y"}) { my $left = $cells->{"$x,$y"}; # [ node ] [ empty ] [ <-| ] if (!$left->isa('Graph::Easy::Node')) { # when the left one is a joint, the right one must be an edge $self->error('Found non-edge piece right to a joint') unless $left->isa('Graph::Easy::Edge::Cell'); # insert the new piece before the joint $self->_repair_cell(EDGE_HOR(), $edge, $cell->{x}+1,$y,0) # $left,$cell) if $edge != $left->{edge}; } } ######################################################################### # check for " [ joint ] # [ empty ] # [ edge ]" $x = $cell->{x}; $y = $cell->{y} + 2; # top is a joint and down exists if ( ($type == EDGE_S_E_W || $type == EDGE_E_N_S || $type == EDGE_W_N_S) && exists $cells->{"$x,$y"}) { my $bottom = $cells->{"$x,$y"}; # when top is a joint, the bottom one must be an edge $self->error('Found non-edge piece below a joint') unless $bottom->isa('Graph::Easy::Edge::Cell'); # print STDERR "splicing in VER piece below joint at $x, $y\n"; # XXX TODO # insert the new piece after the joint $self->_repair_cell(EDGE_VER(), $bottom->{edge},$x,$cell->{y}+1,0) if $edge != $bottom->{edge}; } ######################################################################### # check for "[ --- ] [ empty ] [ ---> ]" $x = $cell->{x} + 2; $y = $cell->{y}; if (exists $cells->{"$x,$y"}) { my $right = $cells->{"$x,$y"}; $self->_repair_cell(EDGE_HOR(), $edge, $cell->{x}+1,$y,$cell,$right) if $right->isa('Graph::Easy::Edge::Cell') && defined $right->{edge} && defined $right->{type} && # check that both cells belong to the same edge ( $edge == $right->{edge} || # or the right part is a cross $right->{type} == EDGE_CROSS || # or the left part is a cross $cell->{type} == EDGE_CROSS ); } ######################################################################### # check for [ | ] # [ empty ] # [ | ] $x = $cell->{x}; $y = $cell->{y}+2; if (exists $cells->{"$x,$y"}) { my $below = $cells->{"$x,$y"}; $self->_repair_cell(EDGE_VER(),$edge,$x,$cell->{y}+1,$cell,$below) if $below->isa('Graph::Easy::Edge::Cell') && # check that both cells belong to the same edge ( $edge == $below->{edge} || # or the lower part is a cross $below->{type} == EDGE_CROSS || # or the upper part is a cross $cell->{type} == EDGE_CROSS ); } } # end for all cells $self; } sub _new_edge_cell { # create a new edge cell to be spliced into the layout for repairs my ($self, $cells, $group, $edge, $x, $y, $after, $type) = @_; $type += EDGE_SHORT_CELL() if defined $group; my $e_cell = Graph::Easy::Edge::Cell->new( type => $type, edge => $edge, x => $x, y => $y, after => $after); $group->_del_cell($e_cell) if defined $group; $cells->{"$x,$y"} = $e_cell; } sub _check_edge_cell { # check a start/end edge cell and if nec. repair it my ($self, $cell, $x, $y, $flag, $type, $match, $check, $where) = @_; my $edge = $cell->{edge}; if (grep { exists $_->{cell_class} && $_->{cell_class} =~ $match } ord_values ($check)) { $cell->{type} &= ~ $flag; # delete the flag $self->_new_edge_cell( $self->{cells}, $edge->{group}, $edge, $x, $y, $where, $type + $flag); } } sub _repair_group_edge { # repair an edges inside a group my ($self, $cell, $rows, $cols, $group) = @_; my $cells = $self->{cells}; my ($x,$y,$doit); my $type = $cell->{type}; ######################################################################### # check for " [ empty ] [ |---> ]" $x = $cell->{x} - 1; $y = $cell->{y}; $self->_check_edge_cell($cell, $x, $y, EDGE_START_W, EDGE_HOR, qr/g[rl]/, $cols->{$x}, 0) if (($type & EDGE_START_MASK) == EDGE_START_W); ######################################################################### # check for " [ <--- ] [ empty ]" $x = $cell->{x} + 1; $self->_check_edge_cell($cell, $x, $y, EDGE_START_E, EDGE_HOR, qr/g[rl]/, $cols->{$x}, 0) if (($type & EDGE_START_MASK) == EDGE_START_E); ######################################################################### # check for " [ --> ] [ empty ]" $x = $cell->{x} + 1; $self->_check_edge_cell($cell, $x, $y, EDGE_END_E, EDGE_HOR, qr/g[rl]/, $cols->{$x}, -1) if (($type & EDGE_END_MASK) == EDGE_END_E); # $self->_check_edge_cell($cell, $x, $y, EDGE_END_E, EDGE_E_N_S, qr/g[rl]/, $cols->{$x}, -1) # if (($type & EDGE_END_MASK) == EDGE_END_E); ######################################################################### # check for " [ empty ] [ <-- ]" $x = $cell->{x} - 1; $self->_check_edge_cell($cell, $x, $y, EDGE_END_W, EDGE_HOR, qr/g[rl]/, $cols->{$x}, -1) if (($type & EDGE_END_MASK) == EDGE_END_W); ######################################################################### ######################################################################### # vertical cases ######################################################################### # check for [empty] # [ | ] $x = $cell->{x}; $y = $cell->{y} - 1; $self->_check_edge_cell($cell, $x, $y, EDGE_START_N, EDGE_VER, qr/g[tb]/, $rows->{$y}, 0) if (($type & EDGE_START_MASK) == EDGE_START_N); ######################################################################### # check for [ |] # [ empty ] $y = $cell->{y} + 1; $self->_check_edge_cell($cell, $x, $y, EDGE_START_S, EDGE_VER, qr/g[tb]/, $rows->{$y}, 0) if (($type & EDGE_START_MASK) == EDGE_START_S); ######################################################################### # check for [ v ] # [empty] $y = $cell->{y} + 1; $self->_check_edge_cell($cell, $x, $y, EDGE_END_S, EDGE_VER, qr/g[tb]/, $rows->{$y}, -1) if (($type & EDGE_END_MASK) == EDGE_END_S); ######################################################################### # check for [ empty ] # [ ^ ] $y = $cell->{y} - 1; $self->_check_edge_cell($cell, $x, $y, EDGE_END_N, EDGE_VER, qr/g[tb]/, $rows->{$y}, -1) if (($type & EDGE_END_MASK) == EDGE_END_N); } sub _repair_edge { # repair an edge outside a group my ($self, $cell, $rows, $cols) = @_; my $cells = $self->{cells}; ######################################################################### # check for [ |\n|\nv ] # [empty] ... [non-empty] # [node] my $x = $cell->{x}; my $y = $cell->{y} + 1; my $below = $cells->{"$x,$y"}; # must be empty if (!ref($below) && (($cell->{type} & EDGE_END_MASK) == EDGE_END_S)) { if (grep { exists $_->{cell_class} && $_->{cell_class} =~ /g[tb]/ } ord_values $rows->{$y}) { # delete the start flag $cell->{type} &= ~ EDGE_END_S; $self->_new_edge_cell($cells, undef, $cell->{edge}, $x, $y, -1, EDGE_VER() + EDGE_END_S() ); } } # XXX TODO: do the other ends (END_N, END_W, END_E), too } sub _repair_edges { # fix edge end/start cells to be closer to the node cell they point at my ($self, $rows, $cols) = @_; my $cells = $self->{cells}; # go over all existing cells for my $cell (sort { $a->{x} <=> $b->{x} || $a->{y} <=> $b->{y} } values %$cells) { next unless $cell->isa('Graph::Easy::Edge::Cell'); # skip odd positions next unless ($cell->{x} & 1) == 0 && ($cell->{y} & 1) == 0; my $group = $cell->group(); $self->_repair_edge($cell,$rows,$cols) unless $group; $self->_repair_group_edge($cell,$rows,$cols,$group) if $group; } # end for all cells } sub _fill_group_cells { # after doing a layout(), we need to add the group to each cell based on # what group the nearest node is in. my ($self, $cells_layout) = @_; print STDERR "\n# Padding with fill cells, have ", scalar $self->groups(), " groups.\n" if $self->{debug}; # take a shortcut if we do not have groups return $self if $self->groups == 0; $self->{padding_cells} = 1; # set to true # We need to insert "filler" cells around each node/edge/cell: # To "insert" the filler cells, we simple multiply each X and Y by 2, this # is O(N) where N is the number of actually existing cells. Otherwise we # would have to create the full table-layout, and then insert rows/columns. my $cells = {}; for my $key (sort keys %$cells_layout) { my ($x,$y) = split /,/, $key; my $cell = $cells_layout->{$key}; $x *= 2; $y *= 2; $cell->{x} = $x; $cell->{y} = $y; $cells->{"$x,$y"} = $cell; } $self->{cells} = $cells; # override with new cell layout $self->_splice_edges(); # repair edges $self->_repair_nodes(); # repair multi-celled nodes my $c = 'Graph::Easy::Group::Cell'; for my $cell (ord_values $self->{cells}) { # DO NOT MODIFY $cell IN THE LOOP BODY! my ($x,$y) = ($cell->{x},$cell->{y}); # find the primary node for node cells, for group check my $group = $cell->group(); # not part of group, so no group-cells nec. next unless $group; # now insert up to 8 filler cells around this cell my $ofs = [ -1, 0, 0, -1, +1, 0, +1, 0, 0, +1, 0, +1, -1, 0, -1, 0, ]; while (@$ofs > 0) { $x += shift @$ofs; $y += shift @$ofs; $cells->{"$x,$y"} = $c->new ( graph => $self, group => $group, x => $x, y => $y ) unless exists $cells->{"$x,$y"}; } } # Nodes positioned two cols/rows apart (f.i. y == 0 and y == 2) will be # three cells apart (y == 0 and y == 4) after the splicing, the step above # will not be able to close that hole - it will create fillers at y == 1 and # y == 3. So we close these holes now with an extra step. for my $cell (ord_values ( $self->{cells} )) { # only for filler cells next unless $cell->isa('Graph::Easy::Group::Cell'); my ($sx,$sy) = ($cell->{x},$cell->{y}); my $group = $cell->{group}; my $x = $sx; my $y2 = $sy + 2; my $y = $sy + 1; # look for: # [ group ] # [ empty ] # [ group ] if (exists $cells->{"$x,$y2"} && !exists $cells->{"$x,$y"}) { my $down = $cells->{"$x,$y2"}; if ($down->isa('Graph::Easy::Group::Cell') && $down->{group} == $group) { $cells->{"$x,$y"} = $c->new ( graph => $self, group => $group, x => $x, y => $y ); } } $x = $sx+1; my $x2 = $sx + 2; $y = $sy; # look for: # [ group ] [ empty ] [ group ] if (exists $cells->{"$x2,$y"} && !exists $cells->{"$x,$y"}) { my $right = $cells->{"$x2,$y"}; if ($right->isa('Graph::Easy::Group::Cell') && $right->{group} == $group) { $cells->{"$x,$y"} = $c->new ( graph => $self, group => $group, x => $x, y => $y ); } } } # XXX TODO # we should "grow" the group area to close holes for my $group (ord_values ( $self->{groups} )) { $group->_set_cell_types($cells); } # create a mapping for each row/column so that we can repair edge starts/ends my $rows = {}; my $cols = {}; for my $cell (ord_values ($cells)) { $rows->{$cell->{y}}->{$cell->{x}} = $cell; $cols->{$cell->{x}}->{$cell->{y}} = $cell; } $self->_repair_edges($rows,$cols); # insert short edge cells on group # border rows/columns # for all groups, set the cell carrying the label (top-left-most cell) for my $group (ord_values ( $self->{groups} )) { $group->_find_label_cell(); } # DEBUG: # for my $cell (ord_values $cells) # { # $cell->_correct_size(); # } # # my $y = 0; # for my $cell (sort { $a->{y} <=> $b->{y} || $a->{x} <=> $b->{x} } values %$cells) # { # print STDERR "\n" if $y != $cell->{y}; # print STDERR "$cell->{x},$cell->{y}, $cell->{w},$cell->{h}, ", $cell->{group}->{name} || 'none', "\t"; # $y = $cell->{y}; # } # print STDERR "\n"; $self; } 1; __END__ =head1 NAME Graph::Easy::Layout::Repair - Repair spliced layout with group cells =head1 SYNOPSIS use Graph::Easy; my $graph = Graph::Easy->new(); my $bonn = Graph::Easy::Node->new( name => 'Bonn', ); my $berlin = Graph::Easy::Node->new( name => 'Berlin', ); $graph->add_edge ($bonn, $berlin); $graph->layout(); print $graph->as_ascii( ); # prints: # +------+ +--------+ # | Bonn | --> | Berlin | # +------+ +--------+ =head1 DESCRIPTION C contains code that can splice in group cells into a layout, as well as repair the layout after that step. It is part of L and used automatically. =head1 METHODS C injects the following methods into the C namespace: =head2 _edges_into_groups() Put the edges into the appropriate group and class. =head2 _assign_ranks() $graph->_assign_ranks(); =head2 _repair_nodes() Splicing the rows/columns to add filler cells will have torn holes into multi-edges nodes, so we insert additional filler cells to repair this. =head2 _splice_edges() Splicing the rows/columns to add filler cells might have torn holes into multi-celled edges, so we splice these together again. =head2 _repair_edges() Splicing the rows/columns to add filler cells might have put "holes" between an edge start/end and the node cell it points to. This routine fixes this problem by extending the edge by one cell if necessary. =head2 _fill_group_cells() After doing a C, we need to add the group to each cell based on what group the nearest node is in. This routine will also find the label cell for each group, and repair edge/node damage done by the splicing. =head1 EXPORT Exports nothing. =head1 SEE ALSO L. =head1 AUTHOR Copyright (C) 2004 - 2007 by Tels L See the LICENSE file for information. =cut Graph-Easy-0.73/lib/Graph/Easy/Layout/Scout.pm0000644000076400007640000013501512150076766020736 0ustar shlomifshlomif############################################################################# # Find paths from node to node in a Manhattan-style grid via A*. # # (c) by Tels - part of Graph::Easy ############################################################################# package Graph::Easy::Layout::Scout; $VERSION = '0.25'; ############################################################################# ############################################################################# package Graph::Easy; use strict; use Graph::Easy::Node::Cell; use Graph::Easy::Edge::Cell qw/ EDGE_SHORT_E EDGE_SHORT_W EDGE_SHORT_N EDGE_SHORT_S EDGE_SHORT_BD_EW EDGE_SHORT_BD_NS EDGE_SHORT_UN_EW EDGE_SHORT_UN_NS EDGE_START_E EDGE_START_W EDGE_START_N EDGE_START_S EDGE_END_E EDGE_END_W EDGE_END_N EDGE_END_S EDGE_N_E EDGE_N_W EDGE_S_E EDGE_S_W EDGE_N_W_S EDGE_S_W_N EDGE_E_S_W EDGE_W_S_E EDGE_LOOP_NORTH EDGE_LOOP_SOUTH EDGE_LOOP_WEST EDGE_LOOP_EAST EDGE_HOR EDGE_VER EDGE_HOLE EDGE_S_E_W EDGE_N_E_W EDGE_E_N_S EDGE_W_N_S EDGE_LABEL_CELL EDGE_TYPE_MASK EDGE_ARROW_MASK EDGE_FLAG_MASK EDGE_START_MASK EDGE_END_MASK EDGE_NO_M_MASK /; ############################################################################# # mapping edge type (HOR, VER, NW etc) and dx/dy to startpoint flag my $start_points = { # [ dx == 1, dx == -1, dy == 1, dy == -1 , # dx == 1, dx == -1, dy == 1, dy == -1 ] EDGE_HOR() => [ EDGE_START_W, EDGE_START_E, 0, 0 , EDGE_END_E, EDGE_END_W, 0, 0, ], EDGE_VER() => [ 0, 0, EDGE_START_N, EDGE_START_S , 0, 0, EDGE_END_S, EDGE_END_N, ], EDGE_N_E() => [ 0, EDGE_START_E, EDGE_START_N, 0 , EDGE_END_E, 0, 0, EDGE_END_N, ], EDGE_N_W() => [ EDGE_START_W, 0, EDGE_START_N, 0 , 0, EDGE_END_W, 0, EDGE_END_N, ], EDGE_S_E() => [ 0, EDGE_START_E, 0, EDGE_START_S , EDGE_END_E, 0, EDGE_END_S, 0, ], EDGE_S_W() => [ EDGE_START_W, 0, 0, EDGE_START_S , 0, EDGE_END_W, EDGE_END_S, 0, ], }; my $start_to_end = { EDGE_START_W() => EDGE_END_W(), EDGE_START_E() => EDGE_END_E(), EDGE_START_S() => EDGE_END_S(), EDGE_START_N() => EDGE_END_N(), }; sub _end_points { # modify last field of path to be the correct endpoint; and the first field # to be the correct startpoint: my ($self, $edge, $coords, $dx, $dy) = @_; return $coords if $edge->undirected(); # there are two cases (for each dx and dy) my $i = 0; # index 0,1 my $co = 2; my $case; for my $d ($dx,$dy,$dx,$dy) { next if $d == 0; my $type = $coords->[$co] & EDGE_TYPE_MASK; $case = 0; $case = 1 if $d == -1; # modify first/last cell my $t = $start_points->{ $type }->[ $case + $i ]; # on bidirectional edges, turn START_X into END_X $t = $start_to_end->{$t} || $t if $edge->{bidirectional}; $coords->[$co] += $t; } continue { $i += 2; # index 2,3, 4,5 etc $co = -1 if $i == 4; # modify now last cell } $coords; } sub _find_path { # Try to find a path between two nodes. $options contains direction # preferences. Returns a list of cells like: # [ $x,$y,$type, $x1,$y1,$type1, ...] my ($self, $src, $dst, $edge) = @_; # one node pointing back to itself? if ($src == $dst) { my $rc = $self->_find_path_loop($src,$edge); return $rc unless scalar @$rc == 0; } # If one of the two nodes is bigger than 1 cell, use _find_path_astar(), # because it automatically handles all the possibilities: return $self->_find_path_astar($edge) if ($src->is_multicelled() || $dst->is_multicelled() || $edge->has_ports()); my ($x0, $y0) = ($src->{x}, $src->{y}); my ($x1, $y1) = ($dst->{x}, $dst->{y}); my $dx = ($x1 - $x0) <=> 0; my $dy = ($y1 - $y0) <=> 0; my $cells = $self->{cells}; my @coords; my ($x,$y) = ($x0,$y0); # starting pos ########################################################################### # below follow some shortcuts for easy things like straight paths: print STDERR "# dx,dy: $dx,$dy\n" if $self->{debug}; if ($dx == 0 || $dy == 0) { # try straight path to target: print STDERR "# $src->{x},$src->{y} => $dst->{x},$dst->{y} - trying short path\n" if $self->{debug}; # distance to node: my $dx1 = ($x1 - $x0); my $dy1 = ($y1 - $y0); ($x,$y) = ($x0+$dx,$y0+$dy); # starting pos if ((abs($dx1) == 2) || (abs($dy1) == 2)) { if (!exists ($cells->{"$x,$y"})) { # a single step for this edge: my $type = EDGE_LABEL_CELL; # short path if ($edge->bidirectional()) { $type += EDGE_SHORT_BD_EW if $dy == 0; $type += EDGE_SHORT_BD_NS if $dx == 0; } elsif ($edge->undirected()) { $type += EDGE_SHORT_UN_EW if $dy == 0; $type += EDGE_SHORT_UN_NS if $dx == 0; } else { $type += EDGE_SHORT_E if ($dx == 1 && $dy == 0); $type += EDGE_SHORT_S if ($dx == 0 && $dy == 1); $type += EDGE_SHORT_W if ($dx == -1 && $dy == 0); $type += EDGE_SHORT_N if ($dx == 0 && $dy == -1); } # if one of the end points of the edge is of shape 'edge' # remove end/start flag if (($edge->{to}->attribute('shape') ||'') eq 'edge') { # we only need to remove one start point, namely the one at the "end" if ($dx > 0) { $type &= ~EDGE_START_E; } elsif ($dx < 0) { $type &= ~EDGE_START_W; } } if (($edge->{from}->attribute('shape') ||'') eq 'edge') { $type &= ~EDGE_START_MASK; } return [ $x, $y, $type ]; # return a short EDGE } } my $type = EDGE_HOR; $type = EDGE_VER if $dx == 0; # - or | my $done = 0; my $label_done = 0; while (3 < 5) # endless loop { # Since we do not handle crossings here, A* will be tried if we hit an # edge in this test. $done = 1, last if exists $cells->{"$x,$y"}; # cell already full # the first cell gets the label my $t = $type; $t += EDGE_LABEL_CELL if $label_done++ == 0; push @coords, $x, $y, $t; # good one, is free $x += $dx; $y += $dy; # next field last if ($x == $x1) && ($y == $y1); } if ($done == 0) { print STDERR "# success for ", scalar @coords / 3, " steps in path\n" if $self->{debug}; # return all fields of path return $self->_end_points($edge, \@coords, $dx, $dy); } } # end else straight path try ########################################################################### # Try paths with one bend: # ($dx != 0 && $dy != 0) => path with one bend # XXX TODO: # This could be handled by A*, too, but it would be probably a bit slower. else { # straight path not possible, since x0 != x1 AND y0 != y1 # " |" "| " # try first "--+" (aka hor => ver), then "+---" (aka ver => hor) my $done = 0; print STDERR "# bend path from $x,$y\n" if $self->{debug}; # try hor => ver my $type = EDGE_HOR; my $label = 0; # attach label? $label = 1 if ref($edge) && ($edge->label()||'') eq ''; # no label? $x += $dx; while ($x != $x1) { $done++, last if exists $cells->{"$x,$y"}; # cell already full print STDERR "# at $x,$y\n" if $self->{debug}; my $t = $type; $t += EDGE_LABEL_CELL if $label++ == 0; push @coords, $x, $y, $t; # good one, is free $x += $dx; # next field }; # check the bend itself $done++ if exists $cells->{"$x,$y"}; # cell already full if ($done == 0) { my $type_bend = _astar_edge_type ($x-$dx,$y, $x,$y, $x,$y+$dy); push @coords, $x, $y, $type_bend; # put in bend print STDERR "# at $x,$y\n" if $self->{debug}; $y += $dy; $type = EDGE_VER; while ($y != $y1) { $done++, last if exists $cells->{"$x,$y"}; # cell already full print STDERR "# at $x,$y\n" if $self->{debug}; push @coords, $x, $y, $type; # good one, is free $y += $dy; } } if ($done != 0) { $done = 0; # try ver => hor print STDERR "# hm, now trying first vertical, then horizontal\n" if $self->{debug}; $type = EDGE_VER; @coords = (); # drop old version ($x,$y) = ($x0, $y0 + $dy); # starting pos while ($y != $y1) { $done++, last if exists $cells->{"$x,$y"}; # cell already full print STDERR "# at $x,$y\n" if $self->{debug}; push @coords, $x, $y, $type; # good one, is free $y += $dy; # next field }; # check the bend itself $done++ if exists $cells->{"$x,$y"}; # cell already full if ($done == 0) { my $type_bend = _astar_edge_type ($x,$y-$dy, $x,$y, $x+$dx,$y); push @coords, $x, $y, $type_bend; # put in bend print STDERR "# at $x,$y\n" if $self->{debug}; $x += $dx; my $label = 0; # attach label? $label = 1 if $edge->label() eq ''; # no label? $type = EDGE_HOR; while ($x != $x1) { $done++, last if exists $cells->{"$x,$y"}; # cell already full print STDERR "# at $x,$y\n" if $self->{debug}; my $t = $type; $t += EDGE_LABEL_CELL if $label++ == 0; push @coords, $x, $y, $t; # good one, is free $x += $dx; } } } if ($done == 0) { print STDERR "# success for ", scalar @coords / 3, " steps in path\n" if $self->{debug}; # return all fields of path return $self->_end_points($edge, \@coords, $dx, $dy); } print STDERR "# no success\n" if $self->{debug}; } # end path with $dx and $dy $self->_find_path_astar($edge); # try generic approach as last hope } sub _find_path_loop { # find a path from one node back to itself my ($self, $src, $edge) = @_; print STDERR "# Finding looping path from $src->{name} to $src->{name}\n" if $self->{debug}; my ($n, $cells, $d, $type, $loose) = @_; # get a list of all places my @places = $src->_near_places( $self->{cells}, 1, [ EDGE_LOOP_EAST, EDGE_LOOP_SOUTH, EDGE_LOOP_WEST, EDGE_LOOP_NORTH, ], 0, 90); my $flow = $src->flow(); # We cannot use _shuffle_dir() here, because self-loops # are tried in a different order: # the default (east) my $index = [ EDGE_LOOP_NORTH, EDGE_LOOP_SOUTH, EDGE_LOOP_WEST, EDGE_LOOP_EAST, ]; # west $index = [ EDGE_LOOP_SOUTH, EDGE_LOOP_NORTH, EDGE_LOOP_EAST, EDGE_LOOP_WEST, ] if $flow == 270; # north $index = [ EDGE_LOOP_WEST, EDGE_LOOP_EAST, EDGE_LOOP_SOUTH, EDGE_LOOP_NORTH, ] if $flow == 0; # south $index = [ EDGE_LOOP_EAST, EDGE_LOOP_WEST, EDGE_LOOP_NORTH, EDGE_LOOP_SOUTH, ] if $flow == 180; for my $this_try (@$index) { my $idx = 0; while ($idx < @places) { print STDERR "# Trying $places[$idx+0],$places[$idx+1]\n" if $self->{debug}; next unless $places[$idx+2] == $this_try; # build a path from the returned piece my @rc = ($places[$idx], $places[$idx+1], $places[$idx+2]); print STDERR "# Trying $rc[0],$rc[1]\n" if $self->{debug}; next unless $self->_path_is_clear(\@rc); print STDERR "# Found looping path\n" if $self->{debug}; return \@rc; } continue { $idx += 3; } } []; # no path found } ############################################################################# ############################################################################# # This package represents a simple/cheap/fast heap: package Graph::Easy::Heap; require Graph::Easy::Base; our @ISA = qw/Graph::Easy::Base/; use strict; sub _init { my ($self,$args) = @_; $self->{_heap} = [ ]; $self; } sub add { # add one element to the heap my ($self,$elem) = @_; my $heap = $self->{_heap}; # heap empty? if (@$heap == 0) { push @$heap, $elem; } # smaller than first elem? elsif ($elem->[0] < $heap->[0]->[0]) { #print STDERR "# $elem->[0] is smaller then first elem $heap->[0]->[0] (with ", scalar @$heap," elems on heap)\n"; unshift @$heap, $elem; } # bigger than or equal to last elem? elsif ($elem->[0] > $heap->[-1]->[0]) { #print STDERR "# $elem->[0] is bigger then last elem $heap->[-1]->[0] (with ", scalar @$heap," elems on heap)\n"; push @$heap, $elem; } else { # insert the elem at the right position # if we have less than X elements, use linear search my $el = $elem->[0]; if (scalar @$heap < 10) { my $i = 0; for my $e (@$heap) { if ($e->[0] > $el) { splice (@$heap, $i, 0, $elem); # insert $elem return undef; } $i++; } # else, append at the end push @$heap, $elem; } else { # use binary search my $l = 0; my $r = scalar @$heap; while (($r - $l) > 2) { my $m = int((($r - $l) / 2) + $l); # print "l=$l r=$r m=$m el=$el heap=$heap->[$m]->[0]\n"; if ($heap->[$m]->[0] <= $el) { $l = $m; } else { $r = $m; } } while ($l < @$heap) { if ($heap->[$l]->[0] > $el) { splice (@$heap, $l, 0, $elem); # insert $elem return undef; } $l++; } # else, append at the end push @$heap, $elem; } } undef; } sub elements { scalar @{$_[0]->{_heap}}; } sub extract_top { # remove and return the top elemt shift @{$_[0]->{_heap}}; } sub delete { # Find an element by $x,$y and delete it my ($self, $x, $y) = @_; my $heap = $self->{_heap}; my $i = 0; for my $e (@$heap) { if ($e->[1] == $x && $e->[2] == $y) { splice (@$heap, $i, 1); return; } $i++; } $self; } sub sort_sub { my ($self) = shift; $self->{_sort} = shift; } ############################################################################# ############################################################################# package Graph::Easy; # Generic pathfinding via the A* algorithm: # See http://bloodgate.com/perl/graph/astar.html for some background. sub _astar_modifier { # calculate the cost for the path at cell x1,y1 my ($x1,$y1,$x,$y,$px,$py, $cells) = @_; my $add = 1; if (defined $x1) { my $xy = "$x1,$y1"; # add a harsh penalty for crossing an edge, meaning we can travel many # fields to go around. $add += 30 if ref($cells->{$xy}) && $cells->{$xy}->isa('Graph::Easy::Edge'); } if (defined $px) { # see whether the new position $x1,$y1 is a continuation from $px,$py => $x,$y # e.g. if from we go down from $px,$py to $x,$y, then anything else then $x,$y+1 will # get a penalty my $dx1 = ($px-$x) <=> 0; my $dy1 = ($py-$y) <=> 0; my $dx2 = ($x-$x1) <=> 0; my $dy2 = ($y-$y1) <=> 0; $add += 6 unless $dx1 == $dx2 || $dy1 == $dy2; } $add; } sub _astar_distance { # calculate the manhattan distance between x1,y1 and x2,y2 # my ($x1,$y1,$x2,$y2) = @_; my $dx = abs($_[2] - $_[0]); my $dy = abs($_[3] - $_[1]); # plus 1 because we need to go around one corner if $dx != 0 && $dx != 0 $dx++ if $dx != 0 && $dy != 0; $dx + $dy; } my $edge_type = { '0,1,-1,0' => EDGE_N_W, '0,1,0,1' => EDGE_VER, '0,1,1,0' => EDGE_N_E, '-1,0,0,-1' => EDGE_N_E, '-1,0,-1,0' => EDGE_HOR, '-1,0,0,1' => EDGE_S_E, '0,-1,-1,0' => EDGE_S_W, '0,-1,0,-1' => EDGE_VER, '0,-1,1,0' => EDGE_S_E, '1,0,0,-1' => EDGE_N_W, '1,0,1,0' => EDGE_HOR, '1,0,0,1' => EDGE_S_W, # loops (left-right-left etc) '0,-1,0,1' => EDGE_N_W_S, '0,1,0,-1' => EDGE_S_W_N, '1,0,-1,0' => EDGE_E_S_W, '-1,0,1,0' => EDGE_W_S_E, }; sub _astar_edge_type { # from three consecutive positions calculate the edge type (VER, HOR, N_W etc) my ($x,$y, $x1,$y1, $x2, $y2) = @_; my $dx1 = ($x1 - $x) <=> 0; my $dy1 = ($y1 - $y) <=> 0; my $dx2 = ($x2 - $x1) <=> 0; my $dy2 = ($y2 - $y1) <=> 0; # in some cases we get (0,-1,0,0), so set the missing parts ($dx2,$dy2) = ($dx1,$dy1) if $dx2 == 0 && $dy2 == 0; # can this case happen? ($dx1,$dy1) = ($dx2,$dy2) if $dx1 == 0 && $dy1 == 0; # return correct type depending on differences $edge_type->{"$dx1,$dy1,$dx2,$dy2"} || EDGE_HOR; } sub _astar_near_nodes { # return possible next nodes from $nx,$ny my ($self, $nx, $ny, $cells, $closed, $min_x, $min_y, $max_x, $max_y) = @_; my @places = (); my @tries = ( # ordered E,S,W,N: $nx + 1, $ny, # right $nx, $ny + 1, # down $nx - 1, $ny, # left $nx, $ny - 1, # up ); # on crossings, only allow one direction (NS or EW) my $type = EDGE_CROSS; # including flags, because only flagless edges may be crossed $type = $cells->{"$nx,$ny"}->{type} if exists $cells->{"$nx,$ny"}; if ($type == EDGE_HOR) { @tries = ( $nx, $ny + 1, # down $nx, $ny - 1, # up ); } elsif ($type == EDGE_VER) { @tries = ( $nx + 1, $ny, # right $nx - 1, $ny, # left ); } # This loop does not check whether the position is already open or not, # the caller will later check if the already-open position needs to be # replaced by one with a lower cost. my $i = 0; while ($i < @tries) { my ($x,$y) = ($tries[$i], $tries[$i+1]); print STDERR "# $min_x,$min_y => $max_x,$max_y\n" if $self->{debug} > 2; # drop cells outside our working space: next if $x < $min_x || $x > $max_x || $y < $min_y || $y > $max_y; my $p = "$x,$y"; print STDERR "# examining pos $p\n" if $self->{debug} > 2; next if exists $closed->{$p}; if (exists $cells->{$p} && ref($cells->{$p}) && $cells->{$p}->isa('Graph::Easy::Edge')) { # If the existing cell is an VER/HOR edge, then we may cross it my $type = $cells->{$p}->{type}; # including flags, because only flagless edges # may be crossed push @places, $x, $y if ($type == EDGE_HOR) || ($type == EDGE_VER); next; } next if exists $cells->{$p}; # uncrossable cell push @places, $x, $y; } continue { $i += 2; } @places; } sub _astar_boundaries { # Calculate boundaries for area that A* should not leave. my $self = shift; my $cache = $self->{cache}; return ( $cache->{min_x}-1, $cache->{min_y}-1, $cache->{max_x}+1, $cache->{max_y}+1 ) if defined $cache->{min_x}; my ($min_x, $min_y, $max_x, $max_y); my $cells = $self->{cells}; $min_x = 10000000; $min_y = 10000000; $max_x = -10000000; $max_y = -10000000; for my $c (sort keys %$cells) { my ($x,$y) = split /,/, $c; $min_x = $x if $x < $min_x; $min_y = $y if $y < $min_y; $max_x = $x if $x > $max_x; $max_y = $y if $y > $max_y; } print STDERR "# A* working space boundaries: $min_x, $min_y, $max_x, $max_y\n" if $self->{debug}; ( $cache->{min_x}, $cache->{min_y}, $cache->{max_x}, $cache->{max_y} ) = ($min_x, $min_y, $max_x, $max_y); # make the area one bigger in each direction $min_x --; $min_y --; $max_x ++; $max_y ++; ($min_x, $min_y, $max_x, $max_y); } # on edge pieces, select start fields (left/right of a VER, above/below of a HOR etc) # contains also for each starting position the joint-type my $next_fields = { EDGE_VER() => [ -1,0, EDGE_W_N_S, +1,0, EDGE_E_N_S ], EDGE_HOR() => [ 0,-1, EDGE_N_E_W, 0,+1, EDGE_S_E_W ], EDGE_N_E() => [ 0,+1, EDGE_E_N_S, -1,0, EDGE_N_E_W ], # |_ EDGE_N_W() => [ 0,+1, EDGE_W_N_S, +1,0, EDGE_N_E_W ], # _| EDGE_S_E() => [ 0,-1, EDGE_E_N_S, -1,0, EDGE_S_E_W ], EDGE_S_W() => [ 0,-1, EDGE_W_N_S, +1,0, EDGE_S_E_W ], }; # on edge pieces, select end fields (left/right of a VER, above/below of a HOR etc) # contains also for each end position the joint-type my $prev_fields = { EDGE_VER() => [ -1,0, EDGE_W_N_S, +1,0, EDGE_E_N_S ], EDGE_HOR() => [ 0,-1, EDGE_N_E_W, 0,+1, EDGE_S_E_W ], EDGE_N_E() => [ 0,+1, EDGE_E_N_S, -1,0, EDGE_N_E_W ], # |_ EDGE_N_W() => [ 0,+1, EDGE_W_N_S, +1,0, EDGE_N_E_W ], # _| EDGE_S_E() => [ 0,-1, EDGE_E_N_S, -1,0, EDGE_S_E_W ], EDGE_S_W() => [ 0,-1, EDGE_W_N_S, +1,0, EDGE_S_E_W ], }; use Graph::Easy::Util qw(ord_values); sub _get_joints { # from a list of shared, already placed edges, get possible start/end fields my ($self, $shared, $mask, $types, $cells, $next_fields) = @_; # XXX TODO: do not do this for edges with no free places for joints # take each cell from all edges shared, already placed edges as start-point for my $e (@$shared) { for my $c (@{$e->{cells}}) { my $type = $c->{type} & EDGE_TYPE_MASK; next unless exists $next_fields->{ $type }; # don't consider end/start (depending on $mask) cells # do not join EDGE_HOR or EDGE_VER, but join corner pieces next if ( ($type == EDGE_HOR()) || ($type == EDGE_VER()) ) && ($c->{type} & $mask); my $fields = $next_fields->{$type}; my ($px,$py) = ($c->{x},$c->{y}); my $i = 0; while ($i < @$fields) { my ($sx,$sy, $jt) = ($fields->[$i], $fields->[$i+1], $fields->[$i+2]); $sx += $px; $sy += $py; $i += 3; my $sxsy = "$sx,$sy"; # don't add the field twice next if exists $cells->{$sxsy}; $cells->{$sxsy} = [ $sx, $sy, undef, $px, $py ]; # keep eventually set start/end points on the original cell $types->{$sxsy} = $jt + ($c->{type} & EDGE_FLAG_MASK); } } } my @R; # convert hash to array for my $s (ord_values ( $cells )) { push @R, @$s; } @R; } sub _join_edge { # Find out whether an edge sharing an ending point with the source edge # runs alongside the source node, if so, convert it to a joint: my ($self, $node, $edge, $shared, $end) = @_; # we check the sides B,C,D and E for HOR and VER edge pices: # --D-- # | +---+ | # E | A | B # | +---+ | # --C-- my $flags = [ EDGE_W_N_S + EDGE_START_W, EDGE_N_E_W + EDGE_START_N, EDGE_E_N_S + EDGE_START_E, EDGE_S_E_W + EDGE_START_S, ]; $flags = [ EDGE_W_N_S + EDGE_END_W, EDGE_N_E_W + EDGE_END_N, EDGE_E_N_S + EDGE_END_E, EDGE_S_E_W + EDGE_END_S, ] if $end || $edge->{bidirectional}; my $cells = $self->{cells}; my @places = $node->_near_places($cells, 1, # distance 1 $flags, 'loose'); my $i = 0; while ($i < @places) { my ($x,$y) = ($places[$i], $places[$i+1]); $i += 3; next unless exists $cells->{"$x,$y"}; # empty space? # found some cell, check that it is a EDGE_HOR or EDGE_VER my $cell = $cells->{"$x,$y"}; next unless $cell->isa('Graph::Easy::Edge::Cell'); my $cell_type = $cell->{type} & EDGE_TYPE_MASK; next unless $cell_type == EDGE_HOR || $cell_type == EDGE_VER; # the cell must belong to one of the shared edges my $e = $cell->{edge}; local $_; next unless scalar grep { $e == $_ } @$shared; # make the cell at the current pos a joint $cell->_make_joint($edge,$places[$i-1]); # The layouter will check that each edge has a cell, so add a dummy one to # $edge to make it happy: Graph::Easy::Edge::Cell->new( type => EDGE_HOLE, edge => $edge, x => $x, y => $y ); return []; # path is empty } undef; # did not find an edge cell that can be used as joint } sub _find_path_astar { # Find a path with the A* algorithm for the given edge (from node A to B) my ($self,$edge) = @_; my $cells = $self->{cells}; my $src = $edge->{from}; my $dst = $edge->{to}; print STDERR "# A* from $src->{x},$src->{y} to $dst->{x},$dst->{y}\n" if $self->{debug}; my $start_flags = [ EDGE_START_W, EDGE_START_N, EDGE_START_E, EDGE_START_S, ]; my $end_flags = [ EDGE_END_W, EDGE_END_N, EDGE_END_E, EDGE_END_S, ]; # if the target/source node is of shape "edge", remove the endpoint if ( ($edge->{to}->attribute('shape')) eq 'edge') { $end_flags = [ 0,0,0,0 ]; } if ( ($edge->{from}->attribute('shape')) eq 'edge') { $start_flags = [ 0,0,0,0 ]; } my ($s_p,@ss_p) = $edge->port('start'); my ($e_p,@ee_p) = $edge->port('end'); my (@A, @B); # Start/Stop positions my @shared_start; my @shared_end; my $joint_type = {}; my $joint_type_end = {}; my $start_cells = {}; my $end_cells = {}; ########################################################################### # end fields first (because maybe an edge runs alongside the node) # has a end point restriction @shared_end = $edge->{to}->edges_at_port('end', $e_p, $ee_p[0]) if defined $e_p && @ee_p == 1; my @shared = (); # filter out all non-placed edges (this will also filter out $edge) for my $s (@shared_end) { push @shared, $s if @{$s->{cells}} > 0; } my $per_field = 5; # for shared: x,y,undef, px,py if (@shared > 0) { # more than one edge share the same end port, and one of the others was # already placed print STDERR "# edge from '$edge->{from}->{name}' to '$edge->{to}->{name}' shares end port with ", scalar @shared, " other edge(s)\n" if $self->{debug}; # if there is one of the already-placed edges running alongside the src # node, we can just convert the field to a joint and be done my $path = $self->_join_edge($src,$edge,\@shared); return $path if $path; # already done? @B = $self->_get_joints(\@shared, EDGE_START_MASK, $joint_type_end, $end_cells, $prev_fields); } else { # potential stop positions @B = $dst->_near_places($cells, 1, $end_flags, 1); # distance = 1: slots # the edge has a port description, limiting the end places @B = $dst->_allowed_places( \@B, $dst->_allow( $e_p, @ee_p ), 3) if defined $e_p; $per_field = 3; # x,y,type } return unless scalar @B > 0; # no free slots on target node? ########################################################################### # start fields # has a starting point restriction: @shared_start = $edge->{from}->edges_at_port('start', $s_p, $ss_p[0]) if defined $s_p && @ss_p == 1; @shared = (); # filter out all non-placed edges (this will also filter out $edge) for my $s (@shared_start) { push @shared, $s if @{$s->{cells}} > 0; } if (@shared > 0) { # More than one edge share the same start port, and one of the others was # already placed, so we just run along until we catch it up with a joint: print STDERR "# edge from '$edge->{from}->{name}' to '$edge->{to}->{name}' shares start port with ", scalar @shared, " other edge(s)\n" if $self->{debug}; # if there is one of the already-placed edges running alongside the src # node, we can just convert the field to a joint and be done my $path = $self->_join_edge($dst, $edge, \@shared, 'end'); return $path if $path; # already done? @A = $self->_get_joints(\@shared, EDGE_END_MASK, $joint_type, $start_cells, $next_fields); } else { # from SRC to DST # get all the starting positions # distance = 1: slots, generate starting types, the direction is shifted # by 90° counter-clockwise my $s = $start_flags; $s = $end_flags if $edge->{bidirectional}; my @start = $src->_near_places($cells, 1, $s, 1, $src->_shift(-90) ); # the edge has a port description, limiting the start places @start = $src->_allowed_places( \@start, $src->_allow( $s_p, @ss_p ), 3) if defined $s_p; return unless @start > 0; # no free slots on start node? my $i = 0; while ($i < scalar @start) { my $sx = $start[$i]; my $sy = $start[$i+1]; my $type = $start[$i+2]; $i += 3; # compute the field inside the node from where $sx,$sy is reached: my $px = $sx; my $py = $sy; if ($sy < $src->{y} || $sy >= $src->{y} + $src->{cy}) { $py = $sy + 1 if $sy < $src->{y}; # above $py = $sy - 1 if $sy > $src->{y}; # below } else { $px = $sx + 1 if $sx < $src->{x}; # right $px = $sx - 1 if $sx > $src->{x}; # left } push @A, ($sx, $sy, $type, $px, $py); } } ########################################################################### # use A* to finally find the path: my $path = $self->_astar(\@A,\@B,$edge, $per_field); if (@$path > 0 && keys %$start_cells > 0) { # convert the edge piece of the starting edge-cell to a joint my ($x, $y) = ($path->[0],$path->[1]); my $xy = "$x,$y"; my ($sx,$sy,$t,$px,$py) = @{$start_cells->{$xy}}; my $jt = $joint_type->{"$sx,$sy"}; $cells->{"$px,$py"}->_make_joint($edge,$jt); } if (@$path > 0 && keys %$end_cells > 0) { # convert the edge piece of the starting edge-cell to a joint my ($x, $y) = ($path->[-3],$path->[-2]); my $xy = "$x,$y"; my ($sx,$sy,$t,$px,$py) = @{$end_cells->{$xy}}; my $jt = $joint_type_end->{"$sx,$sy"}; $cells->{"$px,$py"}->_make_joint($edge,$jt); } $path; } sub _astar { # The core A* algorithm, finds a path from a given list of start # positions @A to and of the given stop positions @B. my ($self, $A, $B, $edge, $per_field) = @_; my @start = @$A; my @stop = @$B; my $stop = scalar @stop; my $src = $edge->{from}; my $dst = $edge->{to}; my $cells = $self->{cells}; my $open = Graph::Easy::Heap->new(); # to find smallest elem fast my $open_by_pos = {}; # to find open nodes by pos my $closed = {}; # to find closed nodes by pos my $elem; # The boundaries of objects in $cell, e.g. the area that the algorithm shall # never leave. my ($min_x, $min_y, $max_x, $max_y) = $self->_astar_boundaries(); # Max. steps to prevent endless searching in case of bugs like endless loops. my $tries = 0; my $max_tries = 2000000; # count how many times we did A* $self->{stats}->{astar}++; ########################################################################### ########################################################################### # put the start positions into OPEN my $i = 0; my $bias = 0; while ($i < scalar @start) { my ($sx,$sy,$type,$px,$py) = ($start[$i],$start[$i+1],$start[$i+2],$start[$i+3],$start[$i+4]); $i += 5; my $cell = $cells->{"$sx,$sy"}; my $rcell = ref($cell); next if $rcell && $rcell !~ /::Edge/; my $t = 0; $t = $cell->{type} & EDGE_NO_M_MASK if $rcell =~ /::Edge/; next if $t != 0 && $t != EDGE_HOR && $t != EDGE_VER; # For each start point, calculate the distance to each stop point, then use # the smallest as value: my $lowest_x = $stop[0]; my $lowest_y = $stop[1]; my $lowest = _astar_distance($sx,$sy, $stop[0], $stop[1]); for (my $u = $per_field; $u < $stop; $u += $per_field) { my $dist = _astar_distance($sx,$sy, $stop[$u], $stop[$u+1]); ($lowest_x, $lowest_y) = ($stop[$u],$stop[$u+1]) if $dist < $lowest; $lowest = $dist if $dist < $lowest; } # add a penalty for crossings my $malus = 0; $malus = 30 if $t != 0; $malus += _astar_modifier($px,$py, $sx, $sy, $sx, $sy); $open->add( [ $lowest, $sx, $sy, $px, $py, $type, 1 ] ); my $o = $malus + $bias + $lowest; print STDERR "# adding open pos $sx,$sy ($o = $malus + $bias + $lowest) at ($lowest_x,$lowest_y)\n" if $self->{debug} > 1; # The cost to reach the starting node is obviously 0. That means that there is # a tie between going down/up if both possibilities are equal likely. We insert # a small bias here that makes the prefered order east/south/west/north. Instead # the algorithmn exploring both way and terminating arbitrarily on the one that # first hits the target, it will explore only one. $open_by_pos->{"$sx,$sy"} = $o; $bias += $self->{_astar_bias} || 0; } ########################################################################### ########################################################################### # main A* loop my $stats = $self->{stats}; STEP: while( defined( $elem = $open->extract_top() ) ) { $stats->{astar_steps}++ if $self->{debug}; # hard limit on number of steps todo if ($tries++ > $max_tries) { $self->warn("A* reached maximum number of tries ($max_tries), giving up."); return []; } print STDERR "# Smallest elem from ", $open->elements(), " elems is: weight=", $elem->[0], " at $elem->[1],$elem->[2]\n" if $self->{debug} > 1; my ($val, $x,$y, $px,$py, $type, $do_stop) = @$elem; my $key = "$x,$y"; # move node into CLOSE and remove from OPEN my $g = $open_by_pos->{$key} || 0; $closed->{$key} = [ $px, $py, $val - $g, $g, $type, $do_stop ]; delete $open_by_pos->{$key}; # we are done when we hit one of the potential stop positions for (my $i = 0; $i < $stop; $i += $per_field) { # reached one stop position? if ($x == $stop[$i] && $y == $stop[$i+1]) { $closed->{$key}->[4] += $stop[$i+2] if defined $stop[$i+2]; # store the reached stop position if it is known if ($per_field > 3) { $closed->{$key}->[6] = $stop[$i+3]; $closed->{$key}->[7] = $stop[$i+4]; print STDERR "# Reached stop position $x,$y (lx,ly $stop[$i+3], $stop[$i+4])\n" if $self->{debug} > 1; } elsif ($self->{debug} > 1) { print STDERR "# Reached stop position $x,$y\n"; } last STEP; } } # end test for stop postion(s) $self->_croak("On of '$x,$y' is not defined") unless defined $x && defined $y; # get list of potential positions we need to explore from the current one my @p = $self->_astar_near_nodes($x,$y, $cells, $closed, $min_x, $min_y, $max_x, $max_y); my $n = 0; while ($n < scalar @p) { my $nx = $p[$n]; my $ny = $p[$n+1]; $n += 2; if (!defined $nx || !defined $ny) { require Carp; Carp::confess("On of '$nx,$ny' is not defined"); } my $lg = $g; $lg += _astar_modifier($px,$py,$x,$y,$nx,$ny,$cells) if defined $px && defined $py; my $n = "$nx,$ny"; # was already open? next if (exists $open_by_pos->{$n}); # print STDERR "# Already open pos $nx,$ny with $open_by_pos->{$n} (would be $lg)\n" # if $self->{debug} && exists $open_by_pos->{$n}; # # next if exists $open_by_pos->{$n} && $open_by_pos->{$n} <= $lg; # # if (exists $open_by_pos->{$n}) # { # $open->delete($nx, $ny); # } # calculate distance to each possible stop position, and # use the lowest one my $lowest_distance = _astar_distance($nx, $ny, $stop[0], $stop[1]); for (my $i = $per_field; $i < $stop; $i += $per_field) { my $d = _astar_distance($nx, $ny, $stop[$i], $stop[$i+1]); $lowest_distance = $d if $d < $lowest_distance; } print STDERR "# Opening pos $nx,$ny ($lowest_distance + $lg)\n" if $self->{debug} > 1; # open new position into OPEN $open->add( [ $lowest_distance + $lg, $nx, $ny, $x, $y, undef ] ); $open_by_pos->{$n} = $lg; } } ########################################################################### # A* is done, now build a path from the information we computed above: # count how many steps we did in A* $self->{stats}->{astar_steps} += $tries; # no more nodes to follow, so we couldn't find a path if (!defined $elem) { print STDERR "# A* couldn't find a path after $max_tries steps.\n" if $self->{debug}; return []; } my $path = []; my ($cx,$cy) = ($elem->[1],$elem->[2]); # the "last" cell in the path. Since we follow it backwards, it # becomes actually the next cell my ($lx,$ly); my $type; my $label_cell = 0; # found a cell to attach the label to? my @bends; # record all bends in the path to straighten it out my $idx = 0; # follow $elem back to the source to find the path while (defined $cx) { last unless exists $closed->{"$cx,$cy"}; my $xy = "$cx,$cy"; $type = $closed->{$xy}->[ 4 ]; my ($px,$py) = @{ $closed->{$xy} }; # get X,Y of parent cell my $edge_type = ($type||0) & EDGE_TYPE_MASK; if ($edge_type == 0) { my $edge_flags = ($type||0) & EDGE_FLAG_MASK; # either a start or a stop cell if (!defined $px) { # We can figure it out from the flag of the position of cx,cy # ................ # : EDGE_START_S : # ....................................... # START_E : px,py : EDGE_START_W : # ....................................... # : EDGE_START_N : # ................ ($px,$py) = ($cx, $cy); # start with same cell $py ++ if ($edge_flags & EDGE_START_S) != 0; $py -- if ($edge_flags & EDGE_START_N) != 0; $px ++ if ($edge_flags & EDGE_START_E) != 0; $px -- if ($edge_flags & EDGE_START_W) != 0; } # if lx, ly is undefined because px,py is a joint, get it via the stored # x,y pos of the very last cell in the path if (!defined $lx) { $lx = $closed->{$xy}->[6]; $ly = $closed->{$xy}->[7]; } # still not known? if (!defined $lx) { # If lx,ly is undefined because we are at the end of the path, # we can figure out from the flag of the position of cx,cy. # .............. # : EDGE_END_S : # ................................. # END_E : lx,ly : EDGE_END_W : # ................................. # : EDGE_END_N : # .............. ($lx,$ly) = ($cx, $cy); # start with same cell $ly ++ if ($edge_flags & EDGE_END_S) != 0; $ly -- if ($edge_flags & EDGE_END_N) != 0; $lx ++ if ($edge_flags & EDGE_END_E) != 0; $lx -- if ($edge_flags & EDGE_END_W) != 0; } # now figure out correct type for this cell from positions of # parent/following cell $type += _astar_edge_type($px, $py, $cx, $cy, $lx,$ly); } print STDERR "# Following back from $lx,$ly over $cx,$cy to $px,$py\n" if $self->{debug} > 1; if ($px == $lx && $py == $ly && ($cx != $lx || $cy != $ly)) { print STDERR "# Warning: A* detected loop in path-backtracking at $px,$py, $cx,$cy, $lx,$ly\n" if $self->{debug}; last; } $type = EDGE_HOR if ($type & EDGE_TYPE_MASK) == 0; # last resort # if this is the first hor edge, attach the label to it # XXX TODO: This clearly is not optimal. Look for left-most HOR CELL my $t = $type & EDGE_TYPE_MASK; # Do not put the label on crossings: if ($label_cell == 0 && (!exists $cells->{"$cx,$cy"}) && ($t == EDGE_HOR || $t == EDGE_VER)) { $label_cell++; $type += EDGE_LABEL_CELL; } push @bends, [ $type, $cx, $cy, -$idx ] if ($type == EDGE_S_E || $t == EDGE_S_W || $t == EDGE_N_E || $t == EDGE_N_W); unshift @$path, $cx, $cy, $type; # unshift to reverse the path last if $closed->{"$cx,$cy"}->[ 5 ]; # stop here? ($lx,$ly) = ($cx,$cy); ($cx,$cy) = @{ $closed->{"$cx,$cy"} }; # get X,Y of next cell $idx += 3; # index into $path (for bends) } print STDERR "# Trying to straighten path\n" if @bends >= 3 && $self->{debug}; # try to straighten unnec. inward bends $self->_straighten_path($path, \@bends, $edge) if @bends >= 3; return ($path,$closed,$open_by_pos) if wantarray; $path; } # 1: # | | # +----+ => | # | | # ----+ ------+ # 2: # +--- +------ # | | # +---+ => | # | | # 3: # ----+ ------+ # | => | # +----+ | # | | # 4: # | | # +---+ | # | => | # +----+ +------ my $bend_patterns = [ # The patterns are duplicated to catch both directions of the path: # First five entries must match # dx, dy, # coordinates for new edge # (2 == y, 1 == x, first is # taken from A, second from B) # these replace the first & last bend # 1: [ EDGE_N_W, EDGE_S_E, EDGE_N_W, 0, -1, 2, 1, EDGE_HOR, EDGE_VER, 1,0, 0,-1 ], # 0 [ EDGE_N_W, EDGE_S_E, EDGE_N_W, -1, 0, 1, 2, EDGE_VER, EDGE_HOR, 0,1, -1,0 ], # 1 # 2: [ EDGE_S_E, EDGE_N_W, EDGE_S_E, 0, -1, 1, 2, EDGE_VER, EDGE_HOR, 0,-1, 1,0 ], # 2 [ EDGE_S_E, EDGE_N_W, EDGE_S_E, -1, 0, 2, 1, EDGE_HOR, EDGE_VER, -1,0, 0,1 ], # 3 # 3: [ EDGE_S_W, EDGE_N_E, EDGE_S_W, 0, 1, 2, 1, EDGE_HOR, EDGE_VER, 1,0, 0,1 ], # 4 [ EDGE_S_W, EDGE_N_E, EDGE_S_W, -1, 0, 1, 2, EDGE_VER, EDGE_HOR, 0,-1, -1,0 ], # 5 # 4: [ EDGE_N_E, EDGE_S_W, EDGE_N_E, 1, 0, 1, 2, EDGE_VER, EDGE_HOR, 0,1, 1,0 ], # 6 [ EDGE_N_E, EDGE_S_W, EDGE_N_E, 0, -1, 2, 1, EDGE_HOR, EDGE_VER, -1,0, 0,-1 ], # 7 ]; sub _straighten_path { my ($self, $path, $bends, $edge) = @_; # XXX TODO: # in case of multiple bends, removes only one of them due to overlap my $cells = $self->{cells}; my $i = 0; BEND: while ($i < (scalar @$bends - 2)) { # for each bend, check it and the next two bends # print STDERR "Checking bend $i at $bends->[$i], $bends->[$i+1], $bends->[$i+2]\n"; my ($a,$b,$c) = ($bends->[$i], $bends->[$i+1], $bends->[$i+2]); my $dx = ($b->[1] - $a->[1]); my $dy = ($b->[2] - $a->[2]); my $p = 0; for my $pattern (@$bend_patterns) { $p++; next if ($a->[0] != $pattern->[0]) || ($b->[0] != $pattern->[1]) || ($c->[0] != $pattern->[2]) || ($dx != $pattern->[3]) || ($dy != $pattern->[4]); # pattern matched # print STDERR "# Got bends for pattern ", $p-1," (@$pattern):\n"; # print STDERR "# type x,y,\n# @$a\n# @$b\n# @$c\n"; # check that the alternative path is empty # new corner: my $cx = $a->[$pattern->[5]]; my $cy = $c->[$pattern->[6]]; ($cx,$cy) = ($cy,$cx) if $pattern->[5] == 2; # need to swap? next BEND if exists $cells->{"$cx,$cy"}; # print STDERR "# new corner at $cx,$cy (swap: $pattern->[5])\n"; # check from A to new corner my $x = $a->[1]; my $y = $a->[2]; my @replace = (); push @replace, $cx, $cy, $pattern->[0] if ($x == $cx && $y == $cy); my $ddx = $pattern->[9]; my $ddy = $pattern->[10]; # print STDERR "# dx,dy: $ddx,$ddy\n"; while ($x != $cx || $y != $cy) { next BEND if exists $cells->{"$x,$y"}; # print STDERR "# at $x $y (go to $cx,$cy)\n"; sleep(1); push @replace, $x, $y, $pattern->[7]; $x += $ddx; $y += $ddy; } $x = $cx; $y = $cy; # check from new corner to C $ddx = $pattern->[11]; $ddy = $pattern->[12]; while ($x != $c->[1] || $y != $c->[2]) { next BEND if exists $cells->{"$x,$y"}; # print STDERR "# at $x $y (go to $cx,$cy)\n"; sleep(1); push @replace, $x, $y, $pattern->[8]; # set the correct type on the corner $replace[-1] = $pattern->[0] if ($x == $cx && $y == $cy); $x += $ddx; $y += $ddy; } # insert Corner push @replace, $x, $y, $pattern->[8]; # use Data::Dumper; print STDERR Dumper(@replace); # print STDERR "# generated ", scalar @replace, " entries\n"; # print STDERR "# idx A $a->[3] C $c->[3]\n"; # the path is clear, so replace the inward bend with the new one my $diff = $a->[3] - $c->[3] ? -3 : 3; my $idx = 0; my $p_idx = $a->[3] + $diff; while ($idx < @replace) { # print STDERR "# replace $p_idx .. $p_idx + 2\n"; # print STDERR "# replace $path->[$p_idx] with $replace[$idx]\n"; # print STDERR "# replace $path->[$p_idx+1] with $replace[$idx+1]\n"; # print STDERR "# replace $path->[$p_idx+2] with $replace[$idx+2]\n"; $path->[$p_idx] = $replace[$idx]; $path->[$p_idx+1] = $replace[$idx+1]; $path->[$p_idx+2] = $replace[$idx+2]; $p_idx += $diff; $idx += 3; } } # end for this pattern } continue { $i++; }; } sub _map_as_html { my ($self, $cells, $p, $closed, $open, $w, $h) = @_; $w ||= 20; $h ||= 20; my $html = <

A* Map

Nodes examined: ##closed##
Nodes still to do (open): ##open##
Nodes in path: ##path##

EOF ; $html =~ s/##closed##/keys %$closed /eg; $html =~ s/##open##/keys %$open /eg; my $path = {}; while (@$p) { my $x = shift @$p; my $y = shift @$p; my $t = shift @$p; $path->{"$x,$y"} = undef; } $html =~ s/##path##/keys %$path /eg; $html .= '' . "\n"; for my $y (0..$h) { $html .= " \n"; for my $x (0..$w) { my $xy = "$x,$y"; my $c = ' ' x 4; $html .= " \n" and next if exists $cells->{$xy} and ref($cells->{$xy}) =~ /Node/; $html .= " \n" and next if exists $cells->{$xy} && !exists $path->{$xy}; $html .= " \n" and next unless exists $closed->{$xy} || exists $open->{$xy}; my $clr = '#a0a0a0'; if (exists $closed->{$xy}) { $c = ($closed->{$xy}->[3] || '0') . '+' . ($closed->{$xy}->[2] || '0'); my $color = 0x10 + 8 * (($closed->{$xy}->[2] || 0)); my $color2 = 0x10 + 8 * (($closed->{$xy}->[3] || 0)); $clr = sprintf("%02x%02x",$color,$color2) . 'a0'; } elsif (exists $open->{$xy}) { $c = ' ' . $open->{$xy} || '0'; my $color = 0xff - 8 * ($open->{$xy} || 0); $clr = 'a0' . sprintf("%02x",$color) . '00'; } my $b = ''; $b = 'border: 2px white solid;' if exists $path->{$xy}; $html .= " \n"; } $html .= " \n"; } $html .= "\n
$c$c$c$c
\n"; $html; } 1; __END__ =head1 NAME Graph::Easy::Layout::Scout - Find paths in a Manhattan-style grid =head1 SYNOPSIS use Graph::Easy; my $graph = Graph::Easy->new(); my $bonn = Graph::Easy::Node->new( name => 'Bonn', ); my $berlin = Graph::Easy::Node->new( name => 'Berlin', ); $graph->add_edge ($bonn, $berlin); $graph->layout(); print $graph->as_ascii( ); # prints: # +------+ +--------+ # | Bonn | --> | Berlin | # +------+ +--------+ =head1 DESCRIPTION C contains just the actual pathfinding code for L. It should not be used directly. =head1 EXPORT Exports nothing. =head1 METHODS This package inserts a few methods into C and C to enable path-finding for graphs. It should not be used directly. =head1 SEE ALSO L. =head1 AUTHOR Copyright (C) 2004 - 2007 by Tels L. See the LICENSE file for information. =cut Graph-Easy-0.73/lib/Graph/Easy/Layout/Grid.pm0000644000076400007640000002142712150072362020513 0ustar shlomifshlomif############################################################################# # Grid-management and layout preperation. # # (c) by Tels 2004-2006. ############################################################################# package Graph::Easy::Layout::Grid; $VERSION = '0.07'; ############################################################################# ############################################################################# package Graph::Easy; use strict; use Graph::Easy::Util qw(ord_values); sub _balance_sizes { # Given a list of column/row sizes and a minimum size that their sum must # be, will grow individual sizes until the constraint (sum) is met. my ($self, $sizes, $need) = @_; # XXX TODO: we can abort the loop and distribute the remaining nec. size # once all elements in $sizes are equal. return if $need < 1; # if there is only one element, return it immidiately if (@$sizes == 1) { $sizes->[0] = $need if $sizes->[0] < $need; return; } # endless loop until constraint is met while (1) { # find the smallest size, and also compute their sum my $sum = 0; my $i = 0; my $sm = $need + 1; # start with an arbitrary size my $sm_i = 0; # if none is != 0, then use the first for my $s (@$sizes) { $sum += $s; next if $s == 0; if ($s < $sm) { $sm = $s; $sm_i = $i; } $i++; } # their sum is already equal or bigger than what we need? last if $sum >= $need; # increase the smallest size by one, then try again $sizes->[$sm_i]++; } # use Data::Dumper; print STDERR "# " . Dumper($sizes),"\n"; undef; } sub _prepare_layout { # this method is used by as_ascii() and as_svg() to find out the # sizes and placement of the different cells (edges, nodes etc). my ($self,$format) = @_; # Find out for each row and colum how big they are: # +--------+-----+------+ # | Berlin | --> | Bonn | # +--------+-----+------+ # results in: # w, h, x, y # 0,0 => 10, 3, 0, 0 # 1,0 => 7, 3, 10, 0 # 2,0 => 8, 3, 16, 0 # Technically, we also need to "compress" away non-existant columns/rows. # We achive that by simply rendering them with size 0, so they become # practically invisible. my $cells = $self->{cells}; my $rows = {}; my $cols = {}; # the last column/row (highest X,Y pair) my $mx = -1000000; my $my = -1000000; # We need to do this twice, once for single-cell objects, and again for # objects covering multiple cells. The single-cell objects can be solved # first: # find all x and y occurances to sort them by row/columns for my $cell (ord_values $cells) { my ($x,$y) = ($cell->{x}, $cell->{y}); { no strict 'refs'; my $method = '_correct_size_' . $format; $method = '_correct_size' unless $cell->can($method); $cell->$method(); } my $w = $cell->{w} || 0; my $h = $cell->{h} || 0; # Set the minimum cell size only for single-celled objects: if ( (($cell->{cx}||1) + ($cell->{cy}||1)) == 2) { # record maximum size for that col/row $rows->{$y} = $h if $h >= ($rows->{$y} || 0); $cols->{$x} = $w if $w >= ($cols->{$x} || 0); } # Find highest X,Y pair. Always use x,y, and not x+cx,y+cy, because # a multi-celled object "sticking" out will not count unless there # is another object in the same row/column. $mx = $x if $x > $mx; $my = $y if $y > $my; } # insert a dummy row/column with size=0 as last $rows->{$my+1} = 0; $cols->{$mx+1} = 0; # do the last step again, but for multi-celled objects for my $cell (ord_values $cells) { my ($x,$y) = ($cell->{x}, $cell->{y}); my $w = $cell->{w} || 0; my $h = $cell->{h} || 0; # Set the minimum cell size only for multi-celled objects: if ( (($cell->{cx} || 1) + ($cell->{cy}||1)) > 2) { $cell->{cx} ||= 1; $cell->{cy} ||= 1; # do this twice, for X and Y: # print STDERR "\n# ", $cell->{name} || $cell->{id}, " cx=$cell->{cx} cy=$cell->{cy} $cell->{w},$cell->{h}:\n"; # create an array with the current sizes for the affacted rows/columns my @sizes; # print STDERR "# $cell->{cx} $cell->{cy} at cx:\n"; # XXX TODO: no need to do this for empty/zero cols for (my $i = 0; $i < $cell->{cx}; $i++) { push @sizes, $cols->{$i+$x} || 0; } $self->_balance_sizes(\@sizes, $cell->{w}); # store the result back for (my $i = 0; $i < $cell->{cx}; $i++) { # print STDERR "# store back $sizes[$i] to col ", $i+$x,"\n"; $cols->{$i+$x} = $sizes[$i]; } @sizes = (); # print STDERR "# $cell->{cx} $cell->{cy} at cy:\n"; # XXX TODO: no need to do this for empty/zero cols for (my $i = 0; $i < $cell->{cy}; $i++) { push @sizes, $rows->{$i+$y} || 0; } $self->_balance_sizes(\@sizes, $cell->{h}); # store the result back for (my $i = 0; $i < $cell->{cy}; $i++) { # print STDERR "# store back $sizes[$i] to row ", $i+$y,"\n"; $rows->{$i+$y} = $sizes[$i]; } } } print STDERR "# Calculating absolute positions for rows/columns\n" if $self->{debug}; # Now run through all rows/columns and get their absolute pos by taking all # previous ones into account. my $pos = 0; for my $y (sort { $a <=> $b } keys %$rows) { my $s = $rows->{$y}; $rows->{$y} = $pos; # first is 0, second is $rows[1] etc $pos += $s; } $pos = 0; for my $x (sort { $a <=> $b } keys %$cols) { my $s = $cols->{$x}; $cols->{$x} = $pos; $pos += $s; } # find out max. dimensions for framebuffer print STDERR "# Finding max. dimensions for framebuffer\n" if $self->{debug}; my $max_y = 0; my $max_x = 0; for my $v (ord_values $cells) { # Skip multi-celled nodes for later. next if ($v->{cx}||1) + ($v->{cy}||1) != 2; # X and Y are col/row, so translate them to real pos my $x = $cols->{ $v->{x} }; my $y = $rows->{ $v->{y} }; # Also set correct the width/height of each cell to be the maximum # width/height of that row/column and store the previous size in 'minw' # and 'minh', respectively. $v->{minw} = $v->{w}; $v->{minh} = $v->{h}; # find next col/row my $nx = $v->{x} + 1; my $next_col = $cols->{ $nx }; my $ny = $v->{y} + 1; my $next_row = $rows->{ $ny }; $next_col = $cols->{ ++$nx } while (!defined $next_col); $next_row = $rows->{ ++$ny } while (!defined $next_row); $v->{w} = $next_col - $x; $v->{h} = $next_row - $y; my $m = $y + $v->{h} - 1; $max_y = $m if $m > $max_y; $m = $x + $v->{w} - 1; $max_x = $m if $m > $max_x; } # repeat the previous step, now for multi-celled objects foreach my $v (ord_values ( $self->{cells} )) { next unless defined $v->{x} && (($v->{cx}||1) + ($v->{cy}||1) > 2); # X and Y are col/row, so translate them to real pos my $x = $cols->{ $v->{x} }; my $y = $rows->{ $v->{y} }; $v->{minw} = $v->{w}; $v->{minh} = $v->{h}; # find next col/row my $nx = $v->{x} + ($v->{cx} || 1); my $next_col = $cols->{ $nx }; my $ny = $v->{y} + ($v->{cy} || 1); my $next_row = $rows->{ $ny }; $next_col = $cols->{ ++$nx } while (!defined $next_col); $next_row = $rows->{ ++$ny } while (!defined $next_row); $v->{w} = $next_col - $x; $v->{h} = $next_row - $y; my $m = $y + $v->{h} - 1; $max_y = $m if $m > $max_y; $m = $x + $v->{w} - 1; $max_x = $m if $m > $max_x; } # return what we found out: ($rows,$cols,$max_x,$max_y); } 1; __END__ =head1 NAME Graph::Easy::Layout::Grid - Grid management and size calculation =head1 SYNOPSIS use Graph::Easy; my $graph = Graph::Easy->new(); my $bonn = Graph::Easy::Node->new( name => 'Bonn', ); my $berlin = Graph::Easy::Node->new( name => 'Berlin', ); $graph->add_edge ($bonn, $berlin); $graph->layout(); print $graph->as_ascii( ); # prints: # +------+ +--------+ # | Bonn | --> | Berlin | # +------+ +--------+ =head1 DESCRIPTION C contains routines that calculate cell sizes on the grid, which is necessary for ASCII, boxart and SVG output. Used automatically by Graph::Easy. =head1 EXPORT Exports nothing. =head1 SEE ALSO L. =head1 METHODS This module injects the following methods into Graph::Easy: =head2 _prepare_layout() my ($rows,$cols,$max_x,$max_y, \@V) = $graph->_prepare_layout(); Returns two hashes (C<$rows> and C<$cols>), containing the columns and rows of the layout with their nec. sizes (in chars) plus the maximum framebuffer size nec. for this layout. Also returns reference of a list of all cells to be rendered. =head1 AUTHOR Copyright (C) 2004 - 2006 by Tels L. See the LICENSE file for information. =cut Graph-Easy-0.73/lib/Graph/Easy/As_ascii.pm0000644000076400007640000011531711675050456020101 0ustar shlomifshlomif############################################################################# # Render Nodes/Edges/Cells as ASCII/Unicode box drawing art # # (c) by Tels 2004-2007. Part of Graph::Easy ############################################################################# package Graph::Easy::As_ascii; $VERSION = '0.22'; use utf8; ############################################################################# ############################################################################# package Graph::Easy::Edge::Cell; use strict; my $edge_styles = [ { # style hor, ver, cross, corner (SE, SW, NE, NW) 'solid' => [ '--', "|", '+', '+','+','+','+' ], # simple line 'double' => [ '==', "H", "#", '#','#','#','#' ], # double line 'double-dash' => [ '= ', '"', "#", '#','#','#','#' ], # double dashed line 'dotted' => [ '..', ":", ':', '.','.','.','.' ], # dotted 'dashed' => [ '- ', "'", '+', '+','+','+','+' ], # dashed 'dot-dash' => [ '.-', "!", '+', '+','+','+','+' ], # dot-dash 'dot-dot-dash' => [ '..-', "!", '+', '+','+','+','+' ], # dot-dot-dash 'wave' => [ '~~', "}", '+', '*','*','*','*' ], # wave 'bold' => [ '##', "#", '#', '#','#','#','#' ], # bold 'bold-dash' => [ '# ', "#", '#', '#','#','#','#' ], # bold-dash 'wide' => [ '##', "#", '#', '#','#','#','#' ], # wide 'broad' => [ '##', "#", '#', '#','#','#','#' ], # broad }, { # style hor, ver, cross, corner (SE, SW, NE, NW) 'solid' => [ '─', '│', '┼', '┌', '┐', '└', '┘' ], 'double' => [ '═', '║', '╬', '╔', '╗', '╚', '╝' ], 'double-dash' => [ '═'.' ', '∥', '╬', '╔', '╗', '╚', '╝' ], # double dashed 'dotted' => [ '·', ':', '┼', '┌', '┐', '└', '┘' ], # dotted 'dashed' => [ '╴', '╵', '┘', '┌', '┐', '╵', '┘' ], # dashed 'dot-dash' => [ '·'.'-', "!", '┼', '┌', '┐', '└', '┘' ], # dot-dash 'dot-dot-dash' => [ ('·' x 2).'-', "!", '┼', '┌', '┐', '└', '┘' ], # dot-dot-dash 'wave' => [ '∼', '≀', '┼', '┌', '┐', '└', '┘' ], # wave 'bold' => [ '━', '┃', '╋', '┏', '┓', '┗', '┛' ], # bold 'bold-dash' => [ '━'.' ', '╻', '╋', '┏', '┓', '┗', '┛' ], # bold-dash 'broad' => [ '▬', '▮', '█', '█', '█', '█', '█' ], # wide 'wide' => [ '█', '█', '█', '█', '█', '█', '█' ], # broad # these two make it nec. to support multi-line styles for the vertical edge pieces # 'broad-dash' => [ '◼', '◼', '◼', '◼', '◼', '◼', '◼' ], # broad-dash # 'wide-dash' => [ ('█'x 2) .' ', '█', '█', '█', '█', '█', '█' ], # wide-dash }, ]; sub _edge_style { my ($self, $st) = @_; my $g = $self->{graph}->{_ascii_style} || 0; $st = $self->{style} unless defined $st; $edge_styles->[$g]->{ $st }; } # | | | | : } | # ===+=== ###+### ....!.... ~~~+~~~ ----+--- ...+... .-.+.-.- # | | | | : { | my $cross_styles = [ # normal cross [ { 'boldsolid' => '┿', 'solidbold' => '╂', 'doublesolid' => '╪', 'soliddouble' => '╫', 'dashedsolid' => '┤', 'soliddashed' => '┴', 'doubledashed' => '╧', 'dasheddouble' => '╢', }, { 'boldsolid' => '+', 'dashedsolid' => '+', 'dottedsolid' => '!', 'dottedwave' => '+', 'doublesolid' => '+', 'dot-dashsolid' => '+', 'dot-dot-dashsolid' => '+', 'soliddotted' => '+', 'solidwave' => '+', 'soliddashed' => '+', 'soliddouble' => 'H', 'wavesolid' => '+', }, ], undef, # HOR, cannot happen undef, # VER, cannot happen undef, undef, undef, undef, # S_E_W -+- # | [ { 'solidsolid' => '┬', 'boldbold' => '┳', 'doubledouble' => '╦', 'dasheddashed' => '╴', 'dotteddotted' => '·', }, ], # N_E_W | # -+- [ { 'solidsolid' => '┴', 'boldbold' => '┻', 'doubledouble' => '╩', 'dotteddotted' => '·', }, ], # E_N_S | # +- # | [ { 'solidsolid' => '├', 'boldbold' => '┣', 'doubledouble' => '╠', 'dotteddotted' => ':', }, ], # W_N_S | # -+ # | [ { 'solidsolid' => '┤', 'boldbold' => '┫', 'doubledouble' => '╣', 'dotteddotted' => ':', }, ] ]; sub _arrow_style { my $self = shift; my $edge = $self->{edge}; my $as = $edge->attribute('arrowstyle'); $as = 'none' if $edge->{undirected}; $as; } sub _arrow_shape { my $self = shift; my $edge = $self->{edge}; my $as = $edge->attribute('arrowshape'); $as; } sub _cross_style { my ($self, $st, $corner_type) = @_; my $g = $self->{graph}->{_ascii_style} || 0; # 0 => 1, 1 => 0 $g = 1 - $g; # for ASCII, one style fist all (e.g a joint has still "+" as corner) $corner_type = 0 unless defined $corner_type; $corner_type = 0 if $g == 1; $cross_styles->[$corner_type]->[$g]->{ $st }; } sub _insert_label { my ($self, $fb, $xs, $ys, $ws, $hs, $align_ver) = @_; my $align = $self->{edge}->attribute('align'); my ($lines,$aligns) = $self->_aligned_label($align); $ys = $self->{h} - scalar @$lines + $ys if $ys < 0; $ws ||= 0; $hs ||= 0; my $w = $self->{w} - $ws - $xs; my $h = $self->{h} - $hs - $ys; $self->_printfb_aligned ($fb, $xs, $ys, $w, $h, $lines, $aligns, $align_ver); } sub _draw_hor { # draw a HOR edge piece my ($self, $fb) = @_; my $style = $self->_edge_style(); my $w = $self->{w}; # '-' => '-----', '.-' => '.-.-.-' # "(2 + ... )" to get space for the offset my $len = length($style->[0]); my $line = $style->[0] x (2 + $w / $len); # '.-.-.-' => '-.-.-' if $x % $ofs == 1 (e.g. on odd positions) my $ofs = $self->{rx} % $len; my $type = ($self->{type} & (~EDGE_MISC_MASK)); substr($line,0,$ofs) = '' if $ofs != 0 && ($type != EDGE_SHORT_E && $type != EDGE_SHORT_W); $line = substr($line, 0, $w) if length($line) > $w; # handle start/end point my $flags = $self->{type} & EDGE_FLAG_MASK; my $as = $self->_arrow_style(); my $ashape; $ashape = $self->_arrow_shape() if $as ne 'none'; my $x = 0; # offset for the edge line my $xs = 1; # offset for the edge label my $xr = 0; # right offset for label if (($flags & EDGE_START_W) != 0) { $x++; chop($line); # ' ---' $xs++; } if (($flags & EDGE_START_E) != 0) { chop($line); # '--- ' } if (($flags & EDGE_END_E) != 0) { # '--> ' chop($line); substr($line,-1,1) = $self->_arrow($as, ARROW_RIGHT, $ashape) if $as ne 'none'; $xr++; } if (($flags & EDGE_END_W) != 0) { # ' <--' substr($line,0,1) = ' ' if $as eq 'none'; substr($line,0,2) = ' ' . $self->_arrow($as, ARROW_LEFT, $ashape) if $as ne 'none'; $xs++; } $self->_printfb_line ($fb, $x, $self->{h} - 2, $line); $self->_insert_label($fb, $xs, 0, $xs+$xr, 2, 'bottom' ) if ($self->{type} & EDGE_LABEL_CELL); } sub _draw_ver { # draw a VER edge piece my ($self, $fb) = @_; my $style = $self->_edge_style(); my $h = $self->{h}; # '|' => '|||||', '{}' => '{}{}{}' my $line = $style->[1] x (1 + $h / length($style->[1])); $line = substr($line, 0, $h) if length($line) > $h; my $flags = $self->{type} & EDGE_FLAG_MASK; # XXX TODO: handle here start points # we get away with not handling them because in VER edges # starting points are currently invisible. my $as = $self->_arrow_style(); if ($as ne 'none') { my $ashape = $self->_arrow_shape(); substr($line,0,1) = $self->_arrow($as,ARROW_UP, $ashape) if (($flags & EDGE_END_N) != 0); substr($line,-1,1) = $self->_arrow($as,ARROW_DOWN, $ashape) if (($flags & EDGE_END_S) != 0); } $self->_printfb_ver ($fb, 2, 0, $line); $self->_insert_label($fb, 4, 1, 4, 2, 'middle') if ($self->{type} & EDGE_LABEL_CELL); } sub _draw_cross { # draw a CROSS sections, or a joint (which is a 3/4 cross) my ($self, $fb) = @_; # vertical piece my $style = $self->_edge_style( $self->{style_ver} ); my $invisible = 0; my $line; my $flags = $self->{type} & EDGE_FLAG_MASK; my $type = $self->{type} & EDGE_TYPE_MASK; my $as = $self->_arrow_style(); my $y = $self->{h} - 2; print STDERR "# drawing cross at $self->{x},$self->{y} with flags $flags\n" if $self->{debug}; if ($self->{style_ver} ne 'invisible') { my $h = $self->{h}; # '|' => '|||||', '{}' => '{}{}{}' $line = $style->[1] x (2 + $h / length($style->[1])); $line = substr($line, 0, $h) if length($line) > $h; if ($as ne 'none') { my $ashape = $self->_arrow_shape(); substr($line,0,1) = $self->_arrow($as,ARROW_UP, $ashape) if (($flags & EDGE_END_N) != 0); substr($line,-1,1) = $self->_arrow($as,ARROW_DOWN, $ashape) if (($flags & EDGE_END_S) != 0); } # create joints substr($line,0,$y) = ' ' x $y if $type == EDGE_S_E_W; substr($line,$y,2) = ' ' if $type == EDGE_N_E_W; $self->_printfb_ver ($fb, 2, 0, $line); } else { $invisible++; } # horizontal piece $style = $self->_edge_style(); my $ashape; $ashape = $self->_arrow_style() if $as ne 'none'; if ($self->{style} ne 'invisible') { my $w = $self->{w}; # '-' => '-----', '.-' => '.-.-.-' my $len = length($style->[0]); $line = $style->[0] x (2 + $w / $len); # '.-.-.-' => '-.-.-' if $x % $ofs == 1 (e.g. on odd positions) my $ofs = $self->{rx} % $len; substr($line,0,$ofs) = '' if $ofs != 0; $line = substr($line, 0, $w) if length($line) > $w; my $x = 0; if (($flags & EDGE_START_W) != 0) { $x++; chop($line); # ' ---' } if (($flags & EDGE_START_E) != 0) { chop($line); # '--- ' } if (($flags & EDGE_END_E) != 0) { # '--> ' chop($line); substr($line,-1,1) = $self->_arrow($as, ARROW_RIGHT, $ashape) if $as ne 'none'; } if (($flags & EDGE_END_W) != 0) { # ' <--' substr($line,0,1) = ' ' if $as eq 'none'; substr($line,0,2) = ' ' . $self->_arrow($as, ARROW_LEFT, $ashape) if $as ne 'none'; } substr($line,0,2) = ' ' if $type == EDGE_E_N_S; substr($line,2,$self->{w}-2) = ' ' x ($self->{w}-2) if $type == EDGE_W_N_S; $self->_printfb_line ($fb, $x, $y, $line); } else { $invisible++; } if (!$invisible) { # draw the crossing character only if both lines are visible my $cross = $style->[2]; my $s = $self->{style} . $self->{style_ver}; $cross = ($self->_cross_style($s,$type) || $cross); # if $self->{style_ver} ne $self->{style}; $self->_printfb ($fb, 2, $y, $cross); } # done } sub _draw_corner { # draw a corner (N_E, S_E etc) my ($self, $fb) = @_; my $type = $self->{type} & EDGE_TYPE_MASK; my $flags = $self->{type} & EDGE_FLAG_MASK; ############ # ........ # 0 : : # 1 : : label would appear here # 2 : +---: (w-3) = 3 chars wide # 3 : | : always 1 char high # .......: # 012345 # draw the vertical piece # get the style my $style = $self->_edge_style(); my $h = 1; my $y = $self->{h} -1; if ($type == EDGE_N_E || $type == EDGE_N_W) { $h = $self->{h} - 2; $y = 0; } # '|' => '|||||', '{}' => '{}{}{}' my $line = $style->[1] x (1 + $h / length($style->[1])); $line = substr($line, 0, $h) if length($line) > $h; my $as = $self->_arrow_style(); my $ashape; if ($as ne 'none') { $ashape = $self->_arrow_shape(); substr($line,0,1) = $self->_arrow($as, ARROW_UP, $ashape) if (($flags & EDGE_END_N) != 0); substr($line,-1,1) = $self->_arrow($as, ARROW_DOWN, $ashape) if (($flags & EDGE_END_S) != 0); } $self->_printfb_ver ($fb, 2, $y, $line); # horizontal piece my $w = $self->{w} - 3; $y = $self->{h} - 2; my $x = 3; if ($type == EDGE_N_W || $type == EDGE_S_W) { $w = 2; $x = 0; } # '-' => '-----', '.-' => '.-.-.-' my $len = length($style->[0]); $line = $style->[0] x (2 + $w / $len); # '.-.-.-' => '-.-.-' if $x % $ofs == 1 (e.g. on odd positions) my $ofs = ($x + $self->{rx}) % $len; substr($line,0,$ofs) = '' if $ofs != 0; $line = substr($line, 0, $w) if length($line) > $w; substr($line,-1,1) = ' ' if ($flags & EDGE_START_E) != 0; substr($line,0,1) = ' ' if ($flags & EDGE_START_W) != 0; if (($flags & EDGE_END_E) != 0) { substr($line,-1,1) = ' ' if $as eq 'none'; substr($line,-2,2) = $self->_arrow($as, ARROW_RIGHT, $ashape) . ' ' if $as ne 'none'; } if (($flags & EDGE_END_W) != 0) { substr($line,0,1) = ' ' if $as eq 'none'; substr($line,0,2) = ' ' . $self->_arrow($as, ARROW_LEFT, $ashape) if $as ne 'none'; } $self->_printfb_line ($fb, $x, $y, $line); my $idx = 3; # corner (SE, SW, NE, NW) $idx = 4 if $type == EDGE_S_W; $idx = 5 if $type == EDGE_N_E; $idx = 6 if $type == EDGE_N_W; # insert the corner character $self->_printfb ($fb, 2, $y, $style->[$idx]); } sub _draw_loop_hor { my ($self, $fb) = @_; my $type = $self->{type} & EDGE_TYPE_MASK; my $flags = $self->{type} & EDGE_FLAG_MASK; ############ # .......... # 0 : : # 1 : : label would appear here # 2 : +--+ : (w-6) = 2 chars wide # 3 : | v : 1 char high # .........: # 01234567 ############ # .......... # 0 : | ^ : ver is h-2 chars high # 1 : | | : label would appear here # 2 : +--+ : (w-6) = 2 chars wide # 3 : : # .........: # 01234567 # draw the vertical pieces # get the style my $style = $self->_edge_style(); my $h = 1; my $y = $self->{h} - 1; if ($type == EDGE_S_W_N) { $h = $self->{h} - 2; $y = 0; } # '|' => '|||||', '{}' => '{}{}{}' my $line = $style->[1] x (1 + $h / length($style->[1])); $line = substr($line, 0, $h) if length($line) > $h; my $as = $self->_arrow_style(); my $ashape; $ashape = $self->_arrow_shape() if $as ne 'none'; if ($self->{edge}->{bidirectional} && $as ne 'none') { substr($line,0,1) = $self->_arrow($as, ARROW_UP, $ashape) if (($flags & EDGE_END_N) != 0); substr($line,-1,1) = $self->_arrow($as, ARROW_DOWN, $ashape) if (($flags & EDGE_END_S) != 0); } $self->_printfb_ver ($fb, $self->{w}-3, $y, $line); if ($as ne 'none') { substr($line,0,1) = $self->_arrow($as, ARROW_UP, $ashape) if (($flags & EDGE_END_N) != 0); substr($line,-1,1) = $self->_arrow($as, ARROW_DOWN, $ashape) if (($flags & EDGE_END_S) != 0); } $self->_printfb_ver ($fb, 2, $y, $line); # horizontal piece my $w = $self->{w} - 6; $y = $self->{h} - 2; my $x = 3; # '-' => '-----', '.-' => '.-.-.-' my $len = length($style->[0]); $line = $style->[0] x (2 + $w / $len); # '.-.-.-' => '-.-.-' if $x % $ofs == 1 (e.g. on odd positions) my $ofs = ($x + $self->{rx}) % $len; substr($line,0,$ofs) = '' if $ofs != 0; $line = substr($line, 0, $w) if length($line) > $w; $self->_printfb_line ($fb, $x, $y, $line); my $corner_idx = 3; $corner_idx = 5 if $type == EDGE_S_W_N; # insert the corner characters $self->_printfb ($fb, 2, $y, $style->[$corner_idx]); $self->_printfb ($fb, $self->{w}-3, $y, $style->[$corner_idx+1]); my $align = 'bottom'; $align = 'top' if $type == EDGE_S_W_N; $self->_insert_label($fb, 4, 0, 4, 2, $align) if ($self->{type} & EDGE_LABEL_CELL); # done } sub _draw_loop_ver { my ($self, $fb) = @_; my $type = $self->{type} & EDGE_TYPE_MASK; my $flags = $self->{type} & EDGE_FLAG_MASK; ############ # ........ # 0 : : label would appear here # 1 : +-- : # 2 : | : # 3 : +-> : # .......: # 012345 # ........ # 0 : : label would appear here # 1 : --+ : # 2 : | : # 3 : <-+ : # .......: # 012345 ########################################################################### # draw the vertical piece # get the style my $style = $self->_edge_style(); my $h = 1; my $y = $self->{h} - 3; # '|' => '|||||', '{}' => '{}{}{}' my $line = $style->[1] x (1 + $h / length($style->[1])); $line = substr($line, 0, $h) if length($line) > $h; my $x = 2; $x = $self->{w}-3 if ($type == EDGE_E_S_W); $self->_printfb_ver ($fb, $x, $y, $line); ########################################################################### # horizontal pieces my $w = $self->{w} - 3; $y = $self->{h} - 4; $x = 2; $x = 1 if ($type == EDGE_E_S_W); # '-' => '-----', '.-' => '.-.-.-' my $len = length($style->[0]); $line = $style->[0] x (2 + $w / $len); # '.-.-.-' => '-.-.-' if $x % $ofs == 1 (e.g. on odd positions) my $ofs = ($x + $self->{rx}) % $len; substr($line,0,$ofs) = '' if $ofs != 0; $line = substr($line, 0, $w) if length($line) > $w; my $as = $self->_arrow_style(); my $ashape; $ashape = $self->_arrow_shape() if $as ne 'none'; if ($self->{edge}->{bidirectional} && $as ne 'none') { substr($line,0,1) = $self->_arrow($as, ARROW_LEFT, $ashape) if (($flags & EDGE_END_W) != 0); substr($line,-1,1) = $self->_arrow($as, ARROW_RIGHT, $ashape) if (($flags & EDGE_END_E) != 0); } $self->_printfb_line ($fb, $x, $y, $line); if ($as ne 'none') { substr($line,0,1) = $self->_arrow($as, ARROW_LEFT, $ashape) if (($flags & EDGE_END_W) != 0); substr($line,-1,1) = $self->_arrow($as, ARROW_RIGHT, $ashape) if (($flags & EDGE_END_E) != 0); } $self->_printfb_line ($fb, $x, $self->{h} - 2, $line); $x = 2; $x = $self->{w}-3 if ($type == EDGE_E_S_W); my $corner_idx = 3; $corner_idx = 4 if $type == EDGE_E_S_W; # insert the corner characters $self->_printfb ($fb, $x, $y, $style->[$corner_idx]); $self->_printfb ($fb, $x, $self->{h}-2, $style->[$corner_idx+2]); $x = 4; $x = 3 if ($type == EDGE_E_S_W); $self->_insert_label($fb, $x, 0, $x, 4, 'bottom') if ($self->{type} & EDGE_LABEL_CELL); # done } # which method to call for which edge type my $draw_dispatch = { EDGE_HOR() => '_draw_hor', EDGE_VER() => '_draw_ver', EDGE_S_E() => '_draw_corner', EDGE_S_W() => '_draw_corner', EDGE_N_E() => '_draw_corner', EDGE_N_W() => '_draw_corner', EDGE_CROSS() => '_draw_cross', EDGE_W_N_S() => '_draw_cross', EDGE_E_N_S() => '_draw_cross', EDGE_N_E_W() => '_draw_cross', EDGE_S_E_W() => '_draw_cross', EDGE_N_W_S() => '_draw_loop_hor', EDGE_S_W_N() => '_draw_loop_hor', EDGE_E_S_W() => '_draw_loop_ver', EDGE_W_S_E() => '_draw_loop_ver', }; sub _draw_label { # This routine is cunningly named _draw_label, because it actually # draws the edge line(s). The label text will be drawn by the individual # routines called below. my ($self, $fb, $x, $y) = @_; my $type = $self->{type} & EDGE_TYPE_MASK; # for cross sections, we maybe need to draw one of the parts: return if $self->attribute('style') eq 'invisible' && $type ne EDGE_CROSS; my $m = $draw_dispatch->{$type}; $self->_croak("Unknown edge type $type") unless defined $m; # store the coordinates of our upper-left corner (for seamless rendering) $self->{rx} = $x || 0; $self->{ry} = $y || 0; $self->$m($fb); delete $self->{rx}; delete $self->{ry}; # no longer needed } ############################################################################# ############################################################################# package Graph::Easy::Node; use strict; sub _framebuffer { # generate an actual framebuffer consisting of spaces my ($self, $w, $h) = @_; print STDERR "# trying to generate framebuffer of undefined width for $self->{name}\n", join (": ", caller(),"\n") if !defined $w; my @fb; my $line = ' ' x $w; for my $y (1..$h) { push @fb, $line; } \@fb; } sub _printfb_aligned { my ($self,$fb, $x1,$y1, $w,$h, $lines, $aligns, $align_ver) = @_; $align_ver = 'middle' unless $align_ver; # $align_ver eq 'middle': my $y = $y1 + ($h / 2) - (scalar @$lines / 2); if ($align_ver eq 'top') { $y = $y1; $y1 = 0; } if ($align_ver eq 'bottom') { $y = $h - scalar @$lines; $y1 = 0; } my $xc = ($w / 2); my $i = 0; while ($i < @$lines) { # get the line and her alignment my ($l,$al) = ($lines->[$i],$aligns->[$i]); my $x = 0; # left is default $x = $xc - length($l) / 2 if $al eq 'c'; $x = $w - length($l) if $al eq 'r'; # now print the line (inlined print_fb_line for speed) substr ($fb->[int($y+$i+$y1)], int($x+$x1), length($l)) = $l; $i++; } } sub _printfb_line { # Print one textline into a framebuffer # Caller MUST ensure proper size of FB, for speed reasons, # we do not check whether text fits! my ($self, $fb, $x, $y, $l) = @_; # [0] = '0123456789...' substr ($fb->[$y], $x, length($l)) = $l; } sub _printfb { # Print (potential a multiline) text into a framebuffer # Caller MUST ensure proper size of FB, for speed reasons, # we do not check whether the text fits! my ($self, $fb, $x, $y, @lines) = @_; # [0] = '0123456789...' # [1] = '0123456789...' etc for my $l (@lines) { # # XXX DEBUG: # if ( $x + length($l) > length($fb->[$y])) # { # require Carp; # Carp::confess("substr outside framebuffer"); # } substr ($fb->[$y], $x, length($l)) = $l; $y++; } } sub _printfb_ver { # Print a string vertical into a framebuffer. # Caller MUST ensure proper size of FB, for speed reasons, # we do not check whether text fits! my ($self, $fb, $x, $y, $line) = @_; # this more than twice as fast as: # "@pieces = split//,$line; _printfb(...)" my $y1 = $y + length($line); substr ($fb->[$y1], $x, 1) = chop($line) while ($y1-- > $y); } # for ASCII and box drawing: # the array contains for each style: # upper left edge # upper right edge # lower right edge # lower left edge # hor style (top edge) # hor style (bottom side) # ver style (right side) (multiple characters possible) # ver style (left side) (multiple characters possible) # T crossing (see drawing below) # T to right # T to left # T to top # T shape (to bottom) # # +-----4-----4------+ # | | | | # | | | | # | | | | # 1-----0-----3------2 1 = T to right, 2 = T to left, 3 T to top # | | 0 = cross, 4 = T shape # | | # | | # +-----+ my $border_styles = [ { solid => [ '+', '+', '+', '+', '-', '-', [ '|' ], [ '|' ], '+', '+', '+', '+', '+' ], dotted => [ '.', '.', ':', ':', '.', '.', [ ':' ], [ ':' ], '.', '.', '.', '.', '.' ], dashed => [ '+', '+', '+', '+', '- ', '- ', [ "'" ], [ "'" ], '+', '+', '+', '+', '+' ], 'dot-dash' => [ '+', '+', '+', '+', '.-', '.-', [ '!' ], [ '!' ], '+', '+', '+', '+', '+' ], 'dot-dot-dash' => [ '+', '+', '+', '+', '..-', '..-', [ '|', ':' ], [ '|',':' ], '+', '+', '+', '+', '+' ], bold => [ '#', '#', '#', '#', '#', '#', [ '#' ], [ '#' ], '#', '#', '#', '#', '#' ], 'bold-dash' => [ '#', '#', '#', '#', '# ', '# ', ['#',' ' ], [ '#',' ' ], '#', '#', '#', '#', '#' ], double => [ '#', '#', '#', '#', '=', '=', [ 'H' ], [ 'H' ], '#', '#', '#', '#', '#' ], 'double-dash' => [ '#', '#', '#', '#', '= ', '= ', [ '"' ], [ '"' ], '#', '#', '#', '#', '#' ], wave => [ '+', '+', '+', '+', '~', '~', [ '{', '}' ], [ '{','}' ], '+', '+', '+', '+', '+' ], broad => [ '#', '#', '#', '#', '#', '#', [ '#' ], [ '#' ], '#', '#', '#', '#', '#' ], wide => [ '#', '#', '#', '#', '#', '#', [ '#' ], [ '#' ], '#', '#', '#', '#', '#' ], none => [ ' ', ' ', ' ', ' ', ' ', ' ', [ ' ' ], [ ' ' ], ' ', ' ', ' ', ' ', ' ' ], }, { solid => [ '┌', '┐', '┘', '└', '─', '─', [ '│' ], [ '│' ], '┼', '├', '┤', '┴', '┬' ], double => [ '╔', '╗', '╝', '╚', '═', '═', [ '║' ], [ '║' ], '┼', '├', '┤', '┴', '┬' ], dotted => [ '┌', '┐', '┘', '└', '⋯', '⋯', [ '⋮' ], [ '⋮' ], '┼', '├', '┤', '┴', '┬' ], dashed => [ '┌', '┐', '┘', '└', '−', '−', [ '╎' ], [ '╎' ], '┼', '├', '┤', '┴', '┬' ], 'dot-dash' => [ '┌', '┐', '┘', '└', '·'.'-', '·'.'-', ['!'], ['!'], '┼', '├', '┤', '┴', '┬' ], 'dot-dot-dash' => [ '┌', '┐', '┘', '└', ('·' x 2) .'-', ('·' x 2) .'-', [ '│', ':' ], [ '│', ':' ], '┼', '├', '┤', '┴', '┬' ], bold => [ '┏', '┓', '┛', '┗', '━', '━', [ '┃' ], [ '┃' ], '┼', '├', '┤', '┴', '┬' ], 'bold-dash' => [ '┏', '┓', '┛', '┗', '━'.' ', '━'.' ', [ '╻' ], [ '╻' ], '┼', '├', '┤', '┴', '┬' ], 'double-dash' => [ '╔', '╗', '╝', '╚', '═'.' ', '═'.' ', [ '∥' ], [ '∥' ], '┼', '├', '┤', '┴', '┬' ], wave => [ '┌', '┐', '┘', '└', '∼', '∼', [ '≀' ], [ '≀' ], '┼', '├', '┤', '┴', '┬' ], broad => [ '▛', '▜', '▟', '▙', '▀', '▄', [ '▌' ], [ '▐' ], '▄', '├', '┤', '┴', '┬' ], wide => [ '█', '█', '█', '█', '█', '█', [ '█' ], [ '█' ], '█', '█', '█', '█', '█' ], none => [ ' ', ' ', ' ', ' ', ' ', ' ', [ ' ' ], [ ' ' ], ' ', ' ', ' ', ' ', ' ', ], }, ]; # for boxart and rounded corners on node-borders: # upper left edge # upper right edge # lower right edge # lower left edge my $rounded_edges = [ '╭', '╮', '╯', '╰', ]; # for ASCII/boxart drawing slopes/slants # lower-left to upper right (repeated twice) # lower-right to upper left (repeated twice) my $slants = [ # ascii { solid => [ '/' , '\\' ], dotted => [ '.' , '.' ], dashed => [ '/ ', '\\ ' ], 'dot-dash' => [ './', '.\\' ], 'dot-dot-dash' => [ '../', '..\\' ], bold => [ '#' , '#' ], 'bold-dash' => [ '# ' , '# ' ], 'double' => [ '/' , '\\' ], 'double-dash' => [ '/ ' , '\\ ' ], wave => [ '/ ' , '\\ ' ], broad => [ '#' , '#' ], wide => [ '#' , '#' ], }, # boxart { solid => [ '╱' , '╲' ], dotted => [ '⋰' , '⋱' ], dashed => [ '╱ ', '╲ ' ], 'dot-dash' => [ '.╱', '.╲' ], 'dot-dot-dash' => [ '⋰╱', '⋱╲' ], bold => [ '#' , '#' ], 'bold-dash' => [ '# ' , '# ' ], 'double' => [ '╱' , '╲' ], 'double-dash' => [ '╱ ' , '╲ ' ], wave => [ '╱ ' , '╲ ' ], broad => [ '#' , '#' ], wide => [ '#' , '#' ], }, ]; # ASCII and box art: the different point shapes and styles my $point_shapes = [ { filled => { 'star' => '*', 'square' => '#', 'dot' => '.', 'circle' => 'o', # unfortunately, there is no filled o in ASCII 'cross' => '+', 'diamond' => '<>', 'x' => 'X', }, closed => { 'star' => '*', 'square' => '#', 'dot' => '.', 'circle' => 'o', 'cross' => '+', 'diamond' => '<>', 'x' => 'X', }, }, { filled => { 'star' => '★', 'square' => '■', 'dot' => '·', 'circle' => '●', 'cross' => '+', 'diamond' => '◆', 'x' => '╳', }, closed => { 'star' => '☆', 'square' => '□', 'dot' => '·', 'circle' => '○', 'cross' => '+', 'diamond' => '◇', 'x' => '╳', }, } ]; sub _point_style { my ($self, $shape, $style) = @_; return '' if $shape eq 'invisible'; if ($style =~ /^(star|square|dot|circle|cross|diamond)\z/) { # support the old "pointstyle: diamond" notion: $shape = $style; $style = 'filled'; } $style = 'filled' unless defined $style; my $g = $self->{graph}->{_ascii_style} || 0; $point_shapes->[$g]->{$style}->{$shape}; } sub _border_style { my ($self, $style, $type) = @_; # make a copy so that we can modify it my $g = $self->{graph}->{_ascii_style} || 0; my $s = [ @{ $border_styles->[ $g ]->{$style} } ]; die ("Unknown $type border style '$style'") if @$s == 0; my $shape = 'rect'; $shape = $self->attribute('shape') unless $self->isa_cell(); return $s unless $shape eq 'rounded'; # if shape: rounded, overlay the rounded edge pieces splice (@$s, 0, 4, @$rounded_edges) if $style =~ /^(solid|dotted|dashed|dot-dash|dot-dot-dash)\z/; # '####' => ' ### ' splice (@$s, 0, 4, (' ', ' ', ' ', ' ')) if $g == 0 || $style =~ /^(bold|wide|broad|double|double-dash|bold-dash)\z/; $s; } ############################################################################# # different arrow styles and shapes in ASCII and boxart my $arrow_form = { normal => 0, sleek => 1, # slightly squashed }; my $arrow_shapes = { triangle => 0, diamond => 1, box => 2, dot => 3, inv => 4, # an inverted triangle line => 5, cross => 6, x => 7, }; # todo: ≪ ≫ my $arrow_styles = [ [ # triangle { open => [ '>', '<', '^', 'v' ], closed => [ '>', '<', '^', 'v' ], filled => [ '>', '<', '^', 'v' ], }, { open => [ '>', '<', '∧', '∨' ], closed => [ '▷', '◁', '△', '▽' ], filled => [ '▶', '◀', '▲', '▼' ], } ], [ # diamond { open => [ '>', '<', '^', 'v' ], closed => [ '>', '<', '^', 'v' ], filled => [ '>', '<', '^', 'v' ], }, { open => [ '>', '<', '∧', '∨' ], closed => [ '◇', '◇', '◇', '◇' ], filled => [ '◆', '◆', '◆', '◆' ], } ], [ # box { open => [ ']', '[', '°', 'u' ], closed => [ 'D', 'D', 'D', 'D' ], filled => [ '#', '#', '#', '#' ], }, { open => [ '⊐', '⊐', '⊓', '⊔' ], closed => [ '◻', '◻', '◻', '◻' ], filled => [ '◼', '◼', '◼', '◼' ], } ], [ # dot { open => [ ')', '(', '^', 'u' ], closed => [ 'o', 'o', 'o', 'o' ], filled => [ '*', '*', '*', '*' ], }, { open => [ ')', '(', '◠', '◡' ], closed => [ '○', '○', '○', '○' ], filled => [ '●', '●', '●', '●' ], } ], [ # inv { open => [ '<', '>', 'v', '^' ], closed => [ '<', '>', 'v', '^' ], filled => [ '<', '>', 'v', '^' ], }, { open => [ '<', '>', '∨', '∧' ], closed => [ '◁', '▷', '▽', '△' ], filled => [ '◀', '▶', '▼', '▲' ], } ], [ # line { open => [ '|', '|', '_', '-' ], closed => [ '|', '|', '_', '-' ], filled => [ '|', '|', '_', '-' ], }, { open => [ '⎥', '⎢', '_', '¯' ], closed => [ '⎥', '⎢', '_', '¯' ], filled => [ '⎥', '⎢', '_', '¯' ], } ], [ # cross { open => [ '+', '+', '+', '+' ], closed => [ '+', '+', '+', '+' ], filled => [ '+', '+', '+', '+' ], }, { open => [ '┼', '┼', '┼', '┼' ], closed => [ '┼', '┼', '┼', '┼' ], filled => [ '┼', '┼', '┼', '┼' ], } ], [ # x { open => [ 'x', 'x', 'x', 'x' ], closed => [ 'x', 'x', 'x', 'x' ], filled => [ 'x', 'x', 'x', 'x' ], }, { open => [ 'x', 'x', 'x', 'x' ], closed => [ 'x', 'x', 'x', 'x' ], filled => [ '⧓', '⧓', 'x', 'x' ], } ] ]; sub _arrow { # return an arror, depending on style and direction my ($self, $style, $dir, $shape) = @_; $shape = '' unless defined $shape; $shape = $arrow_shapes->{$shape} || 0; my $g = $self->{graph}->{_ascii_style} || 0; $arrow_styles->[$shape]->[$g]->{$style}->[$dir]; } # To convert an HTML arrow to Unicode: my $arrow_dir = { '>' => 0, '<' => 1, '^' => 2, 'v' => 3, }; sub _unicode_arrow { # return an arror in unicode, depending on style and direction my ($self, $shape, $style, $arrow_text) = @_; $shape = '' unless defined $shape; $shape = $arrow_shapes->{$shape} || 0; my $dir = $arrow_dir->{$arrow_text} || 0; $arrow_styles->[$shape]->[1]->{$style}->[$dir]; } ############################################################################# # # +---4---4---4---+ # | | | | | # | | | | | # | | | | | # 1---0---3---0---2 1 = T to right, 2 = T to left, 3 T to top # | | | | 0 = cross, 4 = T shape # | | | | # | | | | # +---+ +---+ sub _draw_border { # draws a border into the framebuffer my ($self, $fb, $do_right, $do_bottom, $do_left, $do_top, $x, $y) = @_; return if $do_right.$do_left.$do_bottom.$do_top eq 'nonenonenonenone'; my $g = $self->{graph}; my $w = $self->{w}; if ($do_top ne 'none') { my $style = $self->_border_style($do_top, 'top'); # top-left corner piece is only there if we have a left border my $tl = $style->[0]; $tl = '' if $do_left eq 'none'; # generate the top border my $top = $style->[4] x (($self->{w}) / length($style->[4]) + 1); my $len = length($style->[4]); # for seamless rendering if (defined $x) { my $ofs = $x % $len; substr($top,0,$ofs) = '' if $ofs != 0; } # insert left upper corner (if it is there) substr($top,0,1) = $tl if $tl ne ''; $top = substr($top,0,$w) if length($top) > $w; # top-right corner piece is only there if we have a right border substr($top,-1,1) = $style->[1] if $do_right ne 'none'; # if the border must be collapsed, modify top-right edge piece: if ($self->{border_collapse_right}) { # place "4" (see drawing above) substr($top,-1,1) = $style->[10]; } # insert top row into FB $self->_printfb( $fb, 0,0, $top); } if ($do_bottom ne 'none') { my $style = $self->_border_style($do_bottom, 'bottom'); # bottom-left corner piece is only there if we have a left border my $bl = $style->[3]; $bl = '' if $do_left eq 'none'; # the bottom row '+--------+' etc my $bottom = $style->[5] x (($self->{w}) / length($style->[5]) + 1); my $len = length($style->[5]); # for seamless rendering if (defined $x) { my $ofs = $x % $len; substr($bottom,0,$ofs) = '' if $ofs != 0; } # insert left bottom corner (if it is there) substr($bottom,0,1) = $bl if $bl ne ''; $bottom = substr($bottom,0,$w) if length($bottom) > $w; # bottom-right corner piece is only there if we have a right border substr($bottom,-1,1) = $style->[2] if $do_right ne 'none'; # if the border must be collapsed, modify bottom-right edge piece: if ($self->{border_collapse_right} || $self->{border_collapse_bottom}) { if ($self->{rightbelow_count} > 0) { # place a cross or T piece (see drawing above) my $piece = 8; # cross # inverted T $piece = 11 if $self->{rightbelow_count} < 2 && !$self->{have_below}; $piece = 10 if $self->{rightbelow_count} < 2 && !$self->{have_right}; substr($bottom,-1,1) = $style->[$piece]; } } # insert bottom row into FB $self->_printfb( $fb, 0,$self->{h}-1, $bottom); } return if $do_right.$do_left eq 'nonenone'; # both none => done my $style = $self->_border_style($do_left, 'left'); my $left = $style->[6]; my $lc = scalar @{ $style->[6] } - 1; # count of characters $style = $self->_border_style($do_right, 'right'); my $right = $style->[7]; my $rc = scalar @{ $style->[7] } - 1; # count of characters my (@left, @right); my $l = 0; my $r = 0; # start with first character my $s = 1; $s = 0 if $do_top eq 'none'; my $h = $self->{h} - 2; $h ++ if defined $x && $do_bottom eq 'none'; # for seamless rendering for ($s..$h) { push @left, $left->[$l]; $l ++; $l = 0 if $l > $lc; push @right, $right->[$r]; $r ++; $r = 0 if $r > $rc; } # insert left/right columns into FB $self->_printfb( $fb, 0, $s, @left) unless $do_left eq 'none'; $self->_printfb( $fb, $w-1, $s, @right) unless $do_right eq 'none'; $self; } sub _draw_label { # Draw the node label into the framebuffer my ($self, $fb, $x, $y, $shape) = @_; if ($shape eq 'point') { # point-shaped nodes do not show their label in ASCII my $style = $self->attribute('pointstyle'); my $shape = $self->attribute('pointshape'); my $l = $self->_point_style($shape,$style); $self->_printfb_line ($fb, 2, $self->{h} - 2, $l) if $l; return; } # +---- # | Label # 2,1: ----^ my $w = $self->{w} - 4; my $xs = 2; my $h = $self->{h} - 2; my $ys = 0.5; my $border = $self->attribute('borderstyle'); if ($border eq 'none') { $w += 2; $h += 2; $xs = 1; $ys = 0; } my $align = $self->attribute('align'); $self->_printfb_aligned ($fb, $xs, $ys, $w, $h, $self->_aligned_label($align)); } sub as_ascii { # renders a node or edge like: # +--------+ .......... "" # | A node | or : A node : or " --> " # +--------+ .......... "" my ($self, $x,$y) = @_; my $shape = 'rect'; $shape = $self->attribute('shape') unless $self->isa_cell(); if ($shape eq 'edge') { my $edge = Graph::Easy::Edge->new(); my $cell = Graph::Easy::Edge::Cell->new( edge => $edge, x => $x, y => $y ); $cell->{w} = $self->{w}; $cell->{h} = $self->{h}; $cell->{att}->{label} = $self->label(); $cell->{type} = Graph::Easy::Edge::Cell->EDGE_HOR + Graph::Easy::Edge::Cell->EDGE_LABEL_CELL; return $cell->as_ascii(); } # invisible nodes, or very small ones return '' if $shape eq 'invisible' || $self->{w} == 0 || $self->{h} == 0; my $fb = $self->_framebuffer($self->{w}, $self->{h}); # point-shaped nodes do not have a border if ($shape ne 'point') { ######################################################################### # draw our border into the framebuffer my $cache = $self->{cache}; my $b_top = $cache->{top_border} || 'none'; my $b_left = $cache->{left_border} || 'none'; my $b_right = $cache->{right_border} || 'none'; my $b_bottom = $cache->{bottom_border} || 'none'; $self->_draw_border($fb, $b_right, $b_bottom, $b_left, $b_top); } ########################################################################### # "draw" the label into the framebuffer (e.g. the node/edge and the text) $self->_draw_label($fb, $x, $y, $shape); join ("\n", @$fb); } 1; __END__ =head1 NAME Graph::Easy::As_ascii - Generate ASCII art =head1 SYNOPSIS use Graph::Easy; my $graph = Graph::Easy->new(); $graph->add_edge('Bonn', 'Berlin'); print $graph->as_ascii(); =head1 DESCRIPTION C contains the code to render Nodes/Edges as ASCII art. It is used by Graph::Easy automatically, and there should be no need to use it directly. =head1 EXPORT Exports nothing. =head1 SEE ALSO L. =head1 AUTHOR Copyright (C) 2004 - 2007 by Tels L. See the LICENSE file for more details. =cut Graph-Easy-0.73/lib/Graph/Easy/Attributes.pm0000644000076400007640000037571312147675210020520 0ustar shlomifshlomif############################################################################# # Define and check attributes for a Graph::Easy textual description. # ############################################################################# package Graph::Easy::Attributes; $VERSION = '0.32'; package Graph::Easy; use strict; use utf8; # for examples like "Fähre" # to make it easier to remember the attribute names: my $att_aliases = { 'auto-label' => 'autolabel', 'auto-link' => 'autolink', 'auto-title' => 'autotitle', 'arrow-style' => 'arrowstyle', 'arrow-shape' => 'arrowshape', 'border-color' => 'bordercolor', 'border-style' => 'borderstyle', 'border-width' => 'borderwidth', 'font-size' => 'fontsize', 'label-color' => 'labelcolor', 'label-pos' => 'labelpos', 'text-style' => 'textstyle', 'text-wrap' => 'textwrap', 'point-style' => 'pointstyle', 'point-shape' => 'pointshape', }; sub _att_aliases { $att_aliases; } ############################################################################# # color handling # The W3C/SVG/CSS color scheme my $color_names = { w3c => { inherit => 'inherit', aliceblue => '#f0f8ff', antiquewhite => '#faebd7', aquamarine => '#7fffd4', aqua => '#00ffff', azure => '#f0ffff', beige => '#f5f5dc', bisque => '#ffe4c4', black => '#000000', blanchedalmond => '#ffebcd', blue => '#0000ff', blueviolet => '#8a2be2', brown => '#a52a2a', burlywood => '#deb887', cadetblue => '#5f9ea0', chartreuse => '#7fff00', chocolate => '#d2691e', coral => '#ff7f50', cornflowerblue => '#6495ed', cornsilk => '#fff8dc', crimson => '#dc143c', cyan => '#00ffff', darkblue => '#00008b', darkcyan => '#008b8b', darkgoldenrod => '#b8860b', darkgray => '#a9a9a9', darkgreen => '#006400', darkgrey => '#a9a9a9', darkkhaki => '#bdb76b', darkmagenta => '#8b008b', darkolivegreen => '#556b2f', darkorange => '#ff8c00', darkorchid => '#9932cc', darkred => '#8b0000', darksalmon => '#e9967a', darkseagreen => '#8fbc8f', darkslateblue => '#483d8b', darkslategray => '#2f4f4f', darkslategrey => '#2f4f4f', darkturquoise => '#00ced1', darkviolet => '#9400d3', deeppink => '#ff1493', deepskyblue => '#00bfff', dimgray => '#696969', dodgerblue => '#1e90ff', firebrick => '#b22222', floralwhite => '#fffaf0', forestgreen => '#228b22', fuchsia => '#ff00ff', gainsboro => '#dcdcdc', ghostwhite => '#f8f8ff', goldenrod => '#daa520', gold => '#ffd700', gray => '#808080', green => '#008000', greenyellow => '#adff2f', grey => '#808080', honeydew => '#f0fff0', hotpink => '#ff69b4', indianred => '#cd5c5c', indigo => '#4b0082', ivory => '#fffff0', khaki => '#f0e68c', lavenderblush => '#fff0f5', lavender => '#e6e6fa', lawngreen => '#7cfc00', lemonchiffon => '#fffacd', lightblue => '#add8e6', lightcoral => '#f08080', lightcyan => '#e0ffff', lightgoldenrodyellow => '#fafad2', lightgray => '#d3d3d3', lightgreen => '#90ee90', lightgrey => '#d3d3d3', lightpink => '#ffb6c1', lightsalmon => '#ffa07a', lightseagreen => '#20b2aa', lightskyblue => '#87cefa', lightslategray => '#778899', lightslategrey => '#778899', lightsteelblue => '#b0c4de', lightyellow => '#ffffe0', limegreen => '#32cd32', lime => '#00ff00', linen => '#faf0e6', magenta => '#ff00ff', maroon => '#800000', mediumaquamarine => '#66cdaa', mediumblue => '#0000cd', mediumorchid => '#ba55d3', mediumpurple => '#9370db', mediumseagreen => '#3cb371', mediumslateblue => '#7b68ee', mediumspringgreen => '#00fa9a', mediumturquoise => '#48d1cc', mediumvioletred => '#c71585', midnightblue => '#191970', mintcream => '#f5fffa', mistyrose => '#ffe4e1', moccasin => '#ffe4b5', navajowhite => '#ffdead', navy => '#000080', oldlace => '#fdf5e6', olivedrab => '#6b8e23', olive => '#808000', orangered => '#ff4500', orange => '#ffa500', orchid => '#da70d6', palegoldenrod => '#eee8aa', palegreen => '#98fb98', paleturquoise => '#afeeee', palevioletred => '#db7093', papayawhip => '#ffefd5', peachpuff => '#ffdab9', peru => '#cd853f', pink => '#ffc0cb', plum => '#dda0dd', powderblue => '#b0e0e6', purple => '#800080', red => '#ff0000', rosybrown => '#bc8f8f', royalblue => '#4169e1', saddlebrown => '#8b4513', salmon => '#fa8072', sandybrown => '#f4a460', seagreen => '#2e8b57', seashell => '#fff5ee', sienna => '#a0522d', silver => '#c0c0c0', skyblue => '#87ceeb', slateblue => '#6a5acd', slategray => '#708090', slategrey => '#708090', snow => '#fffafa', springgreen => '#00ff7f', steelblue => '#4682b4', tan => '#d2b48c', teal => '#008080', thistle => '#d8bfd8', tomato => '#ff6347', turquoise => '#40e0d0', violet => '#ee82ee', wheat => '#f5deb3', white => '#ffffff', whitesmoke => '#f5f5f5', yellowgreen => '#9acd32', yellow => '#ffff00', }, x11 => { inherit => 'inherit', aliceblue => '#f0f8ff', antiquewhite => '#faebd7', antiquewhite1 => '#ffefdb', antiquewhite2 => '#eedfcc', antiquewhite3 => '#cdc0b0', antiquewhite4 => '#8b8378', aquamarine => '#7fffd4', aquamarine1 => '#7fffd4', aquamarine2 => '#76eec6', aquamarine3 => '#66cdaa', aquamarine4 => '#458b74', azure => '#f0ffff', azure1 => '#f0ffff', azure2 => '#e0eeee', azure3 => '#c1cdcd', azure4 => '#838b8b', beige => '#f5f5dc', bisque => '#ffe4c4', bisque1 => '#ffe4c4', bisque2 => '#eed5b7', bisque3 => '#cdb79e', bisque4 => '#8b7d6b', black => '#000000', blanchedalmond => '#ffebcd', blue => '#0000ff', blue1 => '#0000ff', blue2 => '#0000ee', blue3 => '#0000cd', blue4 => '#00008b', blueviolet => '#8a2be2', brown => '#a52a2a', brown1 => '#ff4040', brown2 => '#ee3b3b', brown3 => '#cd3333', brown4 => '#8b2323', burlywood => '#deb887', burlywood1 => '#ffd39b', burlywood2 => '#eec591', burlywood3 => '#cdaa7d', burlywood4 => '#8b7355', cadetblue => '#5f9ea0', cadetblue1 => '#98f5ff', cadetblue2 => '#8ee5ee', cadetblue3 => '#7ac5cd', cadetblue4 => '#53868b', chartreuse => '#7fff00', chartreuse1 => '#7fff00', chartreuse2 => '#76ee00', chartreuse3 => '#66cd00', chartreuse4 => '#458b00', chocolate => '#d2691e', chocolate1 => '#ff7f24', chocolate2 => '#ee7621', chocolate3 => '#cd661d', chocolate4 => '#8b4513', coral => '#ff7f50', coral1 => '#ff7256', coral2 => '#ee6a50', coral3 => '#cd5b45', coral4 => '#8b3e2f', cornflowerblue => '#6495ed', cornsilk => '#fff8dc', cornsilk1 => '#fff8dc', cornsilk2 => '#eee8cd', cornsilk3 => '#cdc8b1', cornsilk4 => '#8b8878', crimson => '#dc143c', cyan => '#00ffff', cyan1 => '#00ffff', cyan2 => '#00eeee', cyan3 => '#00cdcd', cyan4 => '#008b8b', darkgoldenrod => '#b8860b', darkgoldenrod1 => '#ffb90f', darkgoldenrod2 => '#eead0e', darkgoldenrod3 => '#cd950c', darkgoldenrod4 => '#8b6508', darkgreen => '#006400', darkkhaki => '#bdb76b', darkolivegreen => '#556b2f', darkolivegreen1 => '#caff70', darkolivegreen2 => '#bcee68', darkolivegreen3 => '#a2cd5a', darkolivegreen4 => '#6e8b3d', darkorange => '#ff8c00', darkorange1 => '#ff7f00', darkorange2 => '#ee7600', darkorange3 => '#cd6600', darkorange4 => '#8b4500', darkorchid => '#9932cc', darkorchid1 => '#bf3eff', darkorchid2 => '#b23aee', darkorchid3 => '#9a32cd', darkorchid4 => '#68228b', darksalmon => '#e9967a', darkseagreen => '#8fbc8f', darkseagreen1 => '#c1ffc1', darkseagreen2 => '#b4eeb4', darkseagreen3 => '#9bcd9b', darkseagreen4 => '#698b69', darkslateblue => '#483d8b', darkslategray => '#2f4f4f', darkslategray1 => '#97ffff', darkslategray2 => '#8deeee', darkslategray3 => '#79cdcd', darkslategray4 => '#528b8b', darkslategrey => '#2f4f4f', darkturquoise => '#00ced1', darkviolet => '#9400d3', deeppink => '#ff1493', deeppink1 => '#ff1493', deeppink2 => '#ee1289', deeppink3 => '#cd1076', deeppink4 => '#8b0a50', deepskyblue => '#00bfff', deepskyblue1 => '#00bfff', deepskyblue2 => '#00b2ee', deepskyblue3 => '#009acd', deepskyblue4 => '#00688b', dimgray => '#696969', dimgrey => '#696969', dodgerblue => '#1e90ff', dodgerblue1 => '#1e90ff', dodgerblue2 => '#1c86ee', dodgerblue3 => '#1874cd', dodgerblue4 => '#104e8b', firebrick => '#b22222', firebrick1 => '#ff3030', firebrick2 => '#ee2c2c', firebrick3 => '#cd2626', firebrick4 => '#8b1a1a', floralwhite => '#fffaf0', forestgreen => '#228b22', gainsboro => '#dcdcdc', ghostwhite => '#f8f8ff', gold => '#ffd700', gold1 => '#ffd700', gold2 => '#eec900', gold3 => '#cdad00', gold4 => '#8b7500', goldenrod => '#daa520', goldenrod1 => '#ffc125', goldenrod2 => '#eeb422', goldenrod3 => '#cd9b1d', goldenrod4 => '#8b6914', gray => '#c0c0c0', gray0 => '#000000', gray1 => '#030303', gray2 => '#050505', gray3 => '#080808', gray4 => '#0a0a0a', gray5 => '#0d0d0d', gray6 => '#0f0f0f', gray7 => '#121212', gray8 => '#141414', gray9 => '#171717', gray10 => '#1a1a1a', gray11 => '#1c1c1c', gray12 => '#1f1f1f', gray13 => '#212121', gray14 => '#242424', gray15 => '#262626', gray16 => '#292929', gray17 => '#2b2b2b', gray18 => '#2e2e2e', gray19 => '#303030', gray20 => '#333333', gray21 => '#363636', gray22 => '#383838', gray23 => '#3b3b3b', gray24 => '#3d3d3d', gray25 => '#404040', gray26 => '#424242', gray27 => '#454545', gray28 => '#474747', gray29 => '#4a4a4a', gray30 => '#4d4d4d', gray31 => '#4f4f4f', gray32 => '#525252', gray33 => '#545454', gray34 => '#575757', gray35 => '#595959', gray36 => '#5c5c5c', gray37 => '#5e5e5e', gray38 => '#616161', gray39 => '#636363', gray40 => '#666666', gray41 => '#696969', gray42 => '#6b6b6b', gray43 => '#6e6e6e', gray44 => '#707070', gray45 => '#737373', gray46 => '#757575', gray47 => '#787878', gray48 => '#7a7a7a', gray49 => '#7d7d7d', gray50 => '#7f7f7f', gray51 => '#828282', gray52 => '#858585', gray53 => '#878787', gray54 => '#8a8a8a', gray55 => '#8c8c8c', gray56 => '#8f8f8f', gray57 => '#919191', gray58 => '#949494', gray59 => '#969696', gray60 => '#999999', gray61 => '#9c9c9c', gray62 => '#9e9e9e', gray63 => '#a1a1a1', gray64 => '#a3a3a3', gray65 => '#a6a6a6', gray66 => '#a8a8a8', gray67 => '#ababab', gray68 => '#adadad', gray69 => '#b0b0b0', gray70 => '#b3b3b3', gray71 => '#b5b5b5', gray72 => '#b8b8b8', gray73 => '#bababa', gray74 => '#bdbdbd', gray75 => '#bfbfbf', gray76 => '#c2c2c2', gray77 => '#c4c4c4', gray78 => '#c7c7c7', gray79 => '#c9c9c9', gray80 => '#cccccc', gray81 => '#cfcfcf', gray82 => '#d1d1d1', gray83 => '#d4d4d4', gray84 => '#d6d6d6', gray85 => '#d9d9d9', gray86 => '#dbdbdb', gray87 => '#dedede', gray88 => '#e0e0e0', gray89 => '#e3e3e3', gray90 => '#e5e5e5', gray91 => '#e8e8e8', gray92 => '#ebebeb', gray93 => '#ededed', gray94 => '#f0f0f0', gray95 => '#f2f2f2', gray96 => '#f5f5f5', gray97 => '#f7f7f7', gray98 => '#fafafa', gray99 => '#fcfcfc', gray100 => '#ffffff', green => '#00ff00', green1 => '#00ff00', green2 => '#00ee00', green3 => '#00cd00', green4 => '#008b00', greenyellow => '#adff2f', grey => '#c0c0c0', grey0 => '#000000', grey1 => '#030303', grey2 => '#050505', grey3 => '#080808', grey4 => '#0a0a0a', grey5 => '#0d0d0d', grey6 => '#0f0f0f', grey7 => '#121212', grey8 => '#141414', grey9 => '#171717', grey10 => '#1a1a1a', grey11 => '#1c1c1c', grey12 => '#1f1f1f', grey13 => '#212121', grey14 => '#242424', grey15 => '#262626', grey16 => '#292929', grey17 => '#2b2b2b', grey18 => '#2e2e2e', grey19 => '#303030', grey20 => '#333333', grey21 => '#363636', grey22 => '#383838', grey23 => '#3b3b3b', grey24 => '#3d3d3d', grey25 => '#404040', grey26 => '#424242', grey27 => '#454545', grey28 => '#474747', grey29 => '#4a4a4a', grey30 => '#4d4d4d', grey31 => '#4f4f4f', grey32 => '#525252', grey33 => '#545454', grey34 => '#575757', grey35 => '#595959', grey36 => '#5c5c5c', grey37 => '#5e5e5e', grey38 => '#616161', grey39 => '#636363', grey40 => '#666666', grey41 => '#696969', grey42 => '#6b6b6b', grey43 => '#6e6e6e', grey44 => '#707070', grey45 => '#737373', grey46 => '#757575', grey47 => '#787878', grey48 => '#7a7a7a', grey49 => '#7d7d7d', grey50 => '#7f7f7f', grey51 => '#828282', grey52 => '#858585', grey53 => '#878787', grey54 => '#8a8a8a', grey55 => '#8c8c8c', grey56 => '#8f8f8f', grey57 => '#919191', grey58 => '#949494', grey59 => '#969696', grey60 => '#999999', grey61 => '#9c9c9c', grey62 => '#9e9e9e', grey63 => '#a1a1a1', grey64 => '#a3a3a3', grey65 => '#a6a6a6', grey66 => '#a8a8a8', grey67 => '#ababab', grey68 => '#adadad', grey69 => '#b0b0b0', grey70 => '#b3b3b3', grey71 => '#b5b5b5', grey72 => '#b8b8b8', grey73 => '#bababa', grey74 => '#bdbdbd', grey75 => '#bfbfbf', grey76 => '#c2c2c2', grey77 => '#c4c4c4', grey78 => '#c7c7c7', grey79 => '#c9c9c9', grey80 => '#cccccc', grey81 => '#cfcfcf', grey82 => '#d1d1d1', grey83 => '#d4d4d4', grey84 => '#d6d6d6', grey85 => '#d9d9d9', grey86 => '#dbdbdb', grey87 => '#dedede', grey88 => '#e0e0e0', grey89 => '#e3e3e3', grey90 => '#e5e5e5', grey91 => '#e8e8e8', grey92 => '#ebebeb', grey93 => '#ededed', grey94 => '#f0f0f0', grey95 => '#f2f2f2', grey96 => '#f5f5f5', grey97 => '#f7f7f7', grey98 => '#fafafa', grey99 => '#fcfcfc', grey100 => '#ffffff', honeydew => '#f0fff0', honeydew1 => '#f0fff0', honeydew2 => '#e0eee0', honeydew3 => '#c1cdc1', honeydew4 => '#838b83', hotpink => '#ff69b4', hotpink1 => '#ff6eb4', hotpink2 => '#ee6aa7', hotpink3 => '#cd6090', hotpink4 => '#8b3a62', indianred => '#cd5c5c', indianred1 => '#ff6a6a', indianred2 => '#ee6363', indianred3 => '#cd5555', indianred4 => '#8b3a3a', indigo => '#4b0082', ivory => '#fffff0', ivory1 => '#fffff0', ivory2 => '#eeeee0', ivory3 => '#cdcdc1', ivory4 => '#8b8b83', khaki => '#f0e68c', khaki1 => '#fff68f', khaki2 => '#eee685', khaki3 => '#cdc673', khaki4 => '#8b864e', lavender => '#e6e6fa', lavenderblush => '#fff0f5', lavenderblush1 => '#fff0f5', lavenderblush2 => '#eee0e5', lavenderblush3 => '#cdc1c5', lavenderblush4 => '#8b8386', lawngreen => '#7cfc00', lemonchiffon => '#fffacd', lemonchiffon1 => '#fffacd', lemonchiffon2 => '#eee9bf', lemonchiffon3 => '#cdc9a5', lemonchiffon4 => '#8b8970', lightblue => '#add8e6', lightblue1 => '#bfefff', lightblue2 => '#b2dfee', lightblue3 => '#9ac0cd', lightblue4 => '#68838b', lightcoral => '#f08080', lightcyan => '#e0ffff', lightcyan1 => '#e0ffff', lightcyan2 => '#d1eeee', lightcyan3 => '#b4cdcd', lightcyan4 => '#7a8b8b', lightgoldenrod => '#eedd82', lightgoldenrod1 => '#ffec8b', lightgoldenrod2 => '#eedc82', lightgoldenrod3 => '#cdbe70', lightgoldenrod4 => '#8b814c', lightgoldenrodyellow => '#fafad2', lightgray => '#d3d3d3', lightgrey => '#d3d3d3', lightpink => '#ffb6c1', lightpink1 => '#ffaeb9', lightpink2 => '#eea2ad', lightpink3 => '#cd8c95', lightpink4 => '#8b5f65', lightsalmon => '#ffa07a', lightsalmon1 => '#ffa07a', lightsalmon2 => '#ee9572', lightsalmon3 => '#cd8162', lightsalmon4 => '#8b5742', lightseagreen => '#20b2aa', lightskyblue => '#87cefa', lightskyblue1 => '#b0e2ff', lightskyblue2 => '#a4d3ee', lightskyblue3 => '#8db6cd', lightskyblue4 => '#607b8b', lightslateblue => '#8470ff', lightslategray => '#778899', lightslategrey => '#778899', lightsteelblue => '#b0c4de', lightsteelblue1 => '#cae1ff', lightsteelblue2 => '#bcd2ee', lightsteelblue3 => '#a2b5cd', lightsteelblue4 => '#6e7b8b', lightyellow => '#ffffe0', lightyellow1 => '#ffffe0', lightyellow2 => '#eeeed1', lightyellow3 => '#cdcdb4', lightyellow4 => '#8b8b7a', limegreen => '#32cd32', linen => '#faf0e6', magenta => '#ff00ff', magenta1 => '#ff00ff', magenta2 => '#ee00ee', magenta3 => '#cd00cd', magenta4 => '#8b008b', maroon => '#b03060', maroon1 => '#ff34b3', maroon2 => '#ee30a7', maroon3 => '#cd2990', maroon4 => '#8b1c62', mediumaquamarine => '#66cdaa', mediumblue => '#0000cd', mediumorchid => '#ba55d3', mediumorchid1 => '#e066ff', mediumorchid2 => '#d15fee', mediumorchid3 => '#b452cd', mediumorchid4 => '#7a378b', mediumpurple => '#9370db', mediumpurple1 => '#ab82ff', mediumpurple2 => '#9f79ee', mediumpurple3 => '#8968cd', mediumpurple4 => '#5d478b', mediumseagreen => '#3cb371', mediumslateblue => '#7b68ee', mediumspringgreen => '#00fa9a', mediumturquoise => '#48d1cc', mediumvioletred => '#c71585', midnightblue => '#191970', mintcream => '#f5fffa', mistyrose => '#ffe4e1', mistyrose1 => '#ffe4e1', mistyrose2 => '#eed5d2', mistyrose3 => '#cdb7b5', mistyrose4 => '#8b7d7b', moccasin => '#ffe4b5', navajowhite => '#ffdead', navajowhite1 => '#ffdead', navajowhite2 => '#eecfa1', navajowhite3 => '#cdb38b', navajowhite4 => '#8b795e', navy => '#000080', navyblue => '#000080', oldlace => '#fdf5e6', olivedrab => '#6b8e23', olivedrab1 => '#c0ff3e', olivedrab2 => '#b3ee3a', olivedrab3 => '#9acd32', olivedrab4 => '#698b22', orange => '#ffa500', orange1 => '#ffa500', orange2 => '#ee9a00', orange3 => '#cd8500', orange4 => '#8b5a00', orangered => '#ff4500', orangered1 => '#ff4500', orangered2 => '#ee4000', orangered3 => '#cd3700', orangered4 => '#8b2500', orchid => '#da70d6', orchid1 => '#ff83fa', orchid2 => '#ee7ae9', orchid3 => '#cd69c9', orchid4 => '#8b4789', palegoldenrod => '#eee8aa', palegreen => '#98fb98', palegreen1 => '#9aff9a', palegreen2 => '#90ee90', palegreen3 => '#7ccd7c', palegreen4 => '#548b54', paleturquoise => '#afeeee', paleturquoise1 => '#bbffff', paleturquoise2 => '#aeeeee', paleturquoise3 => '#96cdcd', paleturquoise4 => '#668b8b', palevioletred => '#db7093', palevioletred1 => '#ff82ab', palevioletred2 => '#ee799f', palevioletred3 => '#cd6889', palevioletred4 => '#8b475d', papayawhip => '#ffefd5', peachpuff => '#ffdab9', peachpuff1 => '#ffdab9', peachpuff2 => '#eecbad', peachpuff3 => '#cdaf95', peachpuff4 => '#8b7765', peru => '#cd853f', pink => '#ffc0cb', pink1 => '#ffb5c5', pink2 => '#eea9b8', pink3 => '#cd919e', pink4 => '#8b636c', plum => '#dda0dd', plum1 => '#ffbbff', plum2 => '#eeaeee', plum3 => '#cd96cd', plum4 => '#8b668b', powderblue => '#b0e0e6', purple => '#a020f0', purple1 => '#9b30ff', purple2 => '#912cee', purple3 => '#7d26cd', purple4 => '#551a8b', red => '#ff0000', red1 => '#ff0000', red2 => '#ee0000', red3 => '#cd0000', red4 => '#8b0000', rosybrown => '#bc8f8f', rosybrown1 => '#ffc1c1', rosybrown2 => '#eeb4b4', rosybrown3 => '#cd9b9b', rosybrown4 => '#8b6969', royalblue => '#4169e1', royalblue1 => '#4876ff', royalblue2 => '#436eee', royalblue3 => '#3a5fcd', royalblue4 => '#27408b', saddlebrown => '#8b4513', salmon => '#fa8072', salmon1 => '#ff8c69', salmon2 => '#ee8262', salmon3 => '#cd7054', salmon4 => '#8b4c39', sandybrown => '#f4a460', seagreen => '#2e8b57', seagreen1 => '#54ff9f', seagreen2 => '#4eee94', seagreen3 => '#43cd80', seagreen4 => '#2e8b57', seashell => '#fff5ee', seashell1 => '#fff5ee', seashell2 => '#eee5de', seashell3 => '#cdc5bf', seashell4 => '#8b8682', sienna => '#a0522d', sienna1 => '#ff8247', sienna2 => '#ee7942', sienna3 => '#cd6839', sienna4 => '#8b4726', skyblue => '#87ceeb', skyblue1 => '#87ceff', skyblue2 => '#7ec0ee', skyblue3 => '#6ca6cd', skyblue4 => '#4a708b', slateblue => '#6a5acd', slateblue1 => '#836fff', slateblue2 => '#7a67ee', slateblue3 => '#6959cd', slateblue4 => '#473c8b', slategray => '#708090', slategray1 => '#c6e2ff', slategray2 => '#b9d3ee', slategray3 => '#9fb6cd', slategray4 => '#6c7b8b', slategrey => '#708090', snow => '#fffafa', snow1 => '#fffafa', snow2 => '#eee9e9', snow3 => '#cdc9c9', snow4 => '#8b8989', springgreen => '#00ff7f', springgreen1 => '#00ff7f', springgreen2 => '#00ee76', springgreen3 => '#00cd66', springgreen4 => '#008b45', steelblue => '#4682b4', steelblue1 => '#63b8ff', steelblue2 => '#5cacee', steelblue3 => '#4f94cd', steelblue4 => '#36648b', tan => '#d2b48c', tan1 => '#ffa54f', tan2 => '#ee9a49', tan3 => '#cd853f', tan4 => '#8b5a2b', thistle => '#d8bfd8', thistle1 => '#ffe1ff', thistle2 => '#eed2ee', thistle3 => '#cdb5cd', thistle4 => '#8b7b8b', tomato => '#ff6347', tomato1 => '#ff6347', tomato2 => '#ee5c42', tomato3 => '#cd4f39', tomato4 => '#8b3626', transparent => '#fffffe', turquoise => '#40e0d0', turquoise1 => '#00f5ff', turquoise2 => '#00e5ee', turquoise3 => '#00c5cd', turquoise4 => '#00868b', violet => '#ee82ee', violetred => '#d02090', violetred1 => '#ff3e96', violetred2 => '#ee3a8c', violetred3 => '#cd3278', violetred4 => '#8b2252', wheat => '#f5deb3', wheat1 => '#ffe7ba', wheat2 => '#eed8ae', wheat3 => '#cdba96', wheat4 => '#8b7e66', white => '#ffffff', whitesmoke => '#f5f5f5', yellow => '#ffff00', yellow1 => '#ffff00', yellow2 => '#eeee00', yellow3 => '#cdcd00', yellow4 => '#8b8b00', yellowgreen => '#9acd32', # The following 12 colors exist here so that a "color: 3; colorscheme: accent3" # will not report an "unknown color 3" from the Parser. As a side-effect # you will not get an error for a plain "color: 3". 1 => '#a6cee3', 2 => '#1f78b4', 3 => '#b2df8a', 4 => '#33a02c', 5 => '#fb9a99', 6 => '#e31a1c', 7 => '#fdbf6f', 8 => '#ff7f00', 9 => '#cab2d6', 10 => '#6a3d9a', 11 => '#ffff99', 12 => '#b15928', }, # The following color specifications were developed by: # Cynthia Brewer (http://colorbrewer.org/) # See the LICENSE FILE for the full license that applies to them. accent3 => { 1 => '#7fc97f', 2 => '#beaed4', 3 => '#fdc086', }, accent4 => { 1 => '#7fc97f', 2 => '#beaed4', 3 => '#fdc086', 4 => '#ffff99', }, accent5 => { 1 => '#7fc97f', 2 => '#beaed4', 3 => '#fdc086', 4 => '#ffff99', 5 => '#386cb0', }, accent6 => { 1 => '#7fc97f', 2 => '#beaed4', 3 => '#fdc086', 4 => '#ffff99', 5 => '#386cb0', 6 => '#f0027f', }, accent7 => { 1 => '#7fc97f', 2 => '#beaed4', 3 => '#fdc086', 4 => '#ffff99', 5 => '#386cb0', 6 => '#f0027f', 7 => '#bf5b17', }, accent8 => { 1 => '#7fc97f', 2 => '#beaed4', 3 => '#fdc086', 4 => '#ffff99', 5 => '#386cb0', 6 => '#f0027f', 7 => '#bf5b17', 8 => '#666666', }, blues3 => { 1 => '#deebf7', 2 => '#9ecae1', 3 => '#3182bd', }, blues4 => { 1 => '#eff3ff', 2 => '#bdd7e7', 3 => '#6baed6', 4 => '#2171b5', }, blues5 => { 1 => '#eff3ff', 2 => '#bdd7e7', 3 => '#6baed6', 4 => '#3182bd', 5 => '#08519c', }, blues6 => { 1 => '#eff3ff', 2 => '#c6dbef', 3 => '#9ecae1', 4 => '#6baed6', 5 => '#3182bd', 6 => '#08519c', }, blues7 => { 1 => '#eff3ff', 2 => '#c6dbef', 3 => '#9ecae1', 4 => '#6baed6', 5 => '#4292c6', 6 => '#2171b5', 7 => '#084594', }, blues8 => { 1 => '#f7fbff', 2 => '#deebf7', 3 => '#c6dbef', 4 => '#9ecae1', 5 => '#6baed6', 6 => '#4292c6', 7 => '#2171b5', 8 => '#084594', }, blues9 => { 1 => '#f7fbff', 2 => '#deebf7', 3 => '#c6dbef', 4 => '#9ecae1', 5 => '#6baed6', 6 => '#4292c6', 7 => '#2171b5', 8 => '#08519c', 9 => '#08306b', }, brbg3 => { 1 => '#d8b365', 2 => '#f5f5f5', 3 => '#5ab4ac', }, brbg4 => { 1 => '#a6611a', 2 => '#dfc27d', 3 => '#80cdc1', 4 => '#018571', }, brbg5 => { 1 => '#a6611a', 2 => '#dfc27d', 3 => '#f5f5f5', 4 => '#80cdc1', 5 => '#018571', }, brbg6 => { 1 => '#8c510a', 2 => '#d8b365', 3 => '#f6e8c3', 4 => '#c7eae5', 5 => '#5ab4ac', 6 => '#01665e', }, brbg7 => { 1 => '#8c510a', 2 => '#d8b365', 3 => '#f6e8c3', 4 => '#f5f5f5', 5 => '#c7eae5', 6 => '#5ab4ac', 7 => '#01665e', }, brbg8 => { 1 => '#8c510a', 2 => '#bf812d', 3 => '#dfc27d', 4 => '#f6e8c3', 5 => '#c7eae5', 6 => '#80cdc1', 7 => '#35978f', 8 => '#01665e', }, brbg9 => { 1 => '#8c510a', 2 => '#bf812d', 3 => '#dfc27d', 4 => '#f6e8c3', 5 => '#f5f5f5', 6 => '#c7eae5', 7 => '#80cdc1', 8 => '#35978f', 9 => '#01665e', }, brbg10 => { 1 => '#543005', 2 => '#8c510a', 3 => '#bf812d', 4 => '#dfc27d', 5 => '#f6e8c3', 6 => '#c7eae5', 7 => '#80cdc1', 8 => '#35978f', 9 => '#01665e', 10 => '#003c30', }, brbg11 => { 1 => '#543005', 2 => '#8c510a', 3 => '#bf812d', 4 => '#dfc27d', 5 => '#f6e8c3', 6 => '#f5f5f5', 7 => '#c7eae5', 8 => '#80cdc1', 9 => '#35978f', 10 => '#01665e', 11 => '#003c30', }, bugn3 => { 1 => '#e5f5f9', 2 => '#99d8c9', 3 => '#2ca25f', }, bugn4 => { 1 => '#edf8fb', 2 => '#b2e2e2', 3 => '#66c2a4', 4 => '#238b45', }, bugn5 => { 1 => '#edf8fb', 2 => '#b2e2e2', 3 => '#66c2a4', 4 => '#2ca25f', 5 => '#006d2c', }, bugn6 => { 1 => '#edf8fb', 2 => '#ccece6', 3 => '#99d8c9', 4 => '#66c2a4', 5 => '#2ca25f', 6 => '#006d2c', }, bugn7 => { 1 => '#edf8fb', 2 => '#ccece6', 3 => '#99d8c9', 4 => '#66c2a4', 5 => '#41ae76', 6 => '#238b45', 7 => '#005824', }, bugn8 => { 1 => '#f7fcfd', 2 => '#e5f5f9', 3 => '#ccece6', 4 => '#99d8c9', 5 => '#66c2a4', 6 => '#41ae76', 7 => '#238b45', 8 => '#005824', }, bugn9 => { 1 => '#f7fcfd', 2 => '#e5f5f9', 3 => '#ccece6', 4 => '#99d8c9', 5 => '#66c2a4', 6 => '#41ae76', 7 => '#238b45', 8 => '#006d2c', 9 => '#00441b', }, bupu3 => { 1 => '#e0ecf4', 2 => '#9ebcda', 3 => '#8856a7', }, bupu4 => { 1 => '#edf8fb', 2 => '#b3cde3', 3 => '#8c96c6', 4 => '#88419d', }, bupu5 => { 1 => '#edf8fb', 2 => '#b3cde3', 3 => '#8c96c6', 4 => '#8856a7', 5 => '#810f7c', }, bupu6 => { 1 => '#edf8fb', 2 => '#bfd3e6', 3 => '#9ebcda', 4 => '#8c96c6', 5 => '#8856a7', 6 => '#810f7c', }, bupu7 => { 1 => '#edf8fb', 2 => '#bfd3e6', 3 => '#9ebcda', 4 => '#8c96c6', 5 => '#8c6bb1', 6 => '#88419d', 7 => '#6e016b', }, bupu8 => { 1 => '#f7fcfd', 2 => '#e0ecf4', 3 => '#bfd3e6', 4 => '#9ebcda', 5 => '#8c96c6', 6 => '#8c6bb1', 7 => '#88419d', 8 => '#6e016b', }, bupu9 => { 1 => '#f7fcfd', 2 => '#e0ecf4', 3 => '#bfd3e6', 4 => '#9ebcda', 5 => '#8c96c6', 6 => '#8c6bb1', 7 => '#88419d', 8 => '#810f7c', 9 => '#4d004b', }, dark23 => { 1 => '#1b9e77', 2 => '#d95f02', 3 => '#7570b3', }, dark24 => { 1 => '#1b9e77', 2 => '#d95f02', 3 => '#7570b3', 4 => '#e7298a', }, dark25 => { 1 => '#1b9e77', 2 => '#d95f02', 3 => '#7570b3', 4 => '#e7298a', 5 => '#66a61e', }, dark26 => { 1 => '#1b9e77', 2 => '#d95f02', 3 => '#7570b3', 4 => '#e7298a', 5 => '#66a61e', 6 => '#e6ab02', }, dark27 => { 1 => '#1b9e77', 2 => '#d95f02', 3 => '#7570b3', 4 => '#e7298a', 5 => '#66a61e', 6 => '#e6ab02', 7 => '#a6761d', }, dark28 => { 1 => '#1b9e77', 2 => '#d95f02', 3 => '#7570b3', 4 => '#e7298a', 5 => '#66a61e', 6 => '#e6ab02', 7 => '#a6761d', 8 => '#666666', }, gnbu3 => { 1 => '#e0f3db', 2 => '#a8ddb5', 3 => '#43a2ca', }, gnbu4 => { 1 => '#f0f9e8', 2 => '#bae4bc', 3 => '#7bccc4', 4 => '#2b8cbe', }, gnbu5 => { 1 => '#f0f9e8', 2 => '#bae4bc', 3 => '#7bccc4', 4 => '#43a2ca', 5 => '#0868ac', }, gnbu6 => { 1 => '#f0f9e8', 2 => '#ccebc5', 3 => '#a8ddb5', 4 => '#7bccc4', 5 => '#43a2ca', 6 => '#0868ac', }, gnbu7 => { 1 => '#f0f9e8', 2 => '#ccebc5', 3 => '#a8ddb5', 4 => '#7bccc4', 5 => '#4eb3d3', 6 => '#2b8cbe', 7 => '#08589e', }, gnbu8 => { 1 => '#f7fcf0', 2 => '#e0f3db', 3 => '#ccebc5', 4 => '#a8ddb5', 5 => '#7bccc4', 6 => '#4eb3d3', 7 => '#2b8cbe', 8 => '#08589e', }, gnbu9 => { 1 => '#f7fcf0', 2 => '#e0f3db', 3 => '#ccebc5', 4 => '#a8ddb5', 5 => '#7bccc4', 6 => '#4eb3d3', 7 => '#2b8cbe', 8 => '#0868ac', 9 => '#084081', }, greens3 => { 1 => '#e5f5e0', 2 => '#a1d99b', 3 => '#31a354', }, greens4 => { 1 => '#edf8e9', 2 => '#bae4b3', 3 => '#74c476', 4 => '#238b45', }, greens5 => { 1 => '#edf8e9', 2 => '#bae4b3', 3 => '#74c476', 4 => '#31a354', 5 => '#006d2c', }, greens6 => { 1 => '#edf8e9', 2 => '#c7e9c0', 3 => '#a1d99b', 4 => '#74c476', 5 => '#31a354', 6 => '#006d2c', }, greens7 => { 1 => '#edf8e9', 2 => '#c7e9c0', 3 => '#a1d99b', 4 => '#74c476', 5 => '#41ab5d', 6 => '#238b45', 7 => '#005a32', }, greens8 => { 1 => '#f7fcf5', 2 => '#e5f5e0', 3 => '#c7e9c0', 4 => '#a1d99b', 5 => '#74c476', 6 => '#41ab5d', 7 => '#238b45', 8 => '#005a32', }, greens9 => { 1 => '#f7fcf5', 2 => '#e5f5e0', 3 => '#c7e9c0', 4 => '#a1d99b', 5 => '#74c476', 6 => '#41ab5d', 7 => '#238b45', 8 => '#006d2c', 9 => '#00441b', }, greys3 => { 1 => '#f0f0f0', 2 => '#bdbdbd', 3 => '#636363', }, greys4 => { 1 => '#f7f7f7', 2 => '#cccccc', 3 => '#969696', 4 => '#525252', }, greys5 => { 1 => '#f7f7f7', 2 => '#cccccc', 3 => '#969696', 4 => '#636363', 5 => '#252525', }, greys6 => { 1 => '#f7f7f7', 2 => '#d9d9d9', 3 => '#bdbdbd', 4 => '#969696', 5 => '#636363', 6 => '#252525', }, greys7 => { 1 => '#f7f7f7', 2 => '#d9d9d9', 3 => '#bdbdbd', 4 => '#969696', 5 => '#737373', 6 => '#525252', 7 => '#252525', }, greys8 => { 1 => '#ffffff', 2 => '#f0f0f0', 3 => '#d9d9d9', 4 => '#bdbdbd', 5 => '#969696', 6 => '#737373', 7 => '#525252', 8 => '#252525', }, greys9 => { 1 => '#ffffff', 2 => '#f0f0f0', 3 => '#d9d9d9', 4 => '#bdbdbd', 5 => '#969696', 6 => '#737373', 7 => '#525252', 8 => '#252525', 9 => '#000000', }, oranges3 => { 1 => '#fee6ce', 2 => '#fdae6b', 3 => '#e6550d', }, oranges4 => { 1 => '#feedde', 2 => '#fdbe85', 3 => '#fd8d3c', 4 => '#d94701', }, oranges5 => { 1 => '#feedde', 2 => '#fdbe85', 3 => '#fd8d3c', 4 => '#e6550d', 5 => '#a63603', }, oranges6 => { 1 => '#feedde', 2 => '#fdd0a2', 3 => '#fdae6b', 4 => '#fd8d3c', 5 => '#e6550d', 6 => '#a63603', }, oranges7 => { 1 => '#feedde', 2 => '#fdd0a2', 3 => '#fdae6b', 4 => '#fd8d3c', 5 => '#f16913', 6 => '#d94801', 7 => '#8c2d04', }, oranges8 => { 1 => '#fff5eb', 2 => '#fee6ce', 3 => '#fdd0a2', 4 => '#fdae6b', 5 => '#fd8d3c', 6 => '#f16913', 7 => '#d94801', 8 => '#8c2d04', }, oranges9 => { 1 => '#fff5eb', 2 => '#fee6ce', 3 => '#fdd0a2', 4 => '#fdae6b', 5 => '#fd8d3c', 6 => '#f16913', 7 => '#d94801', 8 => '#a63603', 9 => '#7f2704', }, orrd3 => { 1 => '#fee8c8', 2 => '#fdbb84', 3 => '#e34a33', }, orrd4 => { 1 => '#fef0d9', 2 => '#fdcc8a', 3 => '#fc8d59', 4 => '#d7301f', }, orrd5 => { 1 => '#fef0d9', 2 => '#fdcc8a', 3 => '#fc8d59', 4 => '#e34a33', 5 => '#b30000', }, orrd6 => { 1 => '#fef0d9', 2 => '#fdd49e', 3 => '#fdbb84', 4 => '#fc8d59', 5 => '#e34a33', 6 => '#b30000', }, orrd7 => { 1 => '#fef0d9', 2 => '#fdd49e', 3 => '#fdbb84', 4 => '#fc8d59', 5 => '#ef6548', 6 => '#d7301f', 7 => '#990000', }, orrd8 => { 1 => '#fff7ec', 2 => '#fee8c8', 3 => '#fdd49e', 4 => '#fdbb84', 5 => '#fc8d59', 6 => '#ef6548', 7 => '#d7301f', 8 => '#990000', }, orrd9 => { 1 => '#fff7ec', 2 => '#fee8c8', 3 => '#fdd49e', 4 => '#fdbb84', 5 => '#fc8d59', 6 => '#ef6548', 7 => '#d7301f', 8 => '#b30000', 9 => '#7f0000', }, paired3 => { 1 => '#a6cee3', 2 => '#1f78b4', 3 => '#b2df8a', }, paired4 => { 1 => '#a6cee3', 2 => '#1f78b4', 3 => '#b2df8a', 4 => '#33a02c', }, paired5 => { 1 => '#a6cee3', 2 => '#1f78b4', 3 => '#b2df8a', 4 => '#33a02c', 5 => '#fb9a99', }, paired6 => { 1 => '#a6cee3', 2 => '#1f78b4', 3 => '#b2df8a', 4 => '#33a02c', 5 => '#fb9a99', 6 => '#e31a1c', }, paired7 => { 1 => '#a6cee3', 2 => '#1f78b4', 3 => '#b2df8a', 4 => '#33a02c', 5 => '#fb9a99', 6 => '#e31a1c', 7 => '#fdbf6f', }, paired8 => { 1 => '#a6cee3', 2 => '#1f78b4', 3 => '#b2df8a', 4 => '#33a02c', 5 => '#fb9a99', 6 => '#e31a1c', 7 => '#fdbf6f', 8 => '#ff7f00', }, paired9 => { 1 => '#a6cee3', 2 => '#1f78b4', 3 => '#b2df8a', 4 => '#33a02c', 5 => '#fb9a99', 6 => '#e31a1c', 7 => '#fdbf6f', 8 => '#ff7f00', 9 => '#cab2d6', }, paired10 => { 1 => '#a6cee3', 2 => '#1f78b4', 3 => '#b2df8a', 4 => '#33a02c', 5 => '#fb9a99', 6 => '#e31a1c', 7 => '#fdbf6f', 8 => '#ff7f00', 9 => '#cab2d6', 10 => '#6a3d9a', }, paired11 => { 1 => '#a6cee3', 2 => '#1f78b4', 3 => '#b2df8a', 4 => '#33a02c', 5 => '#fb9a99', 6 => '#e31a1c', 7 => '#fdbf6f', 8 => '#ff7f00', 9 => '#cab2d6', 10 => '#6a3d9a', 11 => '#ffff99', }, paired12 => { 1 => '#a6cee3', 2 => '#1f78b4', 3 => '#b2df8a', 4 => '#33a02c', 5 => '#fb9a99', 6 => '#e31a1c', 7 => '#fdbf6f', 8 => '#ff7f00', 9 => '#cab2d6', 10 => '#6a3d9a', 11 => '#ffff99', 12 => '#b15928', }, pastel13 => { 1 => '#fbb4ae', 2 => '#b3cde3', 3 => '#ccebc5', }, pastel14 => { 1 => '#fbb4ae', 2 => '#b3cde3', 3 => '#ccebc5', 4 => '#decbe4', }, pastel15 => { 1 => '#fbb4ae', 2 => '#b3cde3', 3 => '#ccebc5', 4 => '#decbe4', 5 => '#fed9a6', }, pastel16 => { 1 => '#fbb4ae', 2 => '#b3cde3', 3 => '#ccebc5', 4 => '#decbe4', 5 => '#fed9a6', 6 => '#ffffcc', }, pastel17 => { 1 => '#fbb4ae', 2 => '#b3cde3', 3 => '#ccebc5', 4 => '#decbe4', 5 => '#fed9a6', 6 => '#ffffcc', 7 => '#e5d8bd', }, pastel18 => { 1 => '#fbb4ae', 2 => '#b3cde3', 3 => '#ccebc5', 4 => '#decbe4', 5 => '#fed9a6', 6 => '#ffffcc', 7 => '#e5d8bd', 8 => '#fddaec', }, pastel19 => { 1 => '#fbb4ae', 2 => '#b3cde3', 3 => '#ccebc5', 4 => '#decbe4', 5 => '#fed9a6', 6 => '#ffffcc', 7 => '#e5d8bd', 8 => '#fddaec', 9 => '#f2f2f2', }, pastel23 => { 1 => '#b3e2cd', 2 => '#fdcdac', 3 => '#cbd5e8', }, pastel24 => { 1 => '#b3e2cd', 2 => '#fdcdac', 3 => '#cbd5e8', 4 => '#f4cae4', }, pastel25 => { 1 => '#b3e2cd', 2 => '#fdcdac', 3 => '#cbd5e8', 4 => '#f4cae4', 5 => '#e6f5c9', }, pastel26 => { 1 => '#b3e2cd', 2 => '#fdcdac', 3 => '#cbd5e8', 4 => '#f4cae4', 5 => '#e6f5c9', 6 => '#fff2ae', }, pastel27 => { 1 => '#b3e2cd', 2 => '#fdcdac', 3 => '#cbd5e8', 4 => '#f4cae4', 5 => '#e6f5c9', 6 => '#fff2ae', 7 => '#f1e2cc', }, pastel28 => { 1 => '#b3e2cd', 2 => '#fdcdac', 3 => '#cbd5e8', 4 => '#f4cae4', 5 => '#e6f5c9', 6 => '#fff2ae', 7 => '#f1e2cc', 8 => '#cccccc', }, piyg3 => { 1 => '#e9a3c9', 2 => '#f7f7f7', 3 => '#a1d76a', }, piyg4 => { 1 => '#d01c8b', 2 => '#f1b6da', 3 => '#b8e186', 4 => '#4dac26', }, piyg5 => { 1 => '#d01c8b', 2 => '#f1b6da', 3 => '#f7f7f7', 4 => '#b8e186', 5 => '#4dac26', }, piyg6 => { 1 => '#c51b7d', 2 => '#e9a3c9', 3 => '#fde0ef', 4 => '#e6f5d0', 5 => '#a1d76a', 6 => '#4d9221', }, piyg7 => { 1 => '#c51b7d', 2 => '#e9a3c9', 3 => '#fde0ef', 4 => '#f7f7f7', 5 => '#e6f5d0', 6 => '#a1d76a', 7 => '#4d9221', }, piyg8 => { 1 => '#c51b7d', 2 => '#de77ae', 3 => '#f1b6da', 4 => '#fde0ef', 5 => '#e6f5d0', 6 => '#b8e186', 7 => '#7fbc41', 8 => '#4d9221', }, piyg9 => { 1 => '#c51b7d', 2 => '#de77ae', 3 => '#f1b6da', 4 => '#fde0ef', 5 => '#f7f7f7', 6 => '#e6f5d0', 7 => '#b8e186', 8 => '#7fbc41', 9 => '#4d9221', }, piyg10 => { 1 => '#8e0152', 2 => '#c51b7d', 3 => '#de77ae', 4 => '#f1b6da', 5 => '#fde0ef', 6 => '#e6f5d0', 7 => '#b8e186', 8 => '#7fbc41', 9 => '#4d9221', 10 => '#276419', }, piyg11 => { 1 => '#8e0152', 2 => '#c51b7d', 3 => '#de77ae', 4 => '#f1b6da', 5 => '#fde0ef', 6 => '#f7f7f7', 7 => '#e6f5d0', 8 => '#b8e186', 9 => '#7fbc41', 10 => '#4d9221', 11 => '#276419', }, prgn3 => { 1 => '#af8dc3', 2 => '#f7f7f7', 3 => '#7fbf7b', }, prgn4 => { 1 => '#7b3294', 2 => '#c2a5cf', 3 => '#a6dba0', 4 => '#008837', }, prgn5 => { 1 => '#7b3294', 2 => '#c2a5cf', 3 => '#f7f7f7', 4 => '#a6dba0', 5 => '#008837', }, prgn6 => { 1 => '#762a83', 2 => '#af8dc3', 3 => '#e7d4e8', 4 => '#d9f0d3', 5 => '#7fbf7b', 6 => '#1b7837', }, prgn7 => { 1 => '#762a83', 2 => '#af8dc3', 3 => '#e7d4e8', 4 => '#f7f7f7', 5 => '#d9f0d3', 6 => '#7fbf7b', 7 => '#1b7837', }, prgn8 => { 1 => '#762a83', 2 => '#9970ab', 3 => '#c2a5cf', 4 => '#e7d4e8', 5 => '#d9f0d3', 6 => '#a6dba0', 7 => '#5aae61', 8 => '#1b7837', }, prgn9 => { 1 => '#762a83', 2 => '#9970ab', 3 => '#c2a5cf', 4 => '#e7d4e8', 5 => '#f7f7f7', 6 => '#d9f0d3', 7 => '#a6dba0', 8 => '#5aae61', 9 => '#1b7837', }, prgn10 => { 1 => '#40004b', 2 => '#762a83', 3 => '#9970ab', 4 => '#c2a5cf', 5 => '#e7d4e8', 6 => '#d9f0d3', 7 => '#a6dba0', 8 => '#5aae61', 9 => '#1b7837', 10 => '#00441b', }, prgn11 => { 1 => '#40004b', 2 => '#762a83', 3 => '#9970ab', 4 => '#c2a5cf', 5 => '#e7d4e8', 6 => '#f7f7f7', 7 => '#d9f0d3', 8 => '#a6dba0', 9 => '#5aae61', 10 => '#1b7837', 11 => '#00441b', }, pubu3 => { 1 => '#ece7f2', 2 => '#a6bddb', 3 => '#2b8cbe', }, pubu4 => { 1 => '#f1eef6', 2 => '#bdc9e1', 3 => '#74a9cf', 4 => '#0570b0', }, pubu5 => { 1 => '#f1eef6', 2 => '#bdc9e1', 3 => '#74a9cf', 4 => '#2b8cbe', 5 => '#045a8d', }, pubu6 => { 1 => '#f1eef6', 2 => '#d0d1e6', 3 => '#a6bddb', 4 => '#74a9cf', 5 => '#2b8cbe', 6 => '#045a8d', }, pubu7 => { 1 => '#f1eef6', 2 => '#d0d1e6', 3 => '#a6bddb', 4 => '#74a9cf', 5 => '#3690c0', 6 => '#0570b0', 7 => '#034e7b', }, pubu8 => { 1 => '#fff7fb', 2 => '#ece7f2', 3 => '#d0d1e6', 4 => '#a6bddb', 5 => '#74a9cf', 6 => '#3690c0', 7 => '#0570b0', 8 => '#034e7b', }, pubu9 => { 1 => '#fff7fb', 2 => '#ece7f2', 3 => '#d0d1e6', 4 => '#a6bddb', 5 => '#74a9cf', 6 => '#3690c0', 7 => '#0570b0', 8 => '#045a8d', 9 => '#023858', }, pubugn3 => { 1 => '#ece2f0', 2 => '#a6bddb', 3 => '#1c9099', }, pubugn4 => { 1 => '#f6eff7', 2 => '#bdc9e1', 3 => '#67a9cf', 4 => '#02818a', }, pubugn5 => { 1 => '#f6eff7', 2 => '#bdc9e1', 3 => '#67a9cf', 4 => '#1c9099', 5 => '#016c59', }, pubugn6 => { 1 => '#f6eff7', 2 => '#d0d1e6', 3 => '#a6bddb', 4 => '#67a9cf', 5 => '#1c9099', 6 => '#016c59', }, pubugn7 => { 1 => '#f6eff7', 2 => '#d0d1e6', 3 => '#a6bddb', 4 => '#67a9cf', 5 => '#3690c0', 6 => '#02818a', 7 => '#016450', }, pubugn8 => { 1 => '#fff7fb', 2 => '#ece2f0', 3 => '#d0d1e6', 4 => '#a6bddb', 5 => '#67a9cf', 6 => '#3690c0', 7 => '#02818a', 8 => '#016450', }, pubugn9 => { 1 => '#fff7fb', 2 => '#ece2f0', 3 => '#d0d1e6', 4 => '#a6bddb', 5 => '#67a9cf', 6 => '#3690c0', 7 => '#02818a', 8 => '#016c59', 9 => '#014636', }, puor3 => { 1 => '#f1a340', 2 => '#f7f7f7', 3 => '#998ec3', }, puor4 => { 1 => '#e66101', 2 => '#fdb863', 3 => '#b2abd2', 4 => '#5e3c99', }, puor5 => { 1 => '#e66101', 2 => '#fdb863', 3 => '#f7f7f7', 4 => '#b2abd2', 5 => '#5e3c99', }, puor6 => { 1 => '#b35806', 2 => '#f1a340', 3 => '#fee0b6', 4 => '#d8daeb', 5 => '#998ec3', 6 => '#542788', }, puor7 => { 1 => '#b35806', 2 => '#f1a340', 3 => '#fee0b6', 4 => '#f7f7f7', 5 => '#d8daeb', 6 => '#998ec3', 7 => '#542788', }, puor8 => { 1 => '#b35806', 2 => '#e08214', 3 => '#fdb863', 4 => '#fee0b6', 5 => '#d8daeb', 6 => '#b2abd2', 7 => '#8073ac', 8 => '#542788', }, puor9 => { 1 => '#b35806', 2 => '#e08214', 3 => '#fdb863', 4 => '#fee0b6', 5 => '#f7f7f7', 6 => '#d8daeb', 7 => '#b2abd2', 8 => '#8073ac', 9 => '#542788', }, purd3 => { 1 => '#e7e1ef', 2 => '#c994c7', 3 => '#dd1c77', }, purd4 => { 1 => '#f1eef6', 2 => '#d7b5d8', 3 => '#df65b0', 4 => '#ce1256', }, purd5 => { 1 => '#f1eef6', 2 => '#d7b5d8', 3 => '#df65b0', 4 => '#dd1c77', 5 => '#980043', }, purd6 => { 1 => '#f1eef6', 2 => '#d4b9da', 3 => '#c994c7', 4 => '#df65b0', 5 => '#dd1c77', 6 => '#980043', }, purd7 => { 1 => '#f1eef6', 2 => '#d4b9da', 3 => '#c994c7', 4 => '#df65b0', 5 => '#e7298a', 6 => '#ce1256', 7 => '#91003f', }, purd8 => { 1 => '#f7f4f9', 2 => '#e7e1ef', 3 => '#d4b9da', 4 => '#c994c7', 5 => '#df65b0', 6 => '#e7298a', 7 => '#ce1256', 8 => '#91003f', }, purd9 => { 1 => '#f7f4f9', 2 => '#e7e1ef', 3 => '#d4b9da', 4 => '#c994c7', 5 => '#df65b0', 6 => '#e7298a', 7 => '#ce1256', 8 => '#980043', 9 => '#67001f', }, puor10 => { 1 => '#7f3b08', 2 => '#b35806', 3 => '#e08214', 4 => '#fdb863', 5 => '#fee0b6', 6 => '#d8daeb', 7 => '#b2abd2', 8 => '#8073ac', 9 => '#542788', 10 => '#2d004b', }, puor11 => { 1 => '#7f3b08', 2 => '#b35806', 3 => '#e08214', 4 => '#fdb863', 5 => '#fee0b6', 6 => '#f7f7f7', 7 => '#d8daeb', 8 => '#b2abd2', 9 => '#8073ac', 10 => '#542788', 11 => '#2d004b', }, purples3 => { 1 => '#efedf5', 2 => '#bcbddc', 3 => '#756bb1', }, purples4 => { 1 => '#f2f0f7', 2 => '#cbc9e2', 3 => '#9e9ac8', 4 => '#6a51a3', }, purples5 => { 1 => '#f2f0f7', 2 => '#cbc9e2', 3 => '#9e9ac8', 4 => '#756bb1', 5 => '#54278f', }, purples6 => { 1 => '#f2f0f7', 2 => '#dadaeb', 3 => '#bcbddc', 4 => '#9e9ac8', 5 => '#756bb1', 6 => '#54278f', }, purples7 => { 1 => '#f2f0f7', 2 => '#dadaeb', 3 => '#bcbddc', 4 => '#9e9ac8', 5 => '#807dba', 6 => '#6a51a3', 7 => '#4a1486', }, purples8 => { 1 => '#fcfbfd', 2 => '#efedf5', 3 => '#dadaeb', 4 => '#bcbddc', 5 => '#9e9ac8', 6 => '#807dba', 7 => '#6a51a3', 8 => '#4a1486', }, purples9 => { 1 => '#fcfbfd', 2 => '#efedf5', 3 => '#dadaeb', 4 => '#bcbddc', 5 => '#9e9ac8', 6 => '#807dba', 7 => '#6a51a3', 8 => '#54278f', 9 => '#3f007d', }, rdbu10 => { 1 => '#67001f', 2 => '#b2182b', 3 => '#d6604d', 4 => '#f4a582', 5 => '#fddbc7', 6 => '#d1e5f0', 7 => '#92c5de', 8 => '#4393c3', 9 => '#2166ac', 10 => '#053061', }, rdbu11 => { 1 => '#67001f', 2 => '#b2182b', 3 => '#d6604d', 4 => '#f4a582', 5 => '#fddbc7', 6 => '#f7f7f7', 7 => '#d1e5f0', 8 => '#92c5de', 9 => '#4393c3', 10 => '#2166ac', 11 => '#053061', }, rdbu3 => { 1 => '#ef8a62', 2 => '#f7f7f7', 3 => '#67a9cf', }, rdbu4 => { 1 => '#ca0020', 2 => '#f4a582', 3 => '#92c5de', 4 => '#0571b0', }, rdbu5 => { 1 => '#ca0020', 2 => '#f4a582', 3 => '#f7f7f7', 4 => '#92c5de', 5 => '#0571b0', }, rdbu6 => { 1 => '#b2182b', 2 => '#ef8a62', 3 => '#fddbc7', 4 => '#d1e5f0', 5 => '#67a9cf', 6 => '#2166ac', }, rdbu7 => { 1 => '#b2182b', 2 => '#ef8a62', 3 => '#fddbc7', 4 => '#f7f7f7', 5 => '#d1e5f0', 6 => '#67a9cf', 7 => '#2166ac', }, rdbu8 => { 1 => '#b2182b', 2 => '#d6604d', 3 => '#f4a582', 4 => '#fddbc7', 5 => '#d1e5f0', 6 => '#92c5de', 7 => '#4393c3', 8 => '#2166ac', }, rdbu9 => { 1 => '#b2182b', 2 => '#d6604d', 3 => '#f4a582', 4 => '#fddbc7', 5 => '#f7f7f7', 6 => '#d1e5f0', 7 => '#92c5de', 8 => '#4393c3', 9 => '#2166ac', }, rdgy3 => { 1 => '#ef8a62', 2 => '#ffffff', 3 => '#999999', }, rdgy4 => { 1 => '#ca0020', 2 => '#f4a582', 3 => '#bababa', 4 => '#404040', }, rdgy5 => { 1 => '#ca0020', 2 => '#f4a582', 3 => '#ffffff', 4 => '#bababa', 5 => '#404040', }, rdgy6 => { 1 => '#b2182b', 2 => '#ef8a62', 3 => '#fddbc7', 4 => '#e0e0e0', 5 => '#999999', 6 => '#4d4d4d', }, rdgy7 => { 1 => '#b2182b', 2 => '#ef8a62', 3 => '#fddbc7', 4 => '#ffffff', 5 => '#e0e0e0', 6 => '#999999', 7 => '#4d4d4d', }, rdgy8 => { 1 => '#b2182b', 2 => '#d6604d', 3 => '#f4a582', 4 => '#fddbc7', 5 => '#e0e0e0', 6 => '#bababa', 7 => '#878787', 8 => '#4d4d4d', }, rdgy9 => { 1 => '#b2182b', 2 => '#d6604d', 3 => '#f4a582', 4 => '#fddbc7', 5 => '#ffffff', 6 => '#e0e0e0', 7 => '#bababa', 8 => '#878787', 9 => '#4d4d4d', }, rdpu3 => { 1 => '#fde0dd', 2 => '#fa9fb5', 3 => '#c51b8a', }, rdpu4 => { 1 => '#feebe2', 2 => '#fbb4b9', 3 => '#f768a1', 4 => '#ae017e', }, rdpu5 => { 1 => '#feebe2', 2 => '#fbb4b9', 3 => '#f768a1', 4 => '#c51b8a', 5 => '#7a0177', }, rdpu6 => { 1 => '#feebe2', 2 => '#fcc5c0', 3 => '#fa9fb5', 4 => '#f768a1', 5 => '#c51b8a', 6 => '#7a0177', }, rdpu7 => { 1 => '#feebe2', 2 => '#fcc5c0', 3 => '#fa9fb5', 4 => '#f768a1', 5 => '#dd3497', 6 => '#ae017e', 7 => '#7a0177', }, rdpu8 => { 1 => '#fff7f3', 2 => '#fde0dd', 3 => '#fcc5c0', 4 => '#fa9fb5', 5 => '#f768a1', 6 => '#dd3497', 7 => '#ae017e', 8 => '#7a0177', }, rdpu9 => { 1 => '#fff7f3', 2 => '#fde0dd', 3 => '#fcc5c0', 4 => '#fa9fb5', 5 => '#f768a1', 6 => '#dd3497', 7 => '#ae017e', 8 => '#7a0177', 9 => '#49006a', }, rdgy10 => { 1 => '#67001f', 2 => '#b2182b', 3 => '#d6604d', 4 => '#f4a582', 5 => '#fddbc7', 6 => '#e0e0e0', 7 => '#bababa', 8 => '#878787', 9 => '#4d4d4d', 10 => '#1a1a1a', }, rdgy11 => { 1 => '#67001f', 2 => '#b2182b', 3 => '#d6604d', 4 => '#f4a582', 5 => '#fddbc7', 6 => '#ffffff', 7 => '#e0e0e0', 8 => '#bababa', 9 => '#878787', 10 => '#4d4d4d', 11 => '#1a1a1a', }, rdylbu3 => { 1 => '#fc8d59', 2 => '#ffffbf', 3 => '#91bfdb', }, rdylbu4 => { 1 => '#d7191c', 2 => '#fdae61', 3 => '#abd9e9', 4 => '#2c7bb6', }, rdylbu5 => { 1 => '#d7191c', 2 => '#fdae61', 3 => '#ffffbf', 4 => '#abd9e9', 5 => '#2c7bb6', }, rdylbu6 => { 1 => '#d73027', 2 => '#fc8d59', 3 => '#fee090', 4 => '#e0f3f8', 5 => '#91bfdb', 6 => '#4575b4', }, rdylbu7 => { 1 => '#d73027', 2 => '#fc8d59', 3 => '#fee090', 4 => '#ffffbf', 5 => '#e0f3f8', 6 => '#91bfdb', 7 => '#4575b4', }, rdylbu8 => { 1 => '#d73027', 2 => '#f46d43', 3 => '#fdae61', 4 => '#fee090', 5 => '#e0f3f8', 6 => '#abd9e9', 7 => '#74add1', 8 => '#4575b4', }, rdylbu9 => { 1 => '#d73027', 2 => '#f46d43', 3 => '#fdae61', 4 => '#fee090', 5 => '#ffffbf', 6 => '#e0f3f8', 7 => '#abd9e9', 8 => '#74add1', 9 => '#4575b4', }, rdylbu10 => { 1 => '#a50026', 2 => '#d73027', 3 => '#f46d43', 4 => '#fdae61', 5 => '#fee090', 6 => '#e0f3f8', 7 => '#abd9e9', 8 => '#74add1', 9 => '#4575b4', 10 => '#313695', }, rdylbu11 => { 1 => '#a50026', 2 => '#d73027', 3 => '#f46d43', 4 => '#fdae61', 5 => '#fee090', 6 => '#ffffbf', 7 => '#e0f3f8', 8 => '#abd9e9', 9 => '#74add1', 10 => '#4575b4', 11 => '#313695', }, rdylgn3 => { 1 => '#fc8d59', 2 => '#ffffbf', 3 => '#91cf60', }, rdylgn4 => { 1 => '#d7191c', 2 => '#fdae61', 3 => '#a6d96a', 4 => '#1a9641', }, rdylgn5 => { 1 => '#d7191c', 2 => '#fdae61', 3 => '#ffffbf', 4 => '#a6d96a', 5 => '#1a9641', }, rdylgn6 => { 1 => '#d73027', 2 => '#fc8d59', 3 => '#fee08b', 4 => '#d9ef8b', 5 => '#91cf60', 6 => '#1a9850', }, rdylgn7 => { 1 => '#d73027', 2 => '#fc8d59', 3 => '#fee08b', 4 => '#ffffbf', 5 => '#d9ef8b', 6 => '#91cf60', 7 => '#1a9850', }, rdylgn8 => { 1 => '#d73027', 2 => '#f46d43', 3 => '#fdae61', 4 => '#fee08b', 5 => '#d9ef8b', 6 => '#a6d96a', 7 => '#66bd63', 8 => '#1a9850', }, rdylgn9 => { 1 => '#d73027', 2 => '#f46d43', 3 => '#fdae61', 4 => '#fee08b', 5 => '#ffffbf', 6 => '#d9ef8b', 7 => '#a6d96a', 8 => '#66bd63', 9 => '#1a9850', }, rdylgn10 => { 1 => '#a50026', 2 => '#d73027', 3 => '#f46d43', 4 => '#fdae61', 5 => '#fee08b', 6 => '#d9ef8b', 7 => '#a6d96a', 8 => '#66bd63', 9 => '#1a9850', 10 => '#006837', }, rdylgn11 => { 1 => '#a50026', 2 => '#d73027', 3 => '#f46d43', 4 => '#fdae61', 5 => '#fee08b', 6 => '#ffffbf', 7 => '#d9ef8b', 8 => '#a6d96a', 9 => '#66bd63', 10 => '#1a9850', 11 => '#006837', }, reds3 => { 1 => '#fee0d2', 2 => '#fc9272', 3 => '#de2d26', }, reds4 => { 1 => '#fee5d9', 2 => '#fcae91', 3 => '#fb6a4a', 4 => '#cb181d', }, reds5 => { 1 => '#fee5d9', 2 => '#fcae91', 3 => '#fb6a4a', 4 => '#de2d26', 5 => '#a50f15', }, reds6 => { 1 => '#fee5d9', 2 => '#fcbba1', 3 => '#fc9272', 4 => '#fb6a4a', 5 => '#de2d26', 6 => '#a50f15', }, reds7 => { 1 => '#fee5d9', 2 => '#fcbba1', 3 => '#fc9272', 4 => '#fb6a4a', 5 => '#ef3b2c', 6 => '#cb181d', 7 => '#99000d', }, reds8 => { 1 => '#fff5f0', 2 => '#fee0d2', 3 => '#fcbba1', 4 => '#fc9272', 5 => '#fb6a4a', 6 => '#ef3b2c', 7 => '#cb181d', 8 => '#99000d', }, reds9 => { 1 => '#fff5f0', 2 => '#fee0d2', 3 => '#fcbba1', 4 => '#fc9272', 5 => '#fb6a4a', 6 => '#ef3b2c', 7 => '#cb181d', 8 => '#a50f15', 9 => '#67000d', }, set13 => { 1 => '#e41a1c', 2 => '#377eb8', 3 => '#4daf4a', }, set14 => { 1 => '#e41a1c', 2 => '#377eb8', 3 => '#4daf4a', 4 => '#984ea3', }, set15 => { 1 => '#e41a1c', 2 => '#377eb8', 3 => '#4daf4a', 4 => '#984ea3', 5 => '#ff7f00', }, set16 => { 1 => '#e41a1c', 2 => '#377eb8', 3 => '#4daf4a', 4 => '#984ea3', 5 => '#ff7f00', 6 => '#ffff33', }, set17 => { 1 => '#e41a1c', 2 => '#377eb8', 3 => '#4daf4a', 4 => '#984ea3', 5 => '#ff7f00', 6 => '#ffff33', 7 => '#a65628', }, set18 => { 1 => '#e41a1c', 2 => '#377eb8', 3 => '#4daf4a', 4 => '#984ea3', 5 => '#ff7f00', 6 => '#ffff33', 7 => '#a65628', 8 => '#f781bf', }, set19 => { 1 => '#e41a1c', 2 => '#377eb8', 3 => '#4daf4a', 4 => '#984ea3', 5 => '#ff7f00', 6 => '#ffff33', 7 => '#a65628', 8 => '#f781bf', 9 => '#999999', }, set23 => { 1 => '#66c2a5', 2 => '#fc8d62', 3 => '#8da0cb', }, set24 => { 1 => '#66c2a5', 2 => '#fc8d62', 3 => '#8da0cb', 4 => '#e78ac3', }, set25 => { 1 => '#66c2a5', 2 => '#fc8d62', 3 => '#8da0cb', 4 => '#e78ac3', 5 => '#a6d854', }, set26 => { 1 => '#66c2a5', 2 => '#fc8d62', 3 => '#8da0cb', 4 => '#e78ac3', 5 => '#a6d854', 6 => '#ffd92f', }, set27 => { 1 => '#66c2a5', 2 => '#fc8d62', 3 => '#8da0cb', 4 => '#e78ac3', 5 => '#a6d854', 6 => '#ffd92f', 7 => '#e5c494', }, set28 => { 1 => '#66c2a5', 2 => '#fc8d62', 3 => '#8da0cb', 4 => '#e78ac3', 5 => '#a6d854', 6 => '#ffd92f', 7 => '#e5c494', 8 => '#b3b3b3', }, set33 => { 1 => '#8dd3c7', 2 => '#ffffb3', 3 => '#bebada', }, set34 => { 1 => '#8dd3c7', 2 => '#ffffb3', 3 => '#bebada', 4 => '#fb8072', }, set35 => { 1 => '#8dd3c7', 2 => '#ffffb3', 3 => '#bebada', 4 => '#fb8072', 5 => '#80b1d3', }, set36 => { 1 => '#8dd3c7', 2 => '#ffffb3', 3 => '#bebada', 4 => '#fb8072', 5 => '#80b1d3', 6 => '#fdb462', }, set37 => { 1 => '#8dd3c7', 2 => '#ffffb3', 3 => '#bebada', 4 => '#fb8072', 5 => '#80b1d3', 6 => '#fdb462', 7 => '#b3de69', }, set38 => { 1 => '#8dd3c7', 2 => '#ffffb3', 3 => '#bebada', 4 => '#fb8072', 5 => '#80b1d3', 6 => '#fdb462', 7 => '#b3de69', 8 => '#fccde5', }, set39 => { 1 => '#8dd3c7', 2 => '#ffffb3', 3 => '#bebada', 4 => '#fb8072', 5 => '#80b1d3', 6 => '#fdb462', 7 => '#b3de69', 8 => '#fccde5', 9 => '#d9d9d9', }, set310 => { 1 => '#8dd3c7', 2 => '#ffffb3', 3 => '#bebada', 4 => '#fb8072', 5 => '#80b1d3', 6 => '#fdb462', 7 => '#b3de69', 8 => '#fccde5', 9 => '#d9d9d9', 10 => '#bc80bd', }, set311 => { 1 => '#8dd3c7', 2 => '#ffffb3', 3 => '#bebada', 4 => '#fb8072', 5 => '#80b1d3', 6 => '#fdb462', 7 => '#b3de69', 8 => '#fccde5', 9 => '#d9d9d9', 10 => '#bc80bd', 11 => '#ccebc5', }, set312 => { 1 => '#8dd3c7', 2 => '#ffffb3', 3 => '#bebada', 4 => '#fb8072', 5 => '#80b1d3', 6 => '#fdb462', 7 => '#b3de69', 8 => '#fccde5', 9 => '#d9d9d9', 10 => '#bc80bd', 11 => '#ccebc5', 12 => '#ffed6f', }, spectral3 => { 1 => '#fc8d59', 2 => '#ffffbf', 3 => '#99d594', }, spectral4 => { 1 => '#d7191c', 2 => '#fdae61', 3 => '#abdda4', 4 => '#2b83ba', }, spectral5 => { 1 => '#d7191c', 2 => '#fdae61', 3 => '#ffffbf', 4 => '#abdda4', 5 => '#2b83ba', }, spectral6 => { 1 => '#d53e4f', 2 => '#fc8d59', 3 => '#fee08b', 4 => '#e6f598', 5 => '#99d594', 6 => '#3288bd', }, spectral7 => { 1 => '#d53e4f', 2 => '#fc8d59', 3 => '#fee08b', 4 => '#ffffbf', 5 => '#e6f598', 6 => '#99d594', 7 => '#3288bd', }, spectral8 => { 1 => '#d53e4f', 2 => '#f46d43', 3 => '#fdae61', 4 => '#fee08b', 5 => '#e6f598', 6 => '#abdda4', 7 => '#66c2a5', 8 => '#3288bd', }, spectral9 => { 1 => '#d53e4f', 2 => '#f46d43', 3 => '#fdae61', 4 => '#fee08b', 5 => '#ffffbf', 6 => '#e6f598', 7 => '#abdda4', 8 => '#66c2a5', 9 => '#3288bd', }, spectral10 => { 1 => '#9e0142', 2 => '#d53e4f', 3 => '#f46d43', 4 => '#fdae61', 5 => '#fee08b', 6 => '#e6f598', 7 => '#abdda4', 8 => '#66c2a5', 9 => '#3288bd', 10 => '#5e4fa2', }, spectral11 => { 1 => '#9e0142', 2 => '#d53e4f', 3 => '#f46d43', 4 => '#fdae61', 5 => '#fee08b', 6 => '#ffffbf', 7 => '#e6f598', 8 => '#abdda4', 9 => '#66c2a5', 10 => '#3288bd', 11 => '#5e4fa2', }, ylgn3 => { 1 => '#f7fcb9', 2 => '#addd8e', 3 => '#31a354', }, ylgn4 => { 1 => '#ffffcc', 2 => '#c2e699', 3 => '#78c679', 4 => '#238443', }, ylgn5 => { 1 => '#ffffcc', 2 => '#c2e699', 3 => '#78c679', 4 => '#31a354', 5 => '#006837', }, ylgn6 => { 1 => '#ffffcc', 2 => '#d9f0a3', 3 => '#addd8e', 4 => '#78c679', 5 => '#31a354', 6 => '#006837', }, ylgn7 => { 1 => '#ffffcc', 2 => '#d9f0a3', 3 => '#addd8e', 4 => '#78c679', 5 => '#41ab5d', 6 => '#238443', 7 => '#005a32', }, ylgn8 => { 1 => '#ffffe5', 2 => '#f7fcb9', 3 => '#d9f0a3', 4 => '#addd8e', 5 => '#78c679', 6 => '#41ab5d', 7 => '#238443', 8 => '#005a32', }, ylgn9 => { 1 => '#ffffe5', 2 => '#f7fcb9', 3 => '#d9f0a3', 4 => '#addd8e', 5 => '#78c679', 6 => '#41ab5d', 7 => '#238443', 8 => '#006837', 9 => '#004529', }, ylgnbu3 => { 1 => '#edf8b1', 2 => '#7fcdbb', 3 => '#2c7fb8', }, ylgnbu4 => { 1 => '#ffffcc', 2 => '#a1dab4', 3 => '#41b6c4', 4 => '#225ea8', }, ylgnbu5 => { 1 => '#ffffcc', 2 => '#a1dab4', 3 => '#41b6c4', 4 => '#2c7fb8', 5 => '#253494', }, ylgnbu6 => { 1 => '#ffffcc', 2 => '#c7e9b4', 3 => '#7fcdbb', 4 => '#41b6c4', 5 => '#2c7fb8', 6 => '#253494', }, ylgnbu7 => { 1 => '#ffffcc', 2 => '#c7e9b4', 3 => '#7fcdbb', 4 => '#41b6c4', 5 => '#1d91c0', 6 => '#225ea8', 7 => '#0c2c84', }, ylgnbu8 => { 1 => '#ffffd9', 2 => '#edf8b1', 3 => '#c7e9b4', 4 => '#7fcdbb', 5 => '#41b6c4', 6 => '#1d91c0', 7 => '#225ea8', 8 => '#0c2c84', }, ylgnbu9 => { 1 => '#ffffd9', 2 => '#edf8b1', 3 => '#c7e9b4', 4 => '#7fcdbb', 5 => '#41b6c4', 6 => '#1d91c0', 7 => '#225ea8', 8 => '#253494', 9 => '#081d58', }, ylorbr3 => { 1 => '#fff7bc', 2 => '#fec44f', 3 => '#d95f0e', }, ylorbr4 => { 1 => '#ffffd4', 2 => '#fed98e', 3 => '#fe9929', 4 => '#cc4c02', }, ylorbr5 => { 1 => '#ffffd4', 2 => '#fed98e', 3 => '#fe9929', 4 => '#d95f0e', 5 => '#993404', }, ylorbr6 => { 1 => '#ffffd4', 2 => '#fee391', 3 => '#fec44f', 4 => '#fe9929', 5 => '#d95f0e', 6 => '#993404', }, ylorbr7 => { 1 => '#ffffd4', 2 => '#fee391', 3 => '#fec44f', 4 => '#fe9929', 5 => '#ec7014', 6 => '#cc4c02', 7 => '#8c2d04', }, ylorbr8 => { 1 => '#ffffe5', 2 => '#fff7bc', 3 => '#fee391', 4 => '#fec44f', 5 => '#fe9929', 6 => '#ec7014', 7 => '#cc4c02', 8 => '#8c2d04', }, ylorbr9 => { 1 => '#ffffe5', 2 => '#fff7bc', 3 => '#fee391', 4 => '#fec44f', 5 => '#fe9929', 6 => '#ec7014', 7 => '#cc4c02', 8 => '#993404', 9 => '#662506', }, ylorrd3 => { 1 => '#ffeda0', 2 => '#feb24c', 3 => '#f03b20', }, ylorrd4 => { 1 => '#ffffb2', 2 => '#fecc5c', 3 => '#fd8d3c', 4 => '#e31a1c', }, ylorrd5 => { 1 => '#ffffb2', 2 => '#fecc5c', 3 => '#fd8d3c', 4 => '#f03b20', 5 => '#bd0026', }, ylorrd6 => { 1 => '#ffffb2', 2 => '#fed976', 3 => '#feb24c', 4 => '#fd8d3c', 5 => '#f03b20', 6 => '#bd0026', }, ylorrd7 => { 1 => '#ffffb2', 2 => '#fed976', 3 => '#feb24c', 4 => '#fd8d3c', 5 => '#fc4e2a', 6 => '#e31a1c', 7 => '#b10026', }, ylorrd8 => { 1 => '#ffffcc', 2 => '#ffeda0', 3 => '#fed976', 4 => '#feb24c', 5 => '#fd8d3c', 6 => '#fc4e2a', 7 => '#e31a1c', 8 => '#b10026', }, ylorrd9 => { 1 => '#ffffcc', 2 => '#ffeda0', 3 => '#fed976', 4 => '#feb24c', 5 => '#fd8d3c', 6 => '#fc4e2a', 7 => '#e31a1c', 8 => '#bd0026', 9 => '#800026', }, }; # reverse mapping value => name my $color_values = { }; my $all_color_names = { }; { # reverse mapping "#ff0000 => 'red'" # also build a list of all possible color names for my $n (sort keys %$color_names) { my $s = $color_names->{$n}; $color_values->{ $n } = {}; my $t = $color_values->{$n}; # sort the names on their length for my $c (sort { length($a) <=> length($b) || $a cmp $b } keys %$s) { # don't add "blue1" if it is already set as "blue" $t->{ $s->{$c} } = $c unless exists $t->{ $s->{$c} }; # mark as existing $all_color_names->{ $c } = undef; } } } our $qr_custom_attribute = qr/^x-([a-z_0-9]+-)*[a-z_0-9]+\z/; sub color_names { $color_names; } sub color_name { # return "red" for "#ff0000" my ($self,$color,$scheme) = @_; $scheme ||= 'w3c'; $color_values->{$scheme}->{$color} || $color; } sub color_value { # return "#ff0000" for "red" my ($self,$color,$scheme) = @_; $scheme ||= 'w3c'; # 'w3c/red' => 'w3c', 'red' $scheme = $1 if $color =~ s/^([a-z0-9])\///; $color_names->{$scheme}->{$color} || $color; } sub _color_scheme { # check that a given color scheme is valid my ($self, $scheme) = @_; return $scheme if $scheme eq 'inherit'; exists $color_names->{ $scheme } ? $scheme : undef; } sub _color { # Check that a given color name (like 'red'), or value (like '#ff0000') # or rgb(1,2,3) is valid. Used by valid_attribute(). # Note that for color names, the color scheme is not known here, so we # can only look if the color name is potentially possible. F.i. under # the Brewer scheme ylorrd9, '1' is a valid color name, while 'red' # would not. To resolve such conflicts, we will fallback to 'x11' # (the largest of the schemes) if the color name doesn't exist in # the current scheme. my ($self, $org_color) = @_; $org_color = lc($org_color); # color names are case insensitive $org_color =~ s/\s//g; # remove spaces to unify format my $color = $org_color; if ($color =~ s/^(w3c|[a-z]+\d{0,2})\///) { my $scheme = $1; return $org_color if exists $color_names->{$scheme}->{$color}; # if it didn't work, then fall back to x11 $scheme = 'x11'; return (exists $color_names->{$scheme}->{$color} ? $org_color : undef); } # scheme unknown, fall back to generic handling # red => red return $org_color if exists $all_color_names->{$color}; # #ff0000 => #ff0000, rgb(1,2,3) => rgb(1,2,3) defined $self->color_as_hex($color) ? $org_color : undef; } sub _hsv_to_rgb { # H=0..360, S=0..1.0, V=0..1.0 my ($h, $s, $v) = @_; my $e = 0.0001; if ($s < $e) { $v = abs(int(256 * $v)); $v = 255 if $v > 255; return ($v,$v,$v); } my ($r,$g,$b); $h *= 360; my $h1 = int($h / 60); my $f = $h / 60 - $h1; my $p = $v * (1 - $s); my $q = $v * (1 - ($s * $f)); my $t = $v * (1 - ($s * (1-$f))); if ($h1 == 0 || $h1 == 6) { $r = $v; $g = $t; $b = $p; } elsif ($h1 == 1) { $r = $q; $g = $v; $b = $p; } elsif ($h1 == 2) { $r = $p; $g = $v; $b = $t; } elsif ($h1 == 3) { $r = $p; $g = $q; $b = $v; } elsif ($h1 == 4) { $r = $t; $g = $p; $b = $v; } else { $r = $v; $g = $p; $b = $q; } # clamp values to 0.255 $r = abs(int($r*256)); $g = abs(int($g*256)); $b = abs(int($b*256)); $r = 255 if $r > 255; $g = 255 if $g > 255; $b = 255 if $b > 255; ($r,$g,$b); } sub _hsl_to_rgb { # H=0..360, S=0..100, L=0..100 my ($h, $s, $l) = @_; my $e = 0.0001; if ($s < $e) { # achromatic or grey $l = abs(int(256 * $l)); $l = 255 if $l > 255; return ($l,$l,$l); } my $t2; if ($l < 0.5) { $t2 = $l * ($s + 1); } else { $t2 = $l + $s - ($l * $s); } my $t1 = $l * 2 - $t2; my ($r,$g,$b); # 0..359 $h %= 360 if $h >= 360; # $h = 0..1 $h /= 360; my $tr = $h + 1/3; my $tg = $h; my $tb = $h - 1/3; $tr += 1 if $tr < 0; $tr -= 1 if $tr > 1; $tg += 1 if $tg < 0; $tg -= 1 if $tg > 1; $tb += 1 if $tb < 0; $tb -= 1 if $tb > 1; my $i = 0; my @temp3 = ($tr,$tg,$tb); my @rc; for my $c ($r,$g,$b) { my $t3 = $temp3[$i++]; if ($t3 < 1/6) { $c = $t1 + ($t2 - $t1) * 6 * $t3; } elsif ($t3 < 1/2) { $c = $t2; } elsif ($t3 < 2/3) { $c = $t1 + ($t2 - $t1) * 6 * (2/3 - $t3); } else { $c = $t1; } $c = int($c * 256); $c = 255 if $c > 255; push @rc, $c; } @rc; } my $factors = { 'rgb' => [ 255, 255, 255, 255 ], 'hsv' => [ 1, 1, 1, 255 ], 'hsl' => [ 360, 1, 1, 255 ], }; sub color_as_hex { # Turn "red" or rgb(255,0,0) or "#f00" into "#ff0000". Return undef for # invalid colors. my ($self,$color,$scheme) = @_; $scheme ||= 'w3c'; $color = lc($color); # 'w3c/red' => 'w3c', 'red' $scheme = $1 if $color =~ s/^([a-z0-9])\///; # convert "red" to "ffff00" return $color_names->{$scheme}->{$color} if exists $color_names->{$scheme}->{$color}; # fallback to x11 scheme if color doesn't exist return $color_names->{x11}->{$color} if exists $color_names->{x11}->{$color}; my $qr_num = qr/\s* ((?:[0-9]{1,3}%?) | # 12%, 10, 2 etc (?:[0-9]?\.[0-9]{1,5}) ) # .1, 0.1, 2.5 etc /x; # rgb(255,100%,1.0) => '#ffffff' if ($color =~ /^(rgb|hsv|hsl)\($qr_num,$qr_num,$qr_num(?:,$qr_num)?\s*\)\z/) { my $r = $2; my $g = $3; my $b = $4; my $a = $5; $a = 255 unless defined $a; my $format = $1; my $i = 0; for my $c ($r,$g,$b,$a) { # for the first value in HSL or HSV, use 360, otherwise 100. For RGB, use 255 my $factor = $factors->{$format}->[$i++]; if ($c =~ /^([0-9]+)%\z/) # 10% => 25.5 { $c = $1 * $factor / 100; } else { $c = $1 * $factor if $c =~ /^([0-9]+\.[0-9]+)\z/; # 0.1, 1.0 } } ($r,$g,$b) = Graph::Easy::_hsv_to_rgb($r,$g,$b) if $format eq 'hsv'; ($r,$g,$b) = Graph::Easy::_hsl_to_rgb($r,$g,$b) if $format eq 'hsl'; $a = int($a); $a = 255 if $a > 255; # #RRGGBB or #RRGGBBAA $color = sprintf("#%02x%02x%02x%02x", $r,$g,$b,$a); } # turn #ff0 into #ffff00 $color = "#$1$1$2$2$3$3" if $color =~ /^#([a-f0-9])([a-f0-9])([a-f[0-9])\z/; # #RRGGBBff => #RRGGBB (alpha value of 255 is the default) $color =~ s/^(#......)ff\z/$1/i; # check final color value to be #RRGGBB or #RRGGBBAA return undef unless $color =~ /^#([a-f0-9]{6}|[a-f0-9]{8})\z/i; $color; } sub text_style { # check whether the given list of textstyle attributes is valid my ($self, $style) = @_; return $style if $style =~ /^(normal|none|)\z/; my @styles = split /\s+/, $style; return undef if grep(!/^(underline|overline|line-through|italic|bold)\z/, @styles); $style; } sub text_styles { # return a hash with the defined textstyles checked my ($self) = @_; my $style = $self->attribute('textstyle'); return { none => 1 } if $style =~ /^(normal|none)\z/; return { } if $style eq ''; my $styles = {}; for my $key ( split /\s+/, $style ) { $styles->{$key} = 1; } $styles; } sub text_styles_as_css { my ($self, $align, $fontsize) = @_; my $style = ''; my $ts = $self->text_styles(); $style .= " font-style: italic;" if $ts->{italic}; $style .= " font-weight: bold;" if $ts->{bold}; if ($ts->{underline} || $ts->{none} || $ts->{overline} || $ts->{'line-through'}) { # XXX TODO: HTML does seem to allow only one of them my @s; foreach my $k (qw/underline overline line-through none/) { push @s, $k if $ts->{$k}; } my $s = join(' ', @s); $style .= " text-decoration: $s;" if $s; } my $fs = $self->raw_attribute('fontsize'); $style .= " font-size: $fs;" if $fs; if (!$align) { # XXX TODO: raw_attribute()? my $al = $self->attribute('align'); $style .= " text-align: $al;" if $al; } $style; } sub _font_size_in_pixels { my ($self, $em, $val) = @_; my $fs = $val; $fs = $self->attribute('fontsize') || '' if !defined $val; return $em if $fs eq ''; if ($fs =~ /^([\d.]+)em\z/) { $fs = $1 * $em; } elsif ($fs =~ /^([\d.]+)%\z/) { $fs = ($1 / 100) * $em; } # this is discouraged: elsif ($fs =~ /^([\d.]+)px\z/) { $fs = int($1 || 5); } else { $self->error("Illegal fontsize '$fs'"); } $fs; } # direction modifier in degrees my $modifier = { forward => 0, front => 0, left => -90, right => +90, back => +180, }; # map absolute direction to degrees my $dirs = { up => 0, north => 0, down => 180, south => 180, west => 270, east => 90, 0 => 0, 180 => 180, 90 => 90, 270 => 270, }; # map absolute direction to side (south etc) my $sides = { north => 'north', south => 'south', east => 'east', west => 'west', up => 'north', down => 'south', 0 => 'north', 180 => 'south', 90 => 'east', 270 => 'west', }; sub _direction_as_number { my ($self,$dir) = @_; my $d = $dirs->{$dir}; $self->_croak("$dir is not an absolut direction") unless defined $d; $d; } sub _direction_as_side { my ($self,$dir) = @_; return unless exists $sides->{$dir}; $sides->{$dir}; } sub _flow_as_direction { # Take a flow direction (0,90,180,270 etc), and a new direction (left|south etc) # and return the new flow. south et al will stay, while left|right etc depend # on the incoming flow. my ($self, $inflow, $dir) = @_; # in=south and dir=forward => south # in=south and dir=back => north etc # in=south and dir=east => east # return 90 unless defined $dir; if ($dir =~ /^(south|north|west|east|up|down|0|90|180|270)\z/) { # new direction is absolut, so inflow doesn't play a role # return 0,90,180 or 270 return $dirs->{$dir}; } my $in = $dirs->{$inflow}; my $modifier = $modifier->{$dir}; $self->_croak("$inflow,$dir results in undefined inflow") unless defined $in; $self->_croak("$inflow,$dir results in undefined modifier") unless defined $modifier; my $out = $in + $modifier; $out -= 360 while $out >= 360; # normalize to 0..359 $out += 360 while $out < 0; # normalize to 0..359 $out; } sub _flow_as_side { # Take a flow direction (0,90,180,270 etc), and a new direction (left|south etc) # and return the new flow. south et al will stay, while left|right etc depend # on the incoming flow. my ($self, $inflow, $dir) = @_; # in=south and dir=forward => south # in=south and dir=back => north etc # in=south and dir=east => east # return 90 unless defined $dir; if ($dir =~ /^(south|north|west|east|up|down|0|90|180|270)\z/) { # new direction is absolut, so inflow doesn't play a role # return east, west etc return $sides->{$dir}; } my $in = $dirs->{$inflow}; my $modifier = $modifier->{$dir}; $self->_croak("$inflow,$dir results in undefined inflow") unless defined $in; $self->_croak("$inflow,$dir results in undefined modifier") unless defined $modifier; my $out = $in + $modifier; $out -= 360 if $out >= 360; # normalize to 0..359 $sides->{$out}; } sub _direction { # check that a direction (south etc) is valid my ($self, $dir) = @_; $dir =~ /^(south|east|west|north|down|up|0|90|180|270|front|forward|back|left|right)\z/ ? $dir : undef; } sub _border_attribute_as_html { # Return "solid 1px red" from the individual border(style|color|width) # attributes, mainly for HTML output. my ($style, $width, $color, $scheme) = @_; $style ||= ''; $width = '' unless defined $width; $color = '' unless defined $color; $color = Graph::Easy->color_as_hex($color,$scheme)||'' if $color !~ /^#/; return $style if $style =~ /^(none|)\z/; # width: 2px for double would collapse to one line $width = '' if $style =~ /^double/; # convert the style and widths to something HTML can understand $width = '0.5em' if $style eq 'broad'; $width = '4px' if $style =~ /^bold/; $width = '1em' if $style eq 'wide'; $style = 'solid' if $style =~ /(broad|wide|bold)\z/; $style = 'dashed' if $style eq 'bold-dash'; $style = 'double' if $style eq 'double-dash'; $width = $width.'px' if $width =~ /^\s*\d+\s*\z/; return '' if $width eq '' && $style ne 'double'; my $val = join(" ", $style, $width, $color); $val =~ s/^\s+//; $val =~ s/\s+\z//; $val; } sub _border_attribute { # Return "solid 1px red" from the individual border(style|color|width) # attributes. Used by as_txt(). my ($style, $width, $color) = @_; $style ||= ''; $width = '' unless defined $width; $color = '' unless defined $color; return $style if $style =~ /^(none|)\z/; $width = $width.'px' if $width =~ /^\s*\d+\s*\z/; my $val = join(" ", $style, $width, $color); $val =~ s/^\s+//; $val =~ s/\s+\z//; $val; } sub _border_width_in_pixels { my ($self, $em) = @_; my $bw = $self->attribute('borderwidth') || '0'; return 0 if $bw eq '0'; my $bs = $self->attribute('borderstyle') || 'none'; return 0 if $bs eq 'none'; return 3 if $bs =~ /^bold/; return $em / 2 if $bs =~ /^broad/; return $em if $bs =~ /^wide/; # width: 1 is 1px; return $bw if $bw =~ /^([\d.]+)\z/; if ($bw =~ /^([\d.]+)em\z/) { $bw = $1 * $em; } elsif ($bw =~ /^([\d.]+)%\z/) { $bw = ($1 / 100) * $em; } # this is discouraged: elsif ($bw =~ /^([\d.]+)px\z/) { $bw = $1; } else { $self->error("Illegal borderwidth '$bw'"); } $bw; } sub _angle { # check an angle for being valid my ($self, $angle) = @_; return undef unless $angle =~ /^([+-]?\d{1,3}|south|west|east|north|up|down|left|right|front|back|forward)\z/; $angle; } sub _uint { # check a small unsigned integer for being valid my ($self, $val) = @_; return undef unless $val =~ /^\d+\z/; $val = abs(int($val)); $val = 4 * 1024 if $val > 4 * 1024; $val; } sub _font { # check a font-list for being valid my ($self, $font) = @_; $font; } sub split_border_attributes { # split "1px solid black" or "red dotted" into style, width and color my ($self,$border) = @_; # special case return ('none', undef, undef) if $border eq '0'; # extract style my $style; $border =~ s/(solid|dotted|dot-dot-dash|dot-dash|dashed|double-dash|double|bold-dash|bold|broad|wide|wave|none)/$style=$1;''/eg; $style ||= 'solid'; # extract width $border =~ s/(\d+(px|em|%))//g; my $width = $1 || ''; $width =~ s/[^0-9]+//g; # leave only digits $border =~ s/\s+//g; # rem unnec. spaces # The left-over part must be a valid color. my $color = $border; $color = Graph::Easy->_color($border) if $border ne ''; $self->error("$border is not a valid bordercolor") unless defined $color; $width = undef if $width eq ''; $color = undef if $color eq ''; $style = undef if $style eq ''; ($style,$width,$color); } ############################################################################# # attribute checking # different types of attributes with pre-defined handling use constant { ATTR_STRING => 0, # an arbitrary string ATTR_COLOR => 1, # color name or value like rgb(1,1,1) ATTR_ANGLE => 2, # 0 .. 359.99 ATTR_PORT => 3, # east, etc. ATTR_UINT => 4, # a "small" unsigned integer ATTR_URL => 5, # these cannot have "inherit", see ATTR_INHERIT_MIN ATTR_LIST => 6, # a list of values ATTR_LCTEXT => 7, # lowercase text (classname) ATTR_TEXT => 8, # titles, links, labels etc ATTR_NO_INHERIT => 6, ATTR_DESC_SLOT => 0, ATTR_MATCH_SLOT => 1, ATTR_DEFAULT_SLOT => 2, ATTR_EXAMPLE_SLOT => 3, ATTR_TYPE_SLOT => 4, }; # Lists the attribute names along with # * a short description, # * regexp or sub name to match valid attributes # * default value # * an short example value # * type # * graph examples my $attributes = { all => { align => [ "The alignment of the label text.", [ qw/center left right/ ], { default => 'center', group => 'left', edge => 'left' }, 'right', undef, "graph { align: left; label: My Graph; }\nnode {align: left;}\n ( Nodes:\n [ Right\\nAligned ] { align: right; } -- label\\n text -->\n { align: left; }\n [ Left\\naligned ] )", ], autolink => [ "If set to something else than 'none', will use the appropriate attribute to automatically generate the L, unless L is already set. See the section about labels, titles, names and links for reference.", [ qw/label title name none inherit/ ], { default => 'inherit', graph => 'none' }, 'title', ], autotitle => [ "If set to something else than 'none', will use the appropriate attribute to automatically generate the L, unless L<title> is already set. See the section about labels, titles, names and links for reference.", [ qw/label name none link inherit/ ], { default => 'inherit', graph => 'none' }, 'label', ], autolabel => [ "Will restrict the L<label> text to N characters. N must be greater than 10. See the section about labels, titles, names and links for reference.", # for compatibility with older versions (pre v0.49), also allow "name,N" qr/^(name\s*,\s*)?[\d]{2,5}\z/, { default => 'inherit', graph => '' }, '20', undef, "graph { autolabel: 20; autotitle: name; }\n\n[ Bonn ]\n -- Acme Travels Incorporated -->\n [ Frankfurt (Main) / Flughafen ]", ], background => [ "The background color, e.g. the color B<outside> the shape. Do not confuse with L<fill>. If set to inherit, the object will inherit the L<fill> color (B<not> the background color!) of the parent e.g. the enclosing group or graph. See the section about color names and values for reference.", undef, # { default => 'inherit', graph => 'white', 'group.anon' => 'white', 'node.anon' => 'white' }, 'inherit', 'rgb(255,0,0)', ATTR_COLOR, "[ Crimson ] { shape: circle; background: crimson; }\n -- Aqua Marine --> { background: #7fffd4; }\n [ Misty Rose ]\n { background: white; fill: rgb(255,228,221); shape: ellipse; }", ], class => [ 'The subclass of the object. See the section about class names for reference.', qr/^(|[a-zA-Z][a-zA-Z0-9_]*)\z/, '', 'mynodeclass', ATTR_LCTEXT, ], color => [ 'The foreground/text/label color. See the section about color names and values for reference.', undef, 'black', 'rgb(255,255,0)', ATTR_COLOR, "[ Lime ] { color: limegreen; }\n -- label --> { color: blue; labelcolor: red; }\n [ Dark Orange ] { color: rgb(255,50%,0.01); }", ], colorscheme => [ "The colorscheme to use for all color values. See the section about color names and values for reference and a list of possible values.", '_color_scheme', { default => 'inherit', graph => 'w3c', }, 'x11', ATTR_STRING, "graph { colorscheme: accent8; } [ 1 ] { fill: 1; }\n" . " -> \n [ 3 ] { fill: 3; }\n" . " -> \n [ 4 ] { fill: 4; }\n" . " -> \n [ 5 ] { fill: 5; }\n" . " -> \n [ 6 ] { fill: 6; }\n" . " -> \n [ 7 ] { fill: 7; }\n" . " -> \n [ 8 ] { fill: 8; }\n" , ], comment => [ "A free-form text field containing a comment on this object. This will be embedded into output formats if possible, e.g. in HTML, SVG and Graphviz, but not ASCII or Boxart.", undef, '', '(C) by Tels 2007. All rights reserved.', ATTR_STRING, "graph { comment: German capitals; }\n [ Bonn ] --> [ Berlin ]", ], fill => [ "The fill color, e.g. the color inside the shape. For the graph, this is the background color for the label. For edges, defines the color inside the arrow shape. See also L<background>. See the section about color names and values for reference.", undef, { default => 'white', graph => 'inherit', edge => 'inherit', group => '#a0d0ff', 'group.anon' => 'white', 'node.anon' => 'inherit' }, 'rgb(255,0,0)', ATTR_COLOR, "[ Crimson ]\n {\n shape: circle;\n background: yellow;\n fill: red;\n border: 3px solid blue;\n }\n-- Aqua Marine -->\n {\n arrowstyle: filled;\n fill: red;\n }\n[ Two ]", ], 'fontsize' => [ "The size of the label text, best expressed in I<em> (1.0em, 0.5em etc) or percent (100%, 50% etc)", qr/^\d+(\.\d+)?(em|px|%)?\z/, { default => '0.8em', graph => '1em', node => '1em', }, '50%', undef, "graph { fontsize: 200%; label: Sample; }\n\n ( Nodes:\n [ Big ] { fontsize: 1.5em; color: white; fill: darkred; }\n -- Small -->\n { fontsize: 0.2em; }\n [ Normal ] )", ], flow => [ "The general direction in which edges will leave nodes first. On edges, influeces where the target node is place. Please see the section about <a href='hinting.html#flow'>flow control</a> for reference.", '_direction', { graph => 'east', default => 'inherit' }, 'south', undef, "graph { flow: up; }\n [ Enschede ] { flow: left; } -> [ Bielefeld ] -> [ Wolfsburg ]", ], font => [ 'A prioritized list of lower-case, unquoted values, separated by a comma. Values are either font family names (like "times", "arial" etc) or generic family names (like "serif", "cursive", "monospace"), the first recognized value will be used. Always offer a generic name as the last possibility.', '_font', { default => 'serif', edge => 'sans-serif' }, 'arial, helvetica, sans-serif', undef, "graph { font: vinque, georgia, utopia, serif; label: Sample; }" . "\n\n ( Nodes:\n [ Webdings ] { font: Dingbats, webdings; }\n". " -- FlatLine -->\n { font: flatline; }\n [ Normal ] )", ], id => [ "A unique identifier for this object, consisting only of letters, digits, or underscores.", qr/^[a-zA-Z0-9_]+\z/, '', 'Bonn123', undef, "[ Bonn ] --> { id: 123; } [ Berlin ]", ], label => [ "The text displayed as label. If not set, equals the name (for nodes) or no label (for edges, groups and the graph itself).", undef, undef, 'My label', ATTR_TEXT, ], linkbase => [ 'The base URL prepended to all generated links. See the section about links for reference.', undef, { default => 'inherit', graph => '/wiki/index.php/', }, 'http://en.wikipedia.org/wiki/', ATTR_URL, ], link => [ 'The link part, appended onto L<linkbase>. See the section about links for reference.', undef, '', 'Graph', ATTR_TEXT, <<LINK_EOF node { autolink: name; textstyle: none; fontsize: 1.1em; } graph { linkbase: http://de.wikipedia.org/wiki/; } edge { textstyle: overline; } [] --> [ Friedrichshafen ] -- Schiff --> { autolink: label; color: orange; title: Vrooom!; } [ Immenstaad ] { color: green; } --> [ Hagnau ] LINK_EOF ], title => [ "The text displayed as mouse-over for nodes/edges, or as the title for the graph. If empty, no title will be generated unless L<autotitle> is set.", undef, '', 'My title', ATTR_TEXT, ], format => [ "The formatting language of the label. The default, C<none> means nothing special will be done. When set to C<pod>, formatting codes like <code>B<bold></code> will change the formatting of the label. See the section about label text formatting for reference.", [ 'none', 'pod' ], 'none', 'pod', undef, <<EOF graph { format: pod; label: I am B<bold> and I<italic>; } node { format: pod; } edge { format: pod; } [ U<B<bold and underlined>> ] --> { label: "S<Fähre>"; } [ O<Konstanz> ] EOF ], textstyle => [ "The style of the label text. Either 'none', or any combination (separated with spaces) of 'underline', 'overline', 'bold', 'italic', 'line-through'. 'none' disables underlines on links.", 'text_style', '', 'underline italic bold', undef, <<EOF graph { fontsize: 150%; label: Verbindung; textstyle: bold italic; } node { textstyle: underline bold; fill: #ffd080; } edge { textstyle: italic bold overline; } [ Meersburg ] { fontsize: 2em; } -- F\x{e4}hre --> { fontsize: 1.2em; color: red; } [ Konstanz ] EOF ], textwrap => [ "The default C<none> makes the label text appear exactly as it was written, with <a href='syntax.html'>manual line breaks</a> applied. When set to a positive number, the label text will be wrapped after this number of characters. When set to C<auto>, the label text will be wrapped to make the node size as small as possible, depending on output format this may even be dynamic. When not C<none>, manual line breaks and alignments on them are ignored.", qr/^(auto|none|\d{1,4})/, { default => 'inherit', graph => 'none' }, 'auto', undef, "node { textwrap: auto; }\n ( Nodes:\n [ Frankfurt (Oder) liegt an der\n ostdeutschen Grenze und an der Oder ] -->\n [ Städte innerhalb der\n Ost-Westfahlen Region mit sehr langen Namen] )", ], }, node => { bordercolor => [ 'The color of the L<border>. See the section about color names and values for reference.', undef, { default => '#000000' }, 'rgb(255,255,0)', ATTR_COLOR, "node { border: black bold; }\n[ Black ]\n --> [ Red ] { bordercolor: red; }\n --> [ Green ] { bordercolor: green; }", ], borderstyle => [ 'The style of the L<border>. The special styles "bold", "broad", "wide", "double-dash" and "bold-dash" will set and override the L<borderwidth>.', [ qw/none solid dotted dashed dot-dash dot-dot-dash double wave bold bold-dash broad double-dash wide/ ], { default => 'none', 'node.anon' => 'none', 'group.anon' => 'none', node => 'solid', group => 'dashed' }, 'dotted', undef, "node { border: dotted; }\n[ Dotted ]\n --> [ Dashed ] { borderstyle: dashed; }\n --> [ broad ] { borderstyle: broad; }", ], borderwidth => [ 'The width of the L<border>. Certain L<border>-styles will override the width.', qr/^\d+(px|em)?\z/, '1', '2px', ], border => [ 'The border. Can be any combination of L<borderstyle>, L<bordercolor> and L<borderwidth>.', undef, { default => 'none', 'node.anon' => 'none', 'group.anon' => 'none', node => 'solid 1px #000000', group => 'dashed 1px #000000' }, 'dotted red', undef, "[ Normal ]\n --> [ Bold ] { border: bold; }\n --> [ Broad ] { border: broad; }\n --> [ Wide ] { border: wide; }\n --> [ Bold-Dash ] { border: bold-dash; }", ], basename => [ "Controls the base name of an autosplit node. Ignored for all other nodes. Unless set, it is generated automatically from the node parts. Please see the section about <a href='hinting.html#autosplit'>autosplit</a> for reference.", undef, '', '123', undef, "[ A|B|C ] { basename: A } [ 1 ] -> [ A.2 ]\n [ A|B|C ] [ 2 ] -> [ ABC.2 ]", ], group => [ "Puts the node into this group.", undef, '', 'Cities', undef, "[ A ] { group: Cities:; } ( Cities: [ B ] ) [ A ] --> [ B ]", ], size => [ 'The size of the node in columns and rows. Must be greater than 1 in each direction.', qr/^\d+\s*,\s*\d+\z/, '1,1', '3,2', ], rows => [ 'The size of the node in rows. See also L<size>.', qr/^\d+\z/, '1', '3', ], columns => [ 'The size of the node in columns. See also L<size>.', qr/^\d+\z/, '1', '2', ], offset => [ 'The offset of this node from the L<origin> node, in columns and rows. Only used if you also set the L<origin> node.', qr/^[+-]?\d+\s*,\s*[+-]?\d+\z/, '0,0', '3,2', undef, "[ A ] -> [ B ] { origin: A; offset: 2,2; }", ], origin => [ 'The name of the node, that this node is relativ to. See also L<offset>.', undef, '', 'Cluster A', ], pointshape => [ "Controls the style of a node that has a L<shape> of 'point'.", [ qw/star square dot circle cross diamond invisible x/ ], 'star', 'square', undef, "node { shape: point; }\n\n [ A ]". "\n -> [ B ] { pointshape: circle; }" . "\n -> [ C ] { pointshape: cross; }" . "\n -> [ D ] { pointshape: diamond; }" . "\n -> [ E ] { pointshape: dot; }" . "\n -> [ F ] { pointshape: invisible; }" . "\n -> [ G ] { pointshape: square; }" . "\n -> [ H ] { pointshape: star; }" . "\n -> [ I ] { pointshape: x; }" . "\n -> [ ☯ ] { shape: none; }" ], pointstyle => [ "Controls the style of the L<pointshape> of a node that has a L<shape> of 'point'. " . "Note for backwards compatibility reasons, the shape names 'star', 'square', 'dot', 'circle', 'cross', 'diamond' and 'invisible' ". "are also supported, but should not be used here, instead set them via L<pointshape>.", [ qw/closed filled star square dot circle cross diamond invisible x/ ], 'filled', 'open', undef, "node { shape: point; pointstyle: closed; pointshape: diamond; }\n\n [ A ] --> [ B ] { pointstyle: filled; }", ], rank => [ "The rank of the node, used by the layouter to find the order and placement of nodes. " . "Set to C<auto> (the default), C<same> (usefull for node lists) or a positive number. " . "See the section about ranks for reference and more examples.", qr/^(auto|same|\d{1,6})\z/, 'auto', 'same', undef, "[ Bonn ], [ Berlin ] { rank: same; }\n [ Bonn ] -> [ Cottbus ] -> [ Berlin ]", ], rotate => [ "The rotation of the node shape, either an absolute value (like C<south>, C<up>, C<down> or C<123>), or a relative value (like C<+12>, C<-90>, C<left>, C<right>). For relative angles, the rotation will be based on the node's L<flow>. Rotation is clockwise.", undef, '0', '180', ATTR_ANGLE, "[ Bonn ] { rotate: 45; } -- ICE --> \n [ Berlin ] { shape: triangle; rotate: -90; }", ], shape => [ "The shape of the node. Nodes with shape 'point' (see L<pointshape>) have a fixed size and do not display their label. The border of such a node is the outline of the C<pointshape>, and the fill is the inside of the C<pointshape>. When the C<shape> is set to the value 'img', the L<label> will be interpreted as an external image resource to display. In this case attributes like L<color>, L<fontsize> etc. are ignored.", [ qw/ circle diamond edge ellipse hexagon house invisible invhouse invtrapezium invtriangle octagon parallelogram pentagon point triangle trapezium septagon rect rounded none img/ ], 'rect', 'circle', undef, "[ Bonn ] -> \n [ Berlin ] { shape: circle; }\n -> [ Regensburg ] { shape: rounded; }\n -> [ Ulm ] { shape: point; }\n -> [ Wasserburg ] { shape: invisible; }\n -> [ Augsburg ] { shape: triangle; }\n -> [ House ] { shape: img; label: img/house.png;\n border: none; title: My House; fill: inherit; }", ], }, # node graph => { bordercolor => [ 'The color of the L<border>. See the section about color names and values for reference.', undef, { default => '#000000' }, 'rgb(255,255,0)', ATTR_COLOR, "node { border: black bold; }\n[ Black ]\n --> [ Red ] { bordercolor: red; }\n --> [ Green ] { bordercolor: green; }", ], borderstyle => [ 'The style of the L<border>. The special styles "bold", "broad", "wide", "double-dash" and "bold-dash" will set and override the L<borderwidth>.', [ qw/none solid dotted dashed dot-dash dot-dot-dash double wave bold bold-dash broad double-dash wide/ ], { default => 'none', 'node.anon' => 'none', 'group.anon' => 'none', node => 'solid', group => 'dashed' }, 'dotted', undef, "node { border: dotted; }\n[ Dotted ]\n --> [ Dashed ] { borderstyle: dashed; }\n --> [ broad ] { borderstyle: broad; }", ], borderwidth => [ 'The width of the L<border>. Certain L<border>-styles will override the width.', qr/^\d+(px|em)?\z/, '1', '2px', ], border => [ 'The border. Can be any combination of L<borderstyle>, L<bordercolor> and L<borderwidth>.', undef, { default => 'none', 'node.anon' => 'none', 'group.anon' => 'none', node => 'solid 1px #000000', group => 'dashed 1px #000000' }, 'dotted red', undef, "[ Normal ]\n --> [ Bold ] { border: bold; }\n --> [ Broad ] { border: broad; }\n --> [ Wide ] { border: wide; }\n --> [ Bold-Dash ] { border: bold-dash; }", ], gid => [ "A unique ID for the graph. Usefull if you want to include two graphs into one HTML page.", qr/^\d+\z/, '', '123', ], labelpos => [ "The position of the graph label.", [ qw/top bottom/ ], 'top', 'bottom', ATTR_LIST, "graph { labelpos: bottom; label: My Graph; }\n\n [ Buxtehude ] -> [ Fuchsberg ]\n" ], output => [ "The desired output format. Only used when calling Graph::Easy::output(), or by mediawiki-graph.", [ qw/ascii html svg graphviz boxart debug/ ], '', 'ascii', ATTR_LIST, "graph { output: debug; }" ], root => [ "The name of the root node, given as hint to the layouter to start the layout there. When not set, the layouter will pick a node at semi-random.", undef, '', 'My Node', ATTR_TEXT, "graph { root: B; }\n # B will be at the left-most place\n [ A ] --> [ B ] --> [ C ] --> [ D ] --> [ A ]", ], type => [ "The type of the graph, either undirected or directed.", [ qw/directed undirected/ ], 'directed', 'undirected', ATTR_LIST, "graph { type: undirected; }\n [ A ] --> [ B ]", ], }, # graph edge => { style => [ 'The line style of the edge. When set on the general edge class, this attribute changes only the style of all solid edges to the specified one.', [ qw/solid dotted dashed dot-dash dot-dot-dash bold bold-dash double-dash double wave broad wide invisible/], # broad-dash wide-dash/ ], 'solid', 'dotted', undef, "[ A ] -- solid --> [ B ]\n .. dotted ..> [ C ]\n - dashed - > [ D ]\n -- bold --> { style: bold; } [ E ]\n -- broad --> { style: broad; } [ F ]\n -- wide --> { style: wide; } [ G ]", ], arrowstyle => [ 'The style of the arrow. Open arrows are vee-shaped and the bit inside the arrow has the color of the L<background>. Closed arrows are triangle shaped, with a background-color fill. Filled arrows are closed, too, but use the L<fill> color for the inside. If the fill color is not set, the L<color> attribute will be used instead. An C<arrowstyle> of none creates undirected edges just like "[A] -- [B]" would do.', [ qw/none open closed filled/ ], 'open', 'closed', undef, "[ A ] -- open --> [ B ]\n -- closed --> { arrowstyle: closed; } [ C ]\n -- filled --> { arrowstyle: filled; } [ D ]\n -- filled --> { arrowstyle: filled; fill: lime; } [ E ]\n -- none --> { arrowstyle: none; } [ F ]", ], arrowshape => [ 'The basic shape of the arrow. Can be combined with each of L<arrowstyle>.', [ qw/triangle box dot inv line diamond cross x/ ], 'triangle', 'box', undef, "[ A ] -- triangle --> [ B ]\n -- box --> { arrowshape: box; } [ C ]\n" . " -- inv --> { arrowshape: inv; } [ D ]\n -- diamond --> { arrowshape: diamond; } [ E ]\n" . " -- dot --> { arrowshape: dot; } [ F ]\n" . " -- line --> { arrowshape: line; } [ G ] \n" . " -- plus --> { arrowshape: cross; } [ H ] \n" . " -- x --> { arrowshape: x; } [ I ] \n\n" . "[ a ] -- triangle --> { arrowstyle: filled; } [ b ]\n". " -- box --> { arrowshape: box; arrowstyle: filled; } [ c ]\n" . " -- inv --> { arrowshape: inv; arrowstyle: filled; } [ d ]\n" . " -- diamond --> { arrowshape: diamond; arrowstyle: filled; } [ e ]\n" . " -- dot --> { arrowshape: dot; arrowstyle: filled; } [ f ]\n" . " -- line --> { arrowshape: line; arrowstyle: filled; } [ g ] \n" . " -- plus --> { arrowshape: cross; arrowstyle: filled; } [ h ] \n" . " -- x --> { arrowshape: x; arrowstyle: filled; } [ i ] \n", ], labelcolor => [ 'The text color for the label. If unspecified, will fall back to L<color>. See the section about color names and values for reference.', undef, 'black', 'rgb(255,255,0)', ATTR_COLOR, "[ Bonn ] -- ICE --> { labelcolor: blue; }\n [ Berlin ]", ], start => [ 'The starting port of this edge. See <a href="hinting.html#joints">the section about joints</a> for reference.', qr/^(south|north|east|west|left|right|front|back)(\s*,\s*-?\d{1,4})?\z/, '', 'front, 0', ATTR_PORT, "[ Bonn ] -- NORTH --> { start: north; end: north; } [ Berlin ]", ], end => [ 'The ending port of this edge. See <a href="hinting.html#joints">the section about joints</a> for reference.', qr/^(south|north|east|west|right|left|front|back)(\s*,\s*-?\d{1,4})?\z/, '', 'back, 0', ATTR_PORT, "[ Bonn ] -- NORTH --> { start: south; end: east; } [ Berlin ]", ], minlen => [ 'The minimum length of the edge, in cells. Defaults to 1. The minimum length is ' . 'automatically increased for edges with joints.', undef, '1', '4', ATTR_UINT, "[ Bonn ] -- longer --> { minlen: 3; } [ Berlin ]\n[ Bonn ] --> [ Potsdam ] { origin: Bonn; offset: 2,2; }", ], autojoin => [ 'Controls whether the layouter can join this edge automatically with other edges leading to the same node. C<never> means this edge will never joined with another edge automatically, C<always> means always (if possible), even if the attributes on the edges do not match. C<equals> means only edges with the same set of attributes will be automatically joined together. See also C<autosplit>.', [qw/never always equals/], 'never', 'always', undef, "[ Bonn ], [ Aachen ]\n -- 1 --> { autojoin: equals; } [ Berlin ]", ], autosplit => [ 'Controls whether the layouter replace multiple edges leading from one node to other nodes with one edge splitting up. C<never> means this edge will never be part of such a split, C<always> means always (if possible), even if the attributes on the edges do not match. C<equals> means only edges with the same set of attributes will be automatically split up. See also C<autojoin>.', [qw/never always equals/], 'never', 'always', undef, "[ Bonn ]\n -- 1 --> { autosplit: equals; } [ Berlin ], [ Aachen ]", ], }, # edge group => { bordercolor => [ 'The color of the L<border>. See the section about color names and values for reference.', undef, { default => '#000000' }, 'rgb(255,255,0)', ATTR_COLOR, "node { border: black bold; }\n[ Black ]\n --> [ Red ] { bordercolor: red; }\n --> [ Green ] { bordercolor: green; }", ], borderstyle => [ 'The style of the L<border>. The special styles "bold", "broad", "wide", "double-dash" and "bold-dash" will set and override the L<borderwidth>.', [ qw/none solid dotted dashed dot-dash dot-dot-dash double wave bold bold-dash broad double-dash wide/ ], { default => 'none', 'node.anon' => 'none', 'group.anon' => 'none', node => 'solid', group => 'dashed' }, 'dotted', undef, "node { border: dotted; }\n[ Dotted ]\n --> [ Dashed ] { borderstyle: dashed; }\n --> [ broad ] { borderstyle: broad; }", ], borderwidth => [ 'The width of the L<border>. Certain L<border>-styles will override the width.', qr/^\d+(px|em)?\z/, '1', '2px', ], border => [ 'The border. Can be any combination of L<borderstyle>, L<bordercolor> and L<borderwidth>.', undef, { default => 'none', 'node.anon' => 'none', 'group.anon' => 'none', node => 'solid 1px #000000', group => 'dashed 1px #000000' }, 'dotted red', undef, "[ Normal ]\n --> [ Bold ] { border: bold; }\n --> [ Broad ] { border: broad; }\n --> [ Wide ] { border: wide; }\n --> [ Bold-Dash ] { border: bold-dash; }", ], nodeclass => [ 'The class into which all nodes of this group are put.', qr/^(|[a-zA-Z][a-zA-Z0-9_]*)\z/, '', 'cities', ], edgeclass => [ 'The class into which all edges defined in this group are put. This includes edges that run between two nodes belonging to the same group.', qr/^(|[a-zA-Z][a-zA-Z0-9_]*)\z/, '', 'connections', ], rank => [ "The rank of the group, used by the layouter to find the order and placement of group. " . "Set to C<auto> (the default), C<same> or a positive number. " . "See the section about ranks for reference and more examples.", qr/^(auto|same|\d{1,6})\z/, 'auto', 'same', undef, "( Cities: [ Bonn ], [ Berlin ] ) { rank: 0; } ( Rivers: [ Rhein ], [ Sieg ] ) { rank: 0; }", ], root => [ "The name of the root node, given as hint to the layouter to start the layout there. When not set, the layouter will pick a node at semi-random.", undef, '', 'My Node', ATTR_TEXT, "( Cities: [ A ] --> [ B ] --> [ C ] --> [ D ] --> [ A ] ) { root: B; }", ], group => [ "Puts the group inside this group, nesting the two groups inside each other.", undef, '', 'Cities', undef, "( Cities: [ Bonn ] ) ( Rivers: [ Rhein ] ) { group: Cities:; }", ], labelpos => [ "The position of the group label.", [ qw/top bottom/ ], 'top', 'bottom', ATTR_LIST, "group { labelpos: bottom; }\n\n ( My Group: [ Buxtehude ] -> [ Fuchsberg ] )\n" ], }, # group # These entries will be allowed temporarily during Graphviz parsing for # intermidiate values, like "shape=record". special => { }, }; # end of attribute definitions sub _allow_special_attributes { # store a hash with special temp. attributes my ($self, $att) = @_; $attributes->{special} = $att; } sub _drop_special_attributes { # drop the hash with special temp. attributes my ($self) = @_; $attributes->{special} = {}; } sub _attribute_entries { # for building the manual page $attributes; } sub border_attribute { # Return "1px solid red" from the border-(style|color|width) attributes, # mainly used by as_txt() output. Does not use colorscheme! my ($self, $class) = @_; my ($style,$width,$color); my $g = $self; $g = $self->{graph} if ref($self->{graph}); my ($def_style, $def_color, $def_width); # XXX TODO need no_default_attribute() if (defined $class) { $style = $g->attribute($class, 'borderstyle'); return $style if $style eq 'none'; $def_style = $g->default_attribute('borderstyle'); $width = $g->attribute($class,'borderwidth'); $def_width = $g->default_attribute($class,'borderwidth'); $width = '' if $def_width eq $width; $color = $g->attribute($class,'bordercolor'); $def_color = $g->default_attribute($class,'bordercolor'); $color = '' if $def_color eq $color; } else { $style = $self->attribute('borderstyle'); return $style if $style eq 'none'; $def_style = $self->default_attribute('borderstyle'); $width = $self->attribute('borderwidth'); $def_width = $self->default_attribute('borderwidth'); $width = '' if $def_width eq $width; $color = $self->attribute('bordercolor'); $def_color = $self->default_attribute('bordercolor'); $color = '' if $def_color eq $color; } return '' if $def_style eq $style and $color eq '' && $width eq ''; Graph::Easy::_border_attribute($style, $width, $color); } sub _unknown_attribute { # either just warn, or raise an error for unknown attributes my ($self, $name, $class) = @_; if ($self->{_warn_on_unknown_attributes}) { $self->warn("Ignoring unknown attribute '$name' for class $class") } else { $self->error("Error in attribute: '$name' is not a valid attribute name for a $class"); } return; } sub default_attribute { # Return the default value for the attribute. my ($self, $class, $name) = @_; # allow $self->default_attribute('fill'); if (scalar @_ == 2) { $name = $class; $class = $self->{class} || 'graph'; } # get the base class: node.foo => node my $base_class = $class; $base_class =~ s/\..*//; # Remap alias names without "-" to their hyphenated version: $name = $att_aliases->{$name} if exists $att_aliases->{$name}; # "x-foo-bar" is a custom attribute, so allow it always. The name must # consist only of letters and hyphens, and end in a letter or number. # Hyphens must be separated by letters. Custom attributes do not have a default. return '' if $name =~ $qr_custom_attribute; # prevent ->{special}->{node} from springing into existance my $s = $attributes->{special}; $s = $s->{$class} if exists $s->{$class}; my $entry = $s->{$name} || $attributes->{all}->{$name} || $attributes->{$base_class}->{$name}; # Didn't found an entry: return $self->_unknown_attribute($name,$class) unless ref($entry); # get the default attribute from the entry my $def = $entry->[ ATTR_DEFAULT_SLOT ]; my $val = $def; # "node.subclass" gets the default from "node", 'edge' from 'default': # " { default => 'foo', 'node.anon' => 'none', node => 'solid' }": if (ref $def) { $val = $def->{$class}; $val = $def->{$base_class} unless defined $val; $val = $def->{default} unless defined $val; } $val; } sub raw_attribute { # Return either the raw attribute set on an object (honoring inheritance), # or undef for when that specific attribute is not set. Does *not* # inspect class attributes. my ($self, $name) = @_; # Remap alias names without "-" to their hyphenated version: $name = $att_aliases->{$name} if exists $att_aliases->{$name}; my $class = $self->{class} || 'graph'; my $base_class = $class; $base_class =~ s/\..*//; # prevent ->{special}->{node} from springing into existance my $s = $attributes->{special}; $s = $s->{$class} if exists $s->{$class}; my $entry = $s->{$name} || $attributes->{all}->{$name} || $attributes->{$base_class}->{$name}; # create a fake entry for custom attributes $entry = [ '', undef, '', '', ATTR_STRING, '' ] if $name =~ $qr_custom_attribute; # Didn't found an entry: return $self->_unknown_attribute($name,$class) unless ref($entry); my $type = $entry->[ ATTR_TYPE_SLOT ] || ATTR_STRING; my $val; ########################################################################### # Check the object directly first my $a = $self->{att}; if (exists $a->{graph}) { # for graphs, look directly in the class to save time: $val = $a->{graph}->{$name} if exists $a->{graph}->{$name}; } else { $val = $a->{$name} if exists $a->{$name}; } # For "background", and objects that are in a group, we inherit "fill": $val = $self->{group}->color_attribute('fill') if $name eq 'background' && ref $self->{group}; return $val if !defined $val || $val ne 'inherit' || $name =~ /^x-([a-z_]+-)*[a-z_]+([0-9]*)\z/; # $val is defined, and "inherit" (and it is not a special attribute) # for graphs, there is nothing to inherit from return $val if $class eq 'graph'; # we try classes in this order: # "node", "graph" my @tries = (); # if the class is already "node", skip it: if ($class =~ /\./) { my $parent_class = $class; $parent_class =~ s/\..*//; push @tries, $parent_class; } # If not part of a graph, we cannot have class attributes, but # we still can find default attributes. So fake a "graph": my $g = $self->{graph}; # for objects in a graph $g = { att => {} } unless ref($g); # for objects not in a graph $val = undef; for my $try (@tries) { # print STDERR "# Trying class $try for attribute $name\n"; my $att = $g->{att}->{$try}; $val = $att->{$name} if exists $att->{$name}; # value was not defined, so get the default value if (!defined $val) { my $def = $entry->[ ATTR_DEFAULT_SLOT ]; $val = $def; # "node.subclass" gets the default from "node", 'edge' from 'default': # " { default => 'foo', 'node.anon' => 'none', node => 'solid' }": if (ref $def) { $val = $def->{$try}; if (!defined $val && $try =~ /\./) { my $base = $try; $base =~ s/\..*//; $val = $def->{$base}; } $val = $def->{default} unless defined $val; } } # $val must now be defined, because default value must exist. # print STDERR "# Found '$val' for $try\n"; if ($name ne 'label') { $self->warn("Uninitialized default for attribute '$name' on class '$try'\n") unless defined $val; } return $val if $type >= ATTR_NO_INHERIT; # got some value other than inherit or already at top of tree: return $val if defined $val && $val ne 'inherit'; # try next class in inheritance tree $val = undef; } $val; } sub color_attribute { # Just like get_attribute(), but for colors, and returns them as hex, # using the current colorscheme. my $self = shift; my $color = $self->attribute(@_); if ($color !~ /^#/ && $color ne '') { my $scheme = $self->attribute('colorscheme'); $color = Graph::Easy->color_as_hex($color, $scheme); } $color; } sub raw_color_attribute { # Just like raw_attribute(), but for colors, and returns them as hex, # using the current colorscheme. my $self = shift; my $color = $self->raw_attribute(@_); return undef unless defined $color; # default to undef if ($color !~ /^#/ && $color ne '') { my $scheme = $self->attribute('colorscheme'); $color = Graph::Easy->color_as_hex($color, $scheme); } $color; } sub _attribute_entry { # return the entry defining an attribute, based on the attribute name my ($self, $class, $name) = @_; # font-size => fontsize $name = $att_aliases->{$name} if exists $att_aliases->{$name}; my $base_class = $class; $base_class =~ s/\.(.*)//; # prevent ->{special}->{node} from springing into existance my $s = $attributes->{special}; $s = $s->{$class} if exists $s->{$class}; my $entry = $s->{$name} || $attributes->{all}->{$name} || $attributes->{$base_class}->{$name}; $entry; } sub attribute { my ($self, $class, $name) = @_; my $three_arg = 0; if (scalar @_ == 3) { # $self->attribute($class,$name) if only allowed on graphs return $self->error("Calling $self->attribute($class,$name) only allowed for graphs") if exists $self->{graph}; if ($class !~ /^(node|group|edge|graph\z)/) { return $self->error ("Illegal class '$class' when trying to get attribute '$name'"); } $three_arg = 1; return $self->border_attribute($class) if $name eq 'border'; # virtual attribute } else { # allow calls of the style get_attribute('background'); $name = $class; $class = $self->{class} || 'graph' if $name eq 'class'; # avoid deep recursion if ($name ne 'class') { $class = $self->{cache}->{class}; $class = $self->class() unless defined $class; } return $self->border_attribute() if $name eq 'border'; # virtual attribute return join (",",$self->size()) if $name eq 'size'; # virtual attribute } # print STDERR "# called attribute($class,$name)\n"; # font-size => fontsize $name = $att_aliases->{$name} if exists $att_aliases->{$name}; my $base_class = $class; $base_class =~ s/\.(.*)//; my $sub_class = $1; $sub_class = '' unless defined $sub_class; if ($name eq 'class') { # "[A] { class: red; }" => "red" return $sub_class if $sub_class ne ''; # "node { class: green; } [A]" => "green": fall through and let the code # below look up the attribute or fall back to the default '': } # prevent ->{special}->{node} from springing into existance my $s = $attributes->{special}; $s = $s->{$class} if exists $s->{$class}; my $entry = $s->{$name} || $attributes->{all}->{$name} || $attributes->{$base_class}->{$name}; # create a fake entry for custom attributes $entry = [ '', undef, '', '', ATTR_STRING, '' ] if $name =~ $qr_custom_attribute; # Didn't found an entry: return $self->_unknown_attribute($name,$class) unless ref($entry); my $type = $entry->[ ATTR_TYPE_SLOT ] || ATTR_STRING; my $val; if ($three_arg == 0) { ########################################################################### # Check the object directly first my $a = $self->{att}; if (exists $a->{graph}) { # for graphs, look directly in the class to save time: $val = $a->{graph}->{$name} if exists $a->{graph}->{$name}; } else { $val = $a->{$name} if exists $a->{$name}; } # For "background", and objects that are in a group, we inherit "fill": if ($name eq 'background' && $val && $val eq 'inherit') { my $parent = $self->parent(); $val = $parent->color_attribute('fill') if $parent && $parent != $self; } # XXX BENCHMARK THIS return $val if defined $val && # no inheritance ("inherit" is just a normal string value) ($type >= ATTR_NO_INHERIT || # no inheritance since value is something else like "red" $val ne 'inherit' || # for graphs, there is nothing to inherit from $class eq 'graph'); } # $val not defined, or 'inherit' ########################################################################### # Check the classes now # print STDERR "# Called self->attribute($class,$name) (#2)\n"; # we try them in this order: # node.subclass, node, graph # print STDERR "# $self->{name} class=$class ", join(" ", caller),"\n" if $name eq 'align'; my @tries = (); # skip "node.foo" if value is 'inherit' push @tries, $class unless defined $val; push @tries, $base_class if $class =~ /\./; push @tries, 'graph' unless @tries && $tries[-1] eq 'graph'; # If not part of a graph, we cannot have class attributes, but # we still can find default attributes. So fake a "graph": my $g = $self->{graph}; # for objects in a graph $g = { att => {} } unless ref($g); # for objects not in a graph # XXX TODO should not happen $g = $self if $self->{class} eq 'graph'; # for the graph itself $val = undef; TRY: for my $try (@tries) { # print STDERR "# Trying class $try for attribute $name\n" if $name eq 'align'; my $att = $g->{att}->{$try}; $val = $att->{$name} if exists $att->{$name}; # value was not defined, so get the default value (but not for subclasses!) if (!defined $val) { my $def = $entry->[ ATTR_DEFAULT_SLOT ]; $val = $def; # "node.subclass" gets the default from "node", 'edge' from 'default': # " { default => 'foo', 'node.anon' => 'none', node => 'solid' }": if (ref $def) { $val = $def->{$try}; if (!defined $val && $try =~ /\./) { my $base = $try; $base =~ s/\..*//; $val = $def->{$base}; } # if this is not a subclass, get the default value next TRY if !defined $val && $try =~ /\./; $val = $def->{default} unless defined $val; } } # $val must now be defined, because default value must exist. # print STDERR "# Found '$val' for $try ($class)\n" if $name eq 'color'; if ($name ne 'label') { $self->warn("Uninitialized default for attribute '$name' on class '$try'\n") unless defined $val; } return $val if $type >= ATTR_NO_INHERIT; # got some value other than inherit or already at top of tree: last if defined $val && ($val ne 'inherit' || $try eq 'graph'); # try next class in inheritance tree $val = undef; } # For "background", and objects that are in a group, we inherit "fill": if ($name eq 'background' && $val && $val eq 'inherit') { my $parent = $self->parent(); $val = $parent->color_attribute('fill') if $parent && $parent != $self; } # If we fell through here, $val is 'inherit' for graph. That happens # for instance for 'background': $val; } sub unquote_attribute { # The parser leaves quotes and escapes in the attribute, these things # are only removed upon storing the attribute at the object/class. # Return the attribute unquoted (remove quotes on labels, links etc). my ($self,$class,$name,$val) = @_; # clean quoted strings # XXX TODO # $val =~ s/^["'](.*[^\\])["']\z/$1/; $val =~ s/^["'](.*)["']\z/$1/; $val =~ s/\\([#"';\\])/$1/g; # reverse backslashed chars # remove any %00-%1f, %7f and high-bit chars to avoid exploits and problems $val =~ s/%[^2-7][a-fA-F0-9]|%7f//g; # decode %XX entities $val =~ s/%([2-7][a-fA-F0-9])/sprintf("%c",hex($1))/eg; $val; } sub valid_attribute { # Only for compatibility, use validate_attribute()! # Check that an name/value pair is an valid attribute, returns: # scalar value: valid, new attribute # undef: not valid # []: unknown attribute (might also warn) my ($self, $name, $value, $class) = @_; my ($error,$newname,$v) = $self->validate_attribute($name,$value,$class); return [] if defined $error && $error == 1; return undef if defined $error && $error == 2; $v; } sub validate_attribute { # Check that an name/value pair is an valid attribute, returns: # $error, $newname, @values # A possible new name is in $newname, this is f.i. used to convert # "font-size" # to "fontsize". # Upon errors, $error contains the error code: # undef: all went well # 1 unknown attribute name # 2 invalid attribute value # 4 found multiple attributes, but these aren't # allowed at this place my ($self, $name, $value, $class, $no_multiples) = @_; $self->error("Got reference $value as value, but expected scalar") if ref($value); $self->error("Got reference $name as name, but expected scalar") if ref($name); # "x-foo-bar" is a custom attribute, so allow it always. The name must # consist only of letters and hyphens, and end in a letter. Hyphens # must be separated by letters. return (undef, $name, $value) if $name =~ $qr_custom_attribute; $class = 'all' unless defined $class; $class =~ s/\..*\z//; # remove subclasses # Remap alias names without "-" to their hyphenated version: $name = $att_aliases->{$name} if exists $att_aliases->{$name}; # prevent ->{special}->{node} from springing into existance my $s = $attributes->{special}; $s = $s->{$class} if exists $s->{$class}; my $entry = $s->{$name} || $attributes->{all}->{$name} || $attributes->{$class}->{$name}; # Didn't found an entry: return (1,undef,$self->_unknown_attribute($name,$class)) unless ref($entry); my $check = $entry->[ATTR_MATCH_SLOT]; my $type = $entry->[ATTR_TYPE_SLOT] || ATTR_STRING; $check = '_color' if $type == ATTR_COLOR; $check = '_angle' if $type == ATTR_ANGLE; $check = '_uint' if $type == ATTR_UINT; my @values = ($value); # split on "|", but not on "\|" # XXX TODO: # This will not work in case of mixed " $i \|\| 0| $a = 1;" # When special attributes are set, we are parsing Graphviz, and do # not allow/use multiple attributes. So skip the split. if (keys %{$attributes->{special}} == 0) { @values = split (/\s*\|\s*/, $value, -1) if $value =~ /(^|[^\\])\|/; } my $multiples = 0; $multiples = 1 if @values > 1; return (4) if $no_multiples && $multiples; # | and no multiples => error # check each part on it's own my @rc; for my $v (@values) { # don't check empty parts for being valid push @rc, undef and next if $multiples && $v eq ''; if (defined $check && !ref($check)) { no strict 'refs'; my $checked = $self->$check($v, $name); if (!defined $checked) { $self->error("Error in attribute: '$v' is not a valid $name for a $class"); return (2); } push @rc, $checked; } elsif ($check) { if (ref($check) eq 'ARRAY') { # build a regexp from the list of words my $list = 'qr/^(' . join ('|', @$check) . ')\z/;'; $entry->[1] = eval($list); $check = $entry->[1]; } if ($v !~ $check) # invalid? { $self->error("Error in attribute: '$v' is not a valid $name for a $class"); return (2); } push @rc, $v; # valid } # entry found, but no specific check => anything goes as value else { push @rc, $v; } # "ClAss" => "class" for LCTEXT entries $rc[-1] = lc($rc[-1]) if $type == ATTR_LCTEXT; } # only one value ('green') return (undef, $name, $rc[0]) unless $multiples; # multiple values ('green|red') (undef, $name, \@rc); } ########################################################################### ########################################################################### sub _remap_attributes { # Take a hash with: # { # class => { # color => 'red' # } # } # and remap it according to the given remap hash (similiar structured). # Also encode/quote the value. Suppresses default attributes. my ($self, $object, $att, $remap, $noquote, $encode, $color_remap ) = @_; my $out = {}; my $class = $object || 'node'; $class = $object->{class} || 'graph' if ref($object); $class =~ s/\..*//; # remove subclass my $r = $remap->{$class}; my $ra = $remap->{all}; my $ral = $remap->{always}; my $x = $remap->{x}; # This loop does also handle the individual "bordercolor" attributes. # If the output should contain only "border", but not "bordercolor", then # the caller must filter them out. # do these attributes my @keys = sort keys %$att; my $color_scheme = 'w3c'; $color_scheme = $object->attribute('colorscheme') if ref($object); $color_scheme = $self->get_attribute($object,'colorscheme') if defined $object && !ref($object); $color_scheme = $self->get_attribute('graph','colorscheme') if defined $color_scheme && $color_scheme eq 'inherit'; for my $atr (@keys) { my $val = $att->{$atr}; # Only for objects (not for classes like "node"), and not if # always says we need to always call the CODE handler: if (!ref($object) && !exists $ral->{$atr}) { # attribute not defined next if !defined $val || $val eq '' || # or $remap says we should suppress it (exists $r->{$atr} && !defined $r->{$atr}) || (exists $ra->{$atr} && !defined $ra->{$atr}); } my $entry = $attributes->{all}->{$atr} || $attributes->{$class}->{$atr}; if ($color_remap && defined $entry && defined $val) { # look up whether attribute is a color # if yes, convert to hex $val = $self->color_as_hex($val,$color_scheme) if ($entry->[ ATTR_TYPE_SLOT ]||ATTR_STRING) == ATTR_COLOR; } my $temp = { $atr => $val }; # see if there is a handler for custom attributes if (exists $r->{$atr} || exists $ra->{$atr} || (defined $x && $atr =~ /^x-/)) { my $rc = $r->{$atr}; $rc = $ra->{$atr} unless defined $rc; $rc = $x unless defined $rc; # if given a code ref, call it to remap name and/or value if (ref($rc) eq 'CODE') { my @rc = &{$rc}($self,$atr,$val,$object); $temp = {}; while (@rc) { my $a = shift @rc; my $v = shift @rc; $temp->{ $a } = $v if defined $a && defined $v; } } else { # otherwise, rename the attribute name if nec. $temp = { }; $temp = { $rc => $val } if defined $val && defined $rc; } } for my $at (sort keys %$temp) { my $v = $temp->{$at}; next if !defined $at || !defined $v || $v eq ''; # encode critical characters (including "), but only if the value actually # contains anything else than '%' (so rgb(10%,0,0) stays as it is) $v =~ s/([;"%\x00-\x1f])/sprintf("%%%02x",ord($1))/eg if $encode && $v =~ /[;"\x00-\x1f]/; # quote if nec. $v = '"' . $v . '"' unless $noquote; $out->{$at} = $v; } } $out; } sub raw_attributes { # return all set attributes on this object (graph/node/group/edge) as # an anonymous hash ref my $self = shift; my $class = $self->{class} || 'graph'; my $att = $self->{att}; $att = $self->{att}->{graph} if $class eq 'graph'; my $g = $self->{graph} || $self; my $out = {}; if (!$g->{strict}) { for my $name (sort keys %$att) { my $val = $att->{$name}; next unless defined $val; # set to undef? $out->{$name} = $val; } return $out; } my $base_class = $class; $base_class =~ s/\..*//; for my $name (sort keys %$att) { my $val = $att->{$name}; next unless defined $val; # set to undef? $out->{$name} = $val; next unless $val eq 'inherit'; # prevent ->{special}->{node} from springing into existance my $s = $attributes->{special}; $s = $s->{$class} if exists $s->{$class}; my $entry = $s->{$name} || $attributes->{all}->{$name} || $attributes->{$base_class}->{$name}; # Didn't found an entry: return $self->_unknown_attribute($name,$class) unless ref($entry); my $type = $entry->[ ATTR_TYPE_SLOT ] || ATTR_STRING; # need to inherit value? $out->{$name} = $self->attribute($name) if $type < ATTR_NO_INHERIT; } $out; } sub get_attributes { # Return all effective attributes on this object (graph/node/group/edge) as # an anonymous hash ref. This respects inheritance and default values. # Does not return custom attributes, see get_custom_attributes(). my $self = shift; $self->error("get_attributes() doesn't take arguments") if @_ > 0; my $att = {}; my $class = $self->main_class(); # f.i. "all", "node" for my $type ('all', $class) { for my $a (sort keys %{$attributes->{$type}}) { my $val = $self->attribute($a); # respect inheritance $att->{$a} = $val if defined $val; } } $att; } package Graph::Easy::Node; BEGIN { *custom_attributes = \&get_custom_attributes; } sub get_custom_attributes { # Return all custom attributes on this object (graph/node/group/edge) as # an anonymous hash ref. my $self = shift; $self->error("get_custom_attributes() doesn't take arguments") if @_ > 0; my $att = {}; for my $key (sort keys %{$self->{att}}) { $att->{$key} = $self->{att}->{$key}; } $att; } 1; __END__ =head1 NAME Graph::Easy::Attributes - Define and check attributes for Graph::Easy =head1 SYNOPSIS use Graph::Easy; my $graph = Graph::Easy->new(); my $hexred = Graph::Easy->color_as_hex( 'red' ); my ($name, $value) = $graph->valid_attribute( 'color', 'red', 'graph' ); print "$name => $value\n" if !ref($value); =head1 DESCRIPTION C<Graph::Easy::Attributes> contains the definitions of valid attribute names and values for L<Graph::Easy|Graph::Easy>. It is used by both the parser and by Graph::Easy to check attributes for being valid and well-formed. There should be no need to use this module directly. For a complete list of attributes and their possible values, please see L<Graph::Easy::Manual>. =head1 EXPORT Exports nothing. =head1 SEE ALSO L<Graph::Easy>. =head1 AUTHOR Copyright (C) 2004 - 2008 by Tels L<http://bloodgate.com> See the LICENSE file for information. =cut �����������������������������������������������������Graph-Easy-0.73/lib/Graph/Easy/Base.pm��������������������������������������������������������������0000644�0000764�0000764�00000023320�11675050456�017230� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������############################################################################# # A baseclass for Graph::Easy objects like nodes, edges etc. # ############################################################################# package Graph::Easy::Base; $VERSION = '0.12'; use strict; ############################################################################# { # protected vars my $id = 0; sub _new_id { $id++; } sub _reset_id { $id = 0; } } ############################################################################# sub new { # Create a new object. This is a generic routine that is inherited # by many other things like Edge, Cell etc. my $self = bless { id => _new_id() }, shift; my $args = $_[0]; $args = { name => $_[0] } if ref($args) ne 'HASH' && @_ == 1; $args = { @_ } if ref($args) ne 'HASH' && @_ > 1; $self->_init($args); } sub _init { # Generic init routine, to be overriden in subclasses. my ($self,$args) = @_; $self; } sub self { my $self = shift; $self; } ############################################################################# sub no_fatal_errors { my $self = shift; $self->{fatal_errors} = ($_[1] ? 1 : 0) if @_ > 0; ~ ($self->{fatal_errors} || 0); } sub fatal_errors { my $self = shift; $self->{fatal_errors} = ($_[1] ? 0 : 1) if @_ > 0; $self->{fatal_errors} || 0; } sub error { my $self = shift; # If we switched to a temp. Graphviz parser, then set the error on the # original parser object, too: $self->{_old_self}->error(@_) if ref($self->{_old_self}); # if called on a member on a graph, call error() on the graph itself: return $self->{graph}->error(@_) if ref($self->{graph}); if (defined $_[0]) { $self->{error} = $_[0]; if ($self->{_catch_errors}) { push @{$self->{_errors}}, $self->{error}; } else { $self->_croak($self->{error}, 2) if ($self->{fatal_errors}) && $self->{error} ne ''; } } $self->{error} || ''; } sub error_as_html { # return error() properly escaped my $self = shift; my $msg = $self->{error}; $msg =~ s/&/&/g; $msg =~ s/</</g; $msg =~ s/>/>/g; $msg =~ s/"/"/g; $msg; } sub catch_messages { # Catch all warnings (and errors if no_fatal_errors() was used) # these can later be retrieved with warnings() and errors(): my $self = shift; if (@_ > 0) { if ($_[0]) { $self->{_catch_warnings} = 1; $self->{_catch_errors} = 1; $self->{_warnings} = []; $self->{_errors} = []; } else { $self->{_catch_warnings} = 0; $self->{_catch_errors} = 0; } } $self; } sub catch_warnings { # Catch all warnings # these can later be retrieved with warnings(): my $self = shift; if (@_ > 0) { if ($_[0]) { $self->{_catch_warnings} = 1; $self->{_warnings} = []; } else { $self->{_catch_warnings} = 0; } } $self->{_catch_warnings}; } sub catch_errors { # Catch all errors # these can later be retrieved with errors(): my $self = shift; if (@_ > 0) { if ($_[0]) { $self->{_catch_errors} = 1; $self->{_errors} = []; } else { $self->{_catch_errors} = 0; } } $self->{_catch_errors}; } sub warnings { # return all warnings that occured after catch_messages(1) my $self = shift; @{$self->{_warnings}}; } sub errors { # return all errors that occured after catch_messages(1) my $self = shift; @{$self->{_errors}}; } sub warn { my ($self, $msg) = @_; if ($self->{_catch_warnings}) { push @{$self->{_warnings}}, $msg; } else { require Carp; Carp::carp('Warning: ' . $msg); } } sub _croak { my ($self, $msg, $level) = @_; $level = 1 unless defined $level; require Carp; if (ref($self) && $self->{debug}) { $Carp::CarpLevel = $level; # don't report Base itself Carp::confess($msg); } else { Carp::croak($msg); } } ############################################################################# # class management sub sub_class { # get/set the subclass my $self = shift; if (defined $_[0]) { $self->{class} =~ s/\..*//; # nix subclass $self->{class} .= '.' . $_[0]; # append new one delete $self->{cache}; $self->{cache}->{subclass} = $_[0]; $self->{cache}->{class} = $self->{class}; return; } $self->{class} =~ /\.(.*)/; return $1 if defined $1; return $self->{cache}->{subclass} if defined $self->{cache}->{subclass}; # Subclass not defined, so check our base class for a possible set class # attribute and return this: # take a shortcut my $g = $self->{graph}; if (defined $g) { my $subclass = $g->{att}->{$self->{class}}->{class}; $subclass = '' unless defined $subclass; $self->{cache}->{subclass} = $subclass; $self->{cache}->{class} = $self->{class}; return $subclass; } # not part of a graph? $self->{cache}->{subclass} = $self->attribute('class'); } sub class { # return our full class name like "node.subclass" or "node" my $self = shift; $self->error("class() method does not take arguments") if @_ > 0; $self->{class} =~ /\.(.*)/; return $self->{class} if defined $1; return $self->{cache}->{class} if defined $self->{cache}->{class}; # Subclass not defined, so check our base class for a possible set class # attribute and return this: my $subclass; # take a shortcut: my $g = $self->{graph}; if (defined $g) { $subclass = $g->{att}->{$self->{class}}->{class}; $subclass = '' unless defined $subclass; } $subclass = $self->{att}->{class} unless defined $subclass; $subclass = '' unless defined $subclass; $self->{cache}->{subclass} = $subclass; $subclass = '.' . $subclass if $subclass ne ''; $self->{cache}->{class} = $self->{class} . $subclass; } sub main_class { my $self = shift; $self->{class} =~ /^(.+?)(\.|\z)/; # extract first part $1; } 1; __END__ =head1 NAME Graph::Easy::Base - base class for Graph::Easy objects like nodes, edges etc =head1 SYNOPSIS package Graph::Easy::My::Node; use Graph::Easy::Base; @ISA = qw/Graph::Easy::Base/; =head1 DESCRIPTION Used automatically and internally by L<Graph::Easy> - should not be used directly. =head1 METHODS =head2 new() my $object = Graph::Easy::Base->new(); Create a new object, and call C<_init()> on it. =head2 error() $last_error = $object->error(); $object->error($error); # set new messags $object->error(''); # clear the error Returns the last error message, or '' for no error. When setting a new error message, C<< $self->_croak($error) >> will be called unless C<< $object->no_fatal_errors() >> is true. =head2 error_as_html() my $error = $object->error_as_html(); Returns the same error message as L<error()>, but properly escaped as HTML so it is safe to output to the client. =head2 warn() $object->warn('Warning!'); Warn on STDERR with the given message. =head2 no_fatal_errors() $object->no_fatal_errors(1); Set the flag that determines whether setting an error message via C<error()> is fatal, e.g. results in a call to C<_croak()>. A true value will make errors non-fatal. See also L<fatal_errors>. =head2 fatal_errors() $fatal = $object->fatal_errors(); $object->fatal_errors(0); # turn off $object->fatal_errors(1); # turn on Set/get the flag that determines whether setting an error message via C<error()> is fatal, e.g. results in a call to C<_croak()>. A true value makes errors fatal. =head2 catch_errors() my $catch_errors = $object->catch_errors(); # query $object->catch_errors(1); # enable $object->...(); # some error if ($object->error()) { my @errors = $object->errors(); # retrieve } Enable/disable catching of all error messages. When enabled, all previously caught error messages are thrown away, and from this poin on new errors are non-fatal and stored internally. You can retrieve these errors later with the errors() method. =head2 catch_warnings() my $catch_warns = $object->catch_warnings(); # query $object->catch_warnings(1); # enable $object->...(); # some error if ($object->warning()) { my @warnings = $object->warnings(); # retrieve } Enable/disable catching of all warnings. When enabled, all previously caught warning messages are thrown away, and from this poin on new warnings are stored internally. You can retrieve these errors later with the errors() method. =head2 catch_messages() # catch errors and warnings $object->catch_messages(1); # stop catching errors and warnings $object->catch_messages(0); A true parameter is equivalent to: $object->catch_warnings(1); $object->catch_errors(1); See also: L<catch_warnings()> and L<catch_errors()> as well as L<errors()> and L<warnings()>. =head2 errors() my @errors = $object->errors(); Return all error messages that occured after L<catch_messages()> was called. =head2 warnings() my @warnings = $object->warnings(); Return all warning messages that occured after L<catch_messages()> or L<catch_errors()> was called. =head2 self() my $self = $object->self(); Returns the object itself. =head2 class() my $class = $object->class(); Returns the full class name like C<node.cities>. See also C<sub_class>. =head2 sub_class() my $sub_class = $object->sub_class(); Returns the sub class name like C<cities>. See also C<class>. =head2 main_class() my $main_class = $object->main_class(); Returns the main class name like C<node>. See also C<sub_class>. =head1 EXPORT None by default. =head1 SEE ALSO L<Graph::Easy>. =head1 AUTHOR Copyright (C) 2004 - 2008 by Tels L<http://bloodgate.com>. See the LICENSE file for more details. X<tels> X<bloodgate> X<license> X<gpl> =cut ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/lib/Graph/Easy/Parser.pm������������������������������������������������������������0000644�0000764�0000764�00000136011�12150072445�017603� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������############################################################################# # Parse text definition into a Graph::Easy object # ############################################################################# package Graph::Easy::Parser; use Graph::Easy; $VERSION = '0.35'; use Graph::Easy::Base; @ISA = qw/Graph::Easy::Base/; use Scalar::Util qw/weaken/; use strict; use constant NO_MULTIPLES => 1; use Graph::Easy::Util qw(ord_values); sub _init { my ($self,$args) = @_; $self->{error} = ''; $self->{debug} = 0; $self->{fatal_errors} = 1; foreach my $k (sort keys %$args) { if ($k !~ /^(debug|fatal_errors)\z/) { require Carp; my $class = ref($self); Carp::confess ("Invalid argument '$k' passed to $class" . '->new()'); } $self->{$k} = $args->{$k}; } # what to replace the matched text with $self->{replace} = ''; $self->{attr_sep} = ':'; # An optional regexp to remove parts of an autosplit label, used by Graphviz # to remove " <p1> ": $self->{_qr_part_clean} = undef; # setup default class names for generated objects $self->{use_class} = { edge => 'Graph::Easy::Edge', group => 'Graph::Easy::Group', graph => 'Graph::Easy', node => 'Graph::Easy::Node', }; $self; } sub reset { # reset the status of the parser, clear errors etc. my $self = shift; $self->{error} = ''; $self->{anon_id} = 0; $self->{cluster_id} = ''; # each cluster gets a unique ID $self->{line_nr} = -1; $self->{match_stack} = []; # patterns and their handlers $self->{clusters} = {}; # cluster names we already created Graph::Easy::Base::_reset_id(); # start with the same set of IDs # After "[ 1 ] -> [ 2 ]" we push "2" on the stack and when we encounter # " -> [ 3 ]" treat the stack as a node-list left of "3". # In addition, for " [ 1 ], [ 2 ] => [ 3 ]", the stack will contain # "1" and "2" when we encounter "3". $self->{stack} = []; $self->{group_stack} = []; # all the (nested) groups we are currently in $self->{left_stack} = []; # stack for the left side for "[]->[],[],..." $self->{left_edge} = undef; # for -> [A], [B] continuations Graph::Easy->_drop_special_attributes(); $self->{_graph} = $self->{use_class}->{graph}->new( { debug => $self->{debug}, strict => 0, fatal_errors => $self->{fatal_errors}, } ); $self; } sub from_file { # read in entire file and call from_text() on the contents my ($self,$file) = @_; $self = $self->new() unless ref $self; my $doc; local $/ = undef; # slurp mode # if given a reference, assume it is a glob, or something like that if (ref($file)) { binmode $file, ':utf8' or die ("binmode '$file', ':utf8' failed: $!"); $doc = <$file>; } else { open my $PARSER_FILE, $file or die (ref($self).": Cannot read $file: $!"); binmode $PARSER_FILE, ':utf8' or die ("binmode '$file', ':utf8' failed: $!"); $doc = <$PARSER_FILE>; # read entire file close $PARSER_FILE; } $self->from_text($doc); } sub use_class { # use the provided class for generating objects of the type $object my ($self, $object, $class) = @_; $self->_croak("Expected one of node, edge, group or graph, but got $object") unless $object =~ /^(node|group|graph|edge)\z/; $self->{use_class}->{$object} = $class; $self; } sub _register_handler { # register a pattern and a handler for it my $self = shift; push @{$self->{match_stack}}, [ @_ ]; $self; } sub _register_attribute_handler { # register a handler for attributes like "{ color: red; }" my ($self, $qr_attr, $target) = @_; # $object is either undef (for Graph::Easy, meaning "node", or "parent" for Graphviz) # { attributes } $self->_register_handler( qr/^$qr_attr/, sub { my $self = shift; # This happens in the case of "[ Test ]\n { ... }", the node is consumed # first, and the attributes are left over: my $stack = $self->{stack}; $stack = $self->{group_stack} if @{$self->{stack}} == 0; my $object = $target; if ($target && $target eq 'parent') { # for Graphviz, stray attributes always apply to the parent $stack = $self->{group_stack}; $object = $stack->[-1] if ref $stack; if (!defined $object) { # try the scope stack next: $stack = $self->{scope_stack}; $object = $self->{_graph}; if (!$stack || @$stack <= 1) { $object = $self->{_graph}; $stack = [ $self->{_graph} ]; } } } my ($a, $max_idx) = $self->_parse_attributes($1||'', $object); return undef if $self->{error}; # wrong attributes or empty stack? if (ref($stack->[-1]) eq 'HASH') { # stack is a scope stack # XXX TODO: Find out wether the attribute goes to graph, node or edge for my $k (sort keys %$a) { $stack->[-1]->{graph}->{$k} = $a->{$k}; } return 1; } print STDERR "max_idx = $max_idx, stack contains ", join (" , ", @$stack),"\n" if $self->{debug} && $self->{debug} > 1; if ($max_idx != 1) { my $i = 0; for my $n (@$stack) { $n->set_attributes($a, $i++); } } else { # set attributes on all nodes/groups on stack for my $n (@$stack) { $n->set_attributes($a); } } # This happens in the case of "[ a | b ]\n { ... }", the node is consumed # first, and the attributes are left over. And if we encounter a basename # attribute here, the node-parts will already have been created with the # wrong basename, so correct this: if (defined $a->{basename}) { for my $s (@$stack) { # for every node on the stack that is the primary one $self->_set_new_basename($s, $a->{basename}) if exists $s->{autosplit_parts}; } } 1; } ); } sub _register_node_attribute_handler { # register a handler for attributes like "[ A ] { ... }" my ($self, $qr_node, $qr_oatr) = @_; $self->_register_handler( qr/^$qr_node$qr_oatr/, sub { my $self = shift; my $n1 = $1; my $a1 = $self->_parse_attributes($2||''); return undef if $self->{error}; $self->{stack} = [ $self->_new_node ($self->{_graph}, $n1, $self->{group_stack}, $a1) ]; # forget left stack $self->{left_edge} = undef; $self->{left_stack} = []; 1; } ); } sub _new_group { # create a new (possible anonymous) group my ($self, $name) = @_; $name = '' unless defined $name; my $gr = $self->{use_class}->{group}; my $group; if ($name eq '') { print STDERR "# Creating new anon group.\n" if $self->{debug}; $gr .= '::Anon'; $group = $gr->new(); } else { $name = $self->_unquote($name); print STDERR "# Creating new group '$name'.\n" if $self->{debug}; $group = $gr->new( name => $name ); } $self->{_graph}->add_group($group); my $group_stack = $self->{group_stack}; if (@$group_stack > 0) { $group->set_attribute('group', $group_stack->[-1]->{name}); } $group; } sub _add_group_match { # register two handlers for group start/end my $self = shift; my $qr_group_start = $self->_match_group_start(); my $qr_group_end = $self->_match_group_end(); my $qr_oatr = $self->_match_optional_attributes(); # "( group start [" or empty group like "( Group )" $self->_register_handler( qr/^$qr_group_start/, sub { my $self = shift; my $graph = $self->{_graph}; my $end = $2; $end = '' unless defined $end; # repair the start of the next node/group $self->{replace} = '[' if $end eq '['; $self->{replace} = '(' if $end eq '('; # create the new group my $group = $self->_new_group($1); if ($end eq ')') { # we matched an empty group like "()", or "( group name )" $self->{stack} = [ $group ]; print STDERR "# Seen end of group '$group->{name}'.\n" if $self->{debug}; } else { # only put the group on the stack if it is still open push @{$self->{group_stack}}, $group; } 1; } ); # ") { }" # group end (with optional attributes) $self->_register_handler( qr/^$qr_group_end$qr_oatr/, sub { my $self = shift; my $group = pop @{$self->{group_stack}}; return $self->parse_error(0) if !defined $group; print STDERR "# Seen end of group '$group->{name}'.\n" if $self->{debug}; my $a1 = $self->_parse_attributes($1||'', 'group', NO_MULTIPLES); return undef if $self->{error}; $group->set_attributes($a1); # the new left side is the group itself $self->{stack} = [ $group ]; 1; } ); } sub _build_match_stack { # put all known patterns and their handlers on the match stack my $self = shift; # regexps for the different parts my $qr_node = $self->_match_node(); my $qr_attr = $self->_match_attributes(); my $qr_oatr = $self->_match_optional_attributes(); my $qr_edge = $self->_match_edge(); my $qr_comma = $self->_match_comma(); my $qr_class = $self->_match_class_selector(); my $e = $self->{use_class}->{edge}; # node { color: red; } # node.graph { ... } # .foo { ... } # .foo, node, edge.red { ... } $self->_register_handler( qr/^\s*$qr_class$qr_attr/, sub { my $self = shift; my $class = lc($1 || ''); my $att = $self->_parse_attributes($2 || '', $class, NO_MULTIPLES ); return undef unless defined $att; # error in attributes? my $graph = $self->{_graph}; $graph->set_attributes ( $class, $att); # forget stacks $self->{stack} = []; $self->{left_edge} = undef; $self->{left_stack} = []; 1; } ); $self->_add_group_match(); $self->_register_attribute_handler($qr_attr); $self->_register_node_attribute_handler($qr_node,$qr_oatr); # , [ Berlin ] { color: red; } $self->_register_handler( qr/^$qr_comma$qr_node$qr_oatr/, sub { my $self = shift; my $graph = $self->{_graph}; my $n1 = $1; my $a1 = $self->_parse_attributes($2||''); return undef if $self->{error}; push @{$self->{stack}}, $self->_new_node ($graph, $n1, $self->{group_stack}, $a1, $self->{stack}); if (defined $self->{left_edge}) { my ($style, $edge_label, $edge_atr, $edge_bd, $edge_un) = @{$self->{left_edge}}; foreach my $node (@{$self->{left_stack}}) { my $edge = $e->new( { style => $style, name => $edge_label } ); $edge->set_attributes($edge_atr); # "<--->": bidirectional $edge->bidirectional(1) if $edge_bd; $edge->undirected(1) if $edge_un; $graph->add_edge ( $node, $self->{stack}->[-1], $edge ); } } 1; } ); # Things like "[ Node ]" will be consumed before, so we do not need a case # for "[ A ] -> [ B ]": # node chain continued like "-> { ... } [ Kassel ] { ... }" $self->_register_handler( qr/^$qr_edge$qr_oatr$qr_node$qr_oatr/, sub { my $self = shift; return if @{$self->{stack}} == 0; # only match this if stack non-empty my $graph = $self->{_graph}; my $eg = $1; # entire edge ("-- label -->" etc) my $edge_bd = $2 || $4; # bidirectional edge ('<') ? my $edge_un = 0; # undirected edge? $edge_un = 1 if !defined $2 && !defined $5; # optional edge label my $edge_label = $7; my $ed = $3 || $5 || $1; # edge pattern/style ("--") my $edge_atr = $11 || ''; # save edge attributes my $n = $12; # node name my $a1 = $self->_parse_attributes($13||''); # node attributes $edge_atr = $self->_parse_attributes($edge_atr, 'edge'); return undef if $self->{error}; # allow undefined edge labels for setting them from the class # strip trailing spaces and convert \[ => [ $edge_label = $self->_unquote($edge_label) if defined $edge_label; # strip trailing spaces $edge_label =~ s/\s+\z// if defined $edge_label; # the right side node(s) (multiple in case of autosplit) my $nodes_b = [ $self->_new_node ($self->{_graph}, $n, $self->{group_stack}, $a1) ]; my $style = $self->_link_lists( $self->{stack}, $nodes_b, $ed, $edge_label, $edge_atr, $edge_bd, $edge_un); # remember the left side $self->{left_edge} = [ $style, $edge_label, $edge_atr, $edge_bd, $edge_un ]; $self->{left_stack} = $self->{stack}; # forget stack and remember the right side instead $self->{stack} = $nodes_b; 1; } ); my $qr_group_start = $self->_match_group_start(); # Things like ")" will be consumed before, so we do not need a case # for ") -> { ... } ( Group [ B ]": # edge to a group like "-> { ... } ( Group [" $self->_register_handler( qr/^$qr_edge$qr_oatr$qr_group_start/, sub { my $self = shift; return if @{$self->{stack}} == 0; # only match this if stack non-empty my $eg = $1; # entire edge ("-- label -->" etc) my $edge_bd = $2 || $4; # bidirectional edge ('<') ? my $edge_un = 0; # undirected edge? $edge_un = 1 if !defined $2 && !defined $5; # optional edge label my $edge_label = $7; my $ed = $3 || $5 || $1; # edge pattern/style ("--") my $edge_atr = $11 || ''; # save edge attributes my $gn = $12; # matched "-> ( Group [" or "-> ( Group (" $self->{replace} = '[' if defined $13 && $13 eq '['; $self->{replace} = '(' if defined $13 && $13 eq '('; $edge_atr = $self->_parse_attributes($edge_atr, 'edge'); return undef if $self->{error}; # get the last group of the stack, lest the new one gets nested in it pop @{$self->{group_stack}}; $self->{group_stack} = [ $self->_new_group($gn) ]; # allow undefined edge labels for setting them from the class $edge_label = $self->_unquote($edge_label) if $edge_label; # strip trailing spaces $edge_label =~ s/\s+\z// if $edge_label; my $style = $self->_link_lists( $self->{stack}, $self->{group_stack}, $ed, $edge_label, $edge_atr, $edge_bd, $edge_un); # remember the left side $self->{left_edge} = [ $style, $edge_label, $edge_atr, $edge_bd, $edge_un ]; $self->{left_stack} = $self->{stack}; # forget stack $self->{stack} = []; # matched "->()" so remember the group on the stack $self->{stack} = [ $self->{group_stack}->[-1] ] if defined $13 && $13 eq ')'; 1; } ); } sub _line_insert { # what to insert between two lines, '' for Graph::Easy, ' ' for Graphviz; ''; } sub _clean_line { # do some cleanups on a line before handling it my ($self,$line) = @_; chomp($line); # convert #808080 into \#808080, and "#fff" into "\#fff" my $sep = $self->{attr_sep}; $line =~ s/$sep\s*("?)(#(?:[a-fA-F0-9]{6}|[a-fA-F0-9]{3}))("?)/$sep $1\\$2$3/g; # remove comment at end of line (but leave \# alone): $line =~ s/(:[^\\]|)$self->{qr_comment}.*/$1/; # remove white space at end (but not at the start, to keep " ||" intact $line =~ s/\s+\z//; # print STDERR "# at line '$line' stack: ", join(",",@{ $self->{stack}}),"\n"; $line; } sub from_text { my ($self,$txt) = @_; # matches a multi-line comment my $o_cmt = qr#((\s*/\*.*?\*/\s*)*\s*|\s+)#; if ((ref($self)||$self) eq 'Graph::Easy::Parser' && # contains "digraph GRAPH {" or something similiar ( $txt =~ /^(\s*|\s*\/\*.*?\*\/\s*)(strict)?$o_cmt(di)?graph$o_cmt("[^"]*"|[\w_]+)$o_cmt\{/im || # contains "digraph {" or something similiar $txt =~ /^(\s*|\s*\/\*.*?\*\/\s*)(strict)?${o_cmt}digraph$o_cmt\{/im || # contains "strict graph {" or something similiar $txt =~ /^(\s*|\s*\/\*.*?\*\/\s*)strict${o_cmt}(di)?graph$o_cmt\{/im)) { require Graph::Easy::Parser::Graphviz; # recreate ourselfes, and pass our arguments along my $debug = 0; my $old_self = $self; if (ref($self)) { $debug = $self->{debug}; $self->{fatal_errors} = 0; } $self = Graph::Easy::Parser::Graphviz->new( debug => $debug, fatal_errors => 0 ); $self->reset(); $self->{_old_self} = $old_self if ref($self); } if ((ref($self)||$self) eq 'Graph::Easy::Parser' && # contains "graph: {" $txt =~ /^([\s\n\t]*|\s*\/\*.*?\*\/\s*)graph\s*:\s*\{/m) { require Graph::Easy::Parser::VCG; # recreate ourselfes, and pass our arguments along my $debug = 0; my $old_self = $self; if (ref($self)) { $debug = $self->{debug}; $self->{fatal_errors} = 0; } $self = Graph::Easy::Parser::VCG->new( debug => $debug, fatal_errors => 0 ); $self->reset(); $self->{_old_self} = $old_self if ref($self); } $self = $self->new() unless ref $self; $self->reset(); my $graph = $self->{_graph}; return $graph if !defined $txt || $txt =~ /^\s*\z/; # empty text? my $uc = $self->{use_class}; # instruct the graph to use the custom classes, too for my $o (sort keys %$uc) { $graph->use_class($o, $uc->{$o}) unless $o eq 'graph'; # group, node and edge } my @lines = split /(\r\n|\n|\r)/, $txt; my $backbuffer = ''; # left over fragments to be combined with next line my $qr_comment = $self->_match_commented_line(); $self->{qr_comment} = $self->_match_comment(); # cache the value of this since it can be expensive to construct: $self->{_match_single_attribute} = $self->_match_single_attribute(); $self->_build_match_stack(); ########################################################################### # main parsing loop my $handled = 0; # did we handle a fragment? my $line; # my $counts = {}; LINE: while (@lines > 0 || $backbuffer ne '') { # only accumulate more text if we didn't handle a fragment if (@lines > 0 && $handled == 0) { $self->{line_nr}++; my $curline = shift @lines; # discard empty lines, or completely commented out lines next if $curline =~ $qr_comment; # convert tabs to spaces (the regexps don't expect tabs) $curline =~ tr/\t/ /; # combine backbuffer, what to insert between two lines and next line: $line = $backbuffer . $self->_line_insert() . $self->_clean_line($curline); } print STDERR "# Line is '$line'\n" if $self->{debug} && $self->{debug} > 2; print STDERR "# Backbuffer is '$backbuffer'\n" if $self->{debug} && $self->{debug} > 2; $handled = 0; #debug my $count = 0; PATTERN: for my $entry (@{$self->{match_stack}}) { # nothing to match against? last PATTERN if $line eq ''; $self->{replace} = ''; # as default just remove the matched text my ($pattern, $handler, $replace) = @$entry; print STDERR "# Matching against $pattern\n" if $self->{debug} && $self->{debug} > 3; if ($line =~ $pattern) { #debug $counts->{$count}++; print STDERR "# Matched, calling handler\n" if $self->{debug} && $self->{debug} > 2; my $rc = 1; $rc = &$handler($self) if defined $handler; if ($rc) { $replace = $self->{replace} unless defined $replace; $replace = &$replace($self,$line) if ref($replace); print STDERR "# Handled it successfully.\n" if $self->{debug} && $self->{debug} > 2; $line =~ s/$pattern/$replace/; print STDERR "# Line is now '$line' (replaced with '$replace')\n" if $self->{debug} && $self->{debug} > 2; $handled++; last PATTERN; } } #debug $count ++; } #debug if ($handled == 0) { $counts->{'-1'}++; } # couldn't handle that fragement, so accumulate it and try again $backbuffer = $line; # stop at the very last line last LINE if $handled == 0 && @lines == 0; # stop at parsing errors last LINE if $self->{error}; } $self->error("'$backbuffer' not recognized by " . ref($self)) if $backbuffer ne ''; # if something was left on the stack, file ended unexpectedly $self->parse_error(7) if !$self->{error} && $self->{scope_stack} && @{$self->{scope_stack}} > 0; return undef if $self->{error} && $self->{fatal_errors}; #debug use Data::Dumper; print Dumper($counts); print STDERR "# Parsing done.\n" if $graph->{debug}; # Do final cleanup (for parsing Graphviz) $self->_parser_cleanup() if $self->can('_parser_cleanup'); $graph->_drop_special_attributes(); # turn on strict checking on returned graph $graph->strict(1); $graph->fatal_errors(1); $graph; } ############################################################################# # internal routines sub _edge_style { my ($self, $ed) = @_; my $style = undef; # default is "inherit from class" $style = 'double-dash' if $ed =~ /^(= )+\z/; $style = 'double' if $ed =~ /^=+\z/; $style = 'dotted' if $ed =~ /^\.+\z/; $style = 'dashed' if $ed =~ /^(- )+\z/; $style = 'dot-dot-dash' if $ed =~ /^(..-)+\z/; $style = 'dot-dash' if $ed =~ /^(\.-)+\z/; $style = 'wave' if $ed =~ /^\~+\z/; $style = 'bold' if $ed =~ /^#+\z/; $style; } sub _link_lists { # Given two node lists and an edge style, links each node from list # one to list two. my ($self, $left, $right, $ed, $label, $edge_atr, $edge_bd, $edge_un) = @_; my $graph = $self->{_graph}; my $style = $self->_edge_style($ed); my $e = $self->{use_class}->{edge}; # add edges for all nodes in the left list for my $node (@$left) { for my $node_b (@$right) { my $edge = $e->new( { style => $style, name => $label } ); $graph->add_edge ( $node, $node_b, $edge ); # 'string' => [ 'string' ] # [ { hash }, 'string' ] => [ { hash }, 'string' ] my $e = $edge_atr; $e = [ $edge_atr ] unless ref($e) eq 'ARRAY'; for my $a (@$e) { if (ref $a) { $edge->set_attributes($a); } else { # deferred parsing with the object as param: my $out = $self->_parse_attributes($a, $edge); return undef if $self->{error}; $edge->set_attributes($out); } } # "<--->": bidirectional $edge->bidirectional(1) if $edge_bd; $edge->undirected(1) if $edge_un; } } $style; } sub _unquote_attribute { my ($self,$name,$value) = @_; $self->_unquote($value); } sub _unquote { my ($self, $name, $no_collapse) = @_; $name = '' unless defined $name; # unquote special chars $name =~ s/\\([\[\(\{\}\]\)#<>\-\.\=])/$1/g; # collapse multiple spaces $name =~ s/\s+/ /g unless $no_collapse; $name; } sub _add_node { # add a node to the graph, overidable by subclasses my ($self, $graph, $name) = @_; $graph->add_node($name); # add unless exists } sub _get_cluster_name { # create a unique name for an autosplit node my ($self, $base_name) = @_; # Try to find a unique cluster name in case some one get's creative and names the # last part "-1": # does work without cluster-id? if (exists $self->{clusters}->{$base_name}) { my $g = 1; while ($g == 1) { my $base_try = $base_name; $base_try .= '-' . $self->{cluster_id} if $self->{cluster_id}; last if !exists $self->{clusters}->{$base_try}; $self->{cluster_id}++; } $base_name .= '-' . $self->{cluster_id} if $self->{cluster_id}; $self->{cluster_id}++; } $self->{clusters}->{$base_name} = undef; # reserve this name $base_name; } sub _set_new_basename { # when encountering something like: # [ a | b ] # { basename: foo; } # the Parser will create two nodes, ab.0 and ab.1, and then later see # the "basename: foo". Sowe need to rename the already created nodes # due to the changed basename: my ($self, $node, $new_basename) = @_; # nothing changes? return if $node->{autosplit_basename} eq $new_basename; my $g = $node->{graph}; my @parts = @{$node->{autosplit_parts}}; my $nr = 0; for my $part ($node, @parts) { print STDERR "# Setting new basename $new_basename for node $part->{name}\n" if $self->{debug} > 1; $part->{autosplit_basename} = $new_basename; $part->set_attribute('basename', $new_basename); # delete it from the list of nodes delete $g->{nodes}->{$part->{name}}; $part->{name} = $new_basename . '.' . $nr; $nr++; # and re-insert it with the right name $g->{nodes}->{$part->{name}} = $part; # we do not need to care for edges here, as they are stored with refs # to the nodes and not the node names itself } } sub _autosplit_node { # Takes a node name like "a|b||c" and splits it into "a", "b", and "c". # Returns the individual parts. my ($self, $graph, $name, $att, $allow_empty) = @_; # Default is to have empty parts. Graphviz sets this to true; $allow_empty = 1 unless defined $allow_empty; my @rc; my $uc = $self->{use_class}; my $qr_clean = $self->{_qr_part_clean}; # build base name: "A|B |C||D" => "ABCD" my $base_name = $name; $base_name =~ s/\s*\|\|?\s*//g; # use user-provided base name $base_name = $att->{basename} if exists $att->{basename}; # strip trailing/leading spaces on basename $base_name =~ s/\s+\z//; $base_name =~ s/^\s+//; # first one gets: "ABC", second one "ABC.1" and so on $base_name = $self->_get_cluster_name($base_name); print STDERR "# Parser: Autosplitting node with basename '$base_name'\n" if $graph->{debug}; my $first_in_row; # for relative placement of new row my $x = 0; my $y = 0; my $idx = 0; my $remaining = $name; my $sep; my $last_sep = ''; my $add = 0; while ($remaining ne '') { # XXX TODO: parsing of "\|" and "|" in one node $remaining =~ s/^((\\\||[^\|])*)(\|\|?|\z)//; my $part = $1 || ' '; $sep = $3; my $port_name = ''; # possible cleanup for this part if ($qr_clean) { $part =~ s/^$qr_clean//; $port_name = $1; } # fix [|G|] to have one empty part as last part if ($add == 0 && $remaining eq '' && $sep =~ /\|\|?/) { $add++; # only do it once $remaining .= '|' } print STDERR "# Parser: Found autosplit part '$part'\n" if $graph->{debug}; my $class = $uc->{node}; if ($allow_empty && $part eq ' ') { # create an empty node with no border $class .= "::Empty"; } elsif ($part =~ /^[ ]{2,}\z/) { # create an empty node with border $part = ' '; } else { $part =~ s/^\s+//; # rem spaces at front $part =~ s/\s+\z//; # rem spaces at end } my $node_name = "$base_name.$idx"; if ($graph->{debug}) { my $empty = ''; $empty = ' empty' if $class ne $self->{use_class}->{node}; print STDERR "# Parser: Creating$empty autosplit part '$part'\n" if $graph->{debug}; } # if it doesn't exist, add it, otherwise retrieve node object to $node if ($class =~ /::Empty/) { my $node = $graph->node($node_name); if (!defined $node) { # create node object from the correct class $node = $class->new($node_name); $graph->add_node($node); } } my $node = $graph->add_node($node_name); $node->{autosplit_label} = $part; # remember these two for Graphviz $node->{autosplit_portname} = $port_name; $node->{autosplit_basename} = $base_name; push @rc, $node; if (@rc == 1) { # for correct as_txt output $node->{autosplit} = $name; $node->{autosplit} =~ s/\s+\z//; # strip trailing spaces $node->{autosplit} =~ s/^\s+//; # strip leading spaces $node->{autosplit} =~ s/([^\|])\s+\|/$1 \|/g; # 'foo |' => 'foo |' $node->{autosplit} =~ s/\|\s+([^\|])/\| $1/g; # '| foo' => '| foo' $node->set_attribute('basename', $att->{basename}) if defined $att->{basename}; # list of all autosplit parts so as_txt() can find them easily again $node->{autosplit_parts} = [ ]; $first_in_row = $node; } else { # second, third etc. get previous as origin my ($sx,$sy) = (1,0); my $origin = $rc[-2]; if ($last_sep eq '||') { ($sx,$sy) = (0,1); $origin = $first_in_row; $first_in_row = $node; } $node->relative_to($origin,$sx,$sy); push @{$rc[0]->{autosplit_parts}}, $node; weaken @{$rc[0]->{autosplit_parts}}[-1]; # suppress as_txt output for other parts $node->{autosplit} = undef; } # nec. for border-collapse $node->{autosplit_xy} = "$x,$y"; $idx++; # next node ID $last_sep = $sep; $x++; # || starts a new row: if ($sep eq '||') { $x = 0; $y++; } } # end for all parts @rc; # return all created nodes } sub _new_node { # Create a new node unless it doesn't already exist. If the group stack # contains entries, the new node appears first in this/these group(s), so # add it to these groups. If the newly created node contains "|", we auto # split it up into several nodes and cluster these together. my ($self, $graph, $name, $group_stack, $att, $stack) = @_; print STDERR "# Parser: new node '$name'\n" if $graph->{debug}; $name = $self->_unquote($name, 'no_collapse'); my $autosplit; my $uc = $self->{use_class}; my @rc = (); if ($name =~ /^\s*\z/) { print STDERR "# Parser: Creating anon node\n" if $graph->{debug}; # create a new anon node and add it to the graph my $class = $uc->{node} . '::Anon'; my $node = $class->new(); @rc = ( $graph->add_node($node) ); } # nodes to be autosplit will be done in a sep. pass for Graphviz elsif ((ref($self) eq 'Graph::Easy::Parser') && $name =~ /[^\\]\|/) { $autosplit = 1; @rc = $self->_autosplit_node($graph, $name, $att); } else { # strip trailing and leading spaces $name =~ s/\s+\z//; $name =~ s/^\s+//; # collapse multiple spaces $name =~ s/\s+/ /g; # unquote \| $name =~ s/\\\|/\|/g; if ($self->{debug}) { if (!$graph->node($name)) { print STDERR "# Parser: Creating normal node from name '$name'.\n"; } else { print STDERR "# Parser: Found node '$name' already in graph.\n"; } } @rc = ( $self->_add_node($graph, $name) ); # add to graph, unless exists } $self->parse_error(5) if exists $att->{basename} && !$autosplit; my $b = $att->{basename}; delete $att->{basename}; # on a node list "[A],[B] { ... }" set attributes on all nodes # encountered so far, too: if (defined $stack) { for my $node (@$stack) { $node->set_attributes ($att, 0); } } my $index = 0; my $group = $self->{group_stack}->[-1]; for my $node (@rc) { $node->add_to_group($group) if $group; $node->set_attributes ($att, $index); $index++; } $att->{basename} = $b if defined $b; # return list of created nodes (usually one, but more for "A|B") @rc; } sub _match_comma { # return a regexp that matches something like " , " like in: # "[ Bonn ], [ Berlin ] => [ Hamburg ]" qr/\s*,\s*/; } sub _match_comment { # match the start of a comment qr/(^|[^\\])#/; } sub _match_commented_line { # match empty lines or a completely commented out line qr/^\s*(#|\z)/; } sub _match_attributes { # return a regexp that matches something like " { color: red; }" and returns # the inner text without the {} qr/\s*\{\s*([^\}]+?)\s*\}/; } sub _match_optional_attributes { # return a regexp that matches something like " { color: red; }" and returns # the inner text with the {} qr/(\s*\{[^\}]+?\})?/; } sub _match_node { # return a regexp that matches something like " [ bonn ]" and returns # the inner text without the [] (might leave some spaces) qr/\s*\[ # '[' start of the node ( (?: # non-capturing group \\. # either '\]' or '\N' etc. | # or [^\]\\] # not ']' and not '\' )* # 0 times for '[]' ) \]/x; # followed by ']' } sub _match_class_selector { my $class = qr/(?:\.\w+|graph|(?:edge|group|node)(?:\.\w+)?)/; qr/($class(?:\s*,\s*$class)*)/; } sub _match_single_attribute { qr/\s*([^:]+?)\s*:\s*("(?:\\"|[^"])+"|(?:\\;|[^;])+?)(?:\s*;\s*|\s*\z)/; # "name: value" } sub _match_group_start { # Return a regexp that matches something like " ( group [" and returns # the text between "(" and "[". Also matches empty groups like "( group )" # or even "()": qr/\s*\(\s*([^\[\)\(]*?)\s*([\[\)\(])/; } sub _match_group_end { # return a regexp that matches something like " )". qr/\s*\)\s*/; } sub _match_edge { # Matches all possible edge variants like: # -->, ---->, ==> etc # <-->, <---->, <==>, <..> etc # <-- label -->, <.- label .-> etc # -- label -->, .- label .-> etc # "- " must come before "-"! # likewise, "..-" must come before ".-" must come before "." # XXX TODO: convert the first group into a non-matching group qr/\s* ( # egde without label ("-->") (<?) # optional left "<" (=\s|=|-\s|-|\.\.-|\.-|\.|~)+> # pattern (style) of edge | # edge with label ("-- label -->") (<?) # optional left "<" ((=\s|=|-\s|-|\.\.-|\.-|\.|~)+) # pattern (style) of edge \s+ # followed by at least a space ((?:\\.|[^>\[\{])*?) # either \\, \[ etc, or not ">", "[", "{" (\s+\5)> # a space and pattern before ">" # inserting this needs mucking with all the code that access $5 etc # | # undirected edge (without arrows, but with label) # ((=\s|=|-\s|-|\.\.-|\.-|\.|~)+) # pattern (style) of edge # \s+ # followed by at least a space # ((?:\\.|[^>\[\{])*?) # either \\, \[ etc, or not ">", "[", "{" # (\s+\10) # a space and pattern | # undirected edge (without arrows and label) (\.\.-|\.-)+ # pattern (style) of edge (at least once) | (=\s|=|-\s|-|\.|~){2,} # these at least two times ) /x; } sub _clean_attributes { my ($self,$text) = @_; $text =~ s/^\s*\{\s*//; # remove left-over "{" and spaces $text =~ s/\s*\}\s*\z//; # remove left-over "}" and spaces $text; } sub _parse_attributes { # Takes a text like "attribute: value; attribute2 : value2;" and # returns a hash with the attributes. $class defaults to 'node'. # In list context, also returns a flag that is maxlevel-1 when one # of the attributes was a multiple one (aka 2 for "red|green", 1 for "red"); my ($self, $text, $object, $no_multiples) = @_; my $class = $object; $class = $object->{class} if ref($object); $class = 'node' unless defined $class; $class =~ s/\..*//; # remove subclass my $out; my $att = {}; my $multiples = 0; $text = $self->_clean_attributes($text); my $qr_att = $self->{_match_single_attribute}; my $qr_cmt; $qr_cmt = $self->_match_multi_line_comment() if $self->can('_match_multi_line_comment'); my $qr_satt; $qr_satt = $self->_match_special_attribute() if $self->can('_match_special_attribute'); return {} if $text =~ /^\s*\z/; print STDERR "attr parsing: matching\n '$text'\n against $qr_att\n" if $self->{debug} > 3; while ($text ne '') { print STDERR "attr parsing: matching '$text'\n" if $self->{debug} > 3; # remove a possible comment $text =~ s/^$qr_cmt//g if $qr_cmt; # if the last part was a comment, we end up with an empty text here: last if $text =~ /^\s*\z/; # match and remove "name: value" my $done = ($text =~ s/^$qr_att//) || 0; # match and remove "name" if "name: value;" didn't match $done++ if $done == 0 && $qr_satt && ($text =~ s/^$qr_satt//); return $self->error ("Error in attribute: '$text' doesn't look valid to me.") if $done == 0; my $name = $1; my $v = $2; $v = '' unless defined $v; # for special attributes w/o value # unquote and store $out->{$name} = $self->_unquote_attribute($name,$v); } if ($self->{debug} && $self->{debug} > 1) { require Data::Dumper; print STDERR "# ", join (" ", caller),"\n"; print STDERR "# Parsed attributes into:\n", Data::Dumper::Dumper($out),"\n"; } # possible remap attributes (for parsing Graphviz) $out = $self->_remap_attributes($out, $object) if $self->can('_remap_attributes'); my $g = $self->{_graph}; # check for being valid and finally create hash with name => value pairs for my $name (sort keys %$out) { my ($rc, $newname, $v) = $g->validate_attribute($name,$out->{$name},$class,$no_multiples); $self->error($g->{error}) if defined $rc; $multiples = scalar @$v if ref($v) eq 'ARRAY'; $att->{$newname} = $v if defined $v; # undef => ignore attribute } return $att unless wantarray; ($att, $multiples || 1); } sub parse_error { # take a msg number, plus params, and throws an exception my $self = shift; my $msg_nr = shift; # XXX TODO: should really use the msg nr mapping my $msg = "Found unexpected group end"; # 0 $msg = "Error in attribute: '##param2##' is not a valid attribute for a ##param3##" # 1 if $msg_nr == 1; $msg = "Error in attribute: '##param1##' is not a valid ##param2## for a ##param3##" if $msg_nr == 2; # 2 $msg = "Error: Found attributes, but expected group or node start" if $msg_nr == 3; # 3 $msg = "Error in attribute: multi-attribute '##param1##' not allowed here" if $msg_nr == 4; # 4 $msg = "Error in attribute: basename not allowed for non-autosplit nodes" if $msg_nr == 5; # 5 # for graphviz parsing $msg = "Error: Already seen graph start" if $msg_nr == 6; # 6 $msg = "Error: Expected '}', but found file end" if $msg_nr == 7; # 7 my $i = 1; foreach my $p (@_) { $msg =~ s/##param$i##/$p/g; $i++; } $self->error($msg . ' at line ' . $self->{line_nr}); } sub _parser_cleanup { # After initial parsing, do a cleanup pass. my ($self) = @_; my $g = $self->{_graph}; for my $n (ord_values ( $g->{nodes} )) { next if $n->{autosplit}; $self->warn("Node '" . $self->_quote($n->{name}) . "' has an offset but no origin") if (($n->attribute('offset') ne '0,0') && $n->attribute('origin') eq ''); } $self; } sub _quote { # make a node name safe for error message output my ($self,$n) = @_; $n =~ s/'/\\'/g; $n; } 1; __END__ =head1 NAME Graph::Easy::Parser - Parse Graph::Easy from textual description =head1 SYNOPSIS # creating a graph from a textual description use Graph::Easy::Parser; my $parser = Graph::Easy::Parser->new(); my $graph = $parser->from_text( '[ Bonn ] => [ Berlin ]'. '[ Berlin ] => [ Rostock ]'. ); print $graph->as_ascii(); print $parser->from_file('mygraph.txt')->as_ascii(); # Also works automatically on graphviz code: print Graph::Easy::Parser->from_file('mygraph.dot')->as_ascii(); =head1 DESCRIPTION C<Graph::Easy::Parser> lets you parse simple textual descriptions of graphs, and constructs a C<Graph::Easy> object from them. The resulting object can than be used to layout and output the graph. =head2 Input The input consists of text describing the graph, encoded in UTF-8. Example: [ Bonn ] --> [ Berlin ] [ Frankfurt ] <=> [ Dresden ] [ Bonn ] --> [ Frankfurt ] [ Bonn ] = > [ Frankfurt ] =head3 Graphviz In addition there is a bit of magic that detects graphviz code, so input of the following form will also work: digraph Graph1 { "Bonn" -> "Berlin" } Note that the magic detection only works for B<named> graphs or graph with "digraph" at their start, so the following will not be detected as graphviz code because it looks exactly like valid Graph::Easy code at the start: graph { "Bonn" -> "Berlin" } See L<Graph::Easy::Parser::Graphviz> for more information about parsing graphs in the DOT language. =head3 VCG In addition there is a bit of magic that detects VCG code, so input of the following form will also work: graph: { node: { title: Bonn; } node: { title: Berlin; } edge: { sourcename: Bonn; targetname: Berlin; } } See L<Graph::Easy::Parser::VCG> for more information about parsing graphs in the VCG language. =head2 Input Syntax This is a B<very> brief description of the syntax for the Graph::Easy language, for a full specification, please see L<Graph::Easy::Manual>. =over 2 =item nodes Nodes are rendered (or "quoted", if you wish) with enclosing square brackets: [ Single node ] [ Node A ] --> [ Node B ] Anonymous nodes do not have a name and cannot be refered to again: [ ] -> [ Bonn ] -> [ ] This creates three nodes, two of them anonymous. =item edges The edges between the nodes can have the following styles: -> solid => double .> dotted ~> wave - > dashed .-> dot-dash ..-> dot-dot-dash = > double-dash There are also the styles C<bold>, C<wide> and C<broad>. Unlike the others, these can only be set via the (optional) edge attributes: [ AB ] --> { style: bold; } [ ABC ] You can repeat each of the style-patterns as much as you like: ---> ==> => ~~~~~> ..-..-..-> Note that in patterns longer than one character, the entire pattern must be repeated e.g. all characters of the pattern must be present. Thus: ..-..-..-> # valid dot-dot-dash ..-..-..> # invalid! .-.-.-> # valid dot-dash .-.-> # invalid! In additon to the styles, the following two directions are possible: -- edge without arrow heads --> arrow at target node (end point) <--> arrow on both the source and target node (end and start point) Of course you can combine all directions with all styles. However, note that edges without arrows cannot use the shortcuts for styles: --- # valid .-.- # valid .- # invalid! - # invalid! ~ # invalid! Just remember to use at least two repititions of the full pattern for arrow-less edges. You can also give edges a label, either by inlining it into the style, or by setting it via the attributes: [ AB ] --> { style: bold; label: foo; } [ ABC ] -- foo --> ... baz ...> -- solid --> == double ==> .. dotted ..> ~~ wave ~~> - dashed - > = double-dash = > .- dot-dash .-> ..- dot-dot-dash ..-> Note that the two patterns on the left and right of the label must be the same, and that there is a space between the left pattern and the label, as well as the label and the right pattern. You may use inline label only with edges that have an arrow. Thus: <-- label --> # valid -- label --> # valid -- label -- # invalid! To use a label with an edge without arrow heads, use the attributes: [ AB ] -- { label: edgelabel; } [ CD ] =item groups Round brackets are used to group nodes together: ( Cities: [ Bonn ] -> [ Berlin ] ) Anonymous groups do not have a name and cannot be refered to again: ( [ Bonn ] ) -> [ Berlin ] This creates an anonymous group with the node C<Bonn> in it, and links it to the node C<Berlin>. =back Please see L<Graph::Easy::Manual> for a full description of the syntax rules. =head2 Output The output will be a L<Graph::Easy|Graph::Easy> object (unless overrriden with C<use_class()>), see the documentation for Graph::Easy what you can do with it. =head1 EXAMPLES See L<Graph::Easy> for an extensive list of examples. =head1 METHODS C<Graph::Easy::Parser> supports the following methods: =head2 new() use Graph::Easy::Parser; my $parser = Graph::Easy::Parser->new(); Creates a new parser object. The valid parameters are: debug fatal_errors The first will enable debug output to STDERR: my $parser = Graph::Easy::Parser->new( debug => 1 ); $parser->from_text('[A] -> [ B ]'); Setting C<fatal_errors> to 0 will make parsing errors not die, but just set an error string, which can be retrieved with L<error()>. my $parser = Graph::Easy::Parser->new( fatal_errors => 0 ); $parser->from_text(' foo ' ); print $parser->error(); See also L<catch_messages()> for how to catch errors and warnings. =head2 reset() $parser->reset(); Reset the status of the parser, clear errors etc. Automatically called when you call any of the C<from_XXX()> methods below. =head2 use_class() $parser->use_class('node', 'Graph::Easy::MyNode'); Override the class to be used to constructs objects while parsing. The first parameter can be one of the following: node edge graph group The second parameter should be a class that is a subclass of the appropriate base class: package Graph::Easy::MyNode; use base qw/Graph::Easy::Node/; # override here methods for your node class ###################################################### # when overriding nodes, we also need ::Anon package Graph::Easy::MyNode::Anon; use base qw/Graph::Easy::MyNode/; use base qw/Graph::Easy::Node::Anon/; ###################################################### # and :::Empty package Graph::Easy::MyNode::Empty; use base qw/Graph::Easy::MyNode/; ###################################################### package main; use Graph::Easy::Parser; use Graph::Easy; use Graph::Easy::MyNode; use Graph::Easy::MyNode::Anon; use Graph::Easy::MyNode::Empty; my $parser = Graph::Easy::Parser; $parser->use_class('node', 'Graph::Easy::MyNode'); my $graph = $parser->from_text(...); The object C<$graph> will now contain nodes that are of your custom class instead of plain C<Graph::Easy::Node>. When overriding nodes, you also should provide subclasses for C<Graph::Easy::Node::Anon> and C<Graph::Easy::Node::Empty>, and make these subclasses of your custom node class as shown above. For edges, groups and graphs, you need just one subclass. =head2 from_text() my $graph = $parser->from_text( $text ); Create a L<Graph::Easy|Graph::Easy> object from the textual description in C<$text>. Returns undef for error, you can find out what the error was with L<error()>. This method will reset any previous error, and thus the C<$parser> object can be re-used to parse different texts by just calling C<from_text()> multiple times. =head2 from_file() my $graph = $parser->from_file( $filename ); my $graph = Graph::Easy::Parser->from_file( $filename ); Creates a L<Graph::Easy|Graph::Easy> object from the textual description in the file C<$filename>. The second calling style will create a temporary C<Graph::Easy::Parser> object, parse the file and return the resulting C<Graph::Easy> object. Returns undef for error, you can find out what the error was with L<error()> when using the first calling style. =head2 error() my $error = $parser->error(); Returns the last error, or the empty string if no error occured. If you want to catch warnings from the parser, enable catching of warnings or errors: $parser->catch_messages(1); # Or individually: # $parser->catch_warnings(1); # $parser->catch_errors(1); # something which warns or throws an error: ... if ($parser->error()) { my @errors = $parser->errors(); } if ($parser->warning()) { my @warnings = $parser->warnings(); } See L<Graph::Easy::Base> for more details on error/warning message capture. =head2 parse_error() $parser->parse_error( $msg_nr, @params); Sets an error message from a message number and replaces embedded templates like C<##param1##> with the passed parameters. =head2 _parse_attributes() my $attributes = $parser->_parse_attributes( $txt, $class ); my ($att, $multiples) = $parser->_parse_attributes( $txt, $class ); B<Internal usage only>. Takes a text like this: attribute: value; attribute2 : value2; and returns a hash with the attributes. In list context, also returns the max count of multiple attributes, e.g. 3 when it encounters something like C<< red|green|blue >>. When =head1 EXPORT Exports nothing. =head1 SEE ALSO L<Graph::Easy>. L<Graph::Easy::Parser::Graphviz> and L<Graph::Easy::Parser::VCG>. =head1 AUTHOR Copyright (C) 2004 - 2007 by Tels L<http://bloodgate.com> See the LICENSE file for information. =cut �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/lib/Graph/Easy/Edge/����������������������������������������������������������������0000755�0000764�0000764�00000000000�12150110221�016634� 5����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/lib/Graph/Easy/Edge/Cell.pm���������������������������������������������������������0000644�0000764�0000764�00000127775�12147675241�020124� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������############################################################################# # Part of Graph::Easy. # ############################################################################# package Graph::Easy::Edge::Cell; use strict; use Graph::Easy::Edge; use Graph::Easy::Attributes; require Exporter; use vars qw/$VERSION @EXPORT_OK @ISA/; @ISA = qw/Exporter Graph::Easy::Edge/; $VERSION = '0.29'; use Scalar::Util qw/weaken/; ############################################################################# # The different cell types: use constant { EDGE_CROSS => 0, # + crossing lines EDGE_HOR => 1, # -- horizontal line EDGE_VER => 2, # | vertical line EDGE_N_E => 3, # |_ corner (N to E) EDGE_N_W => 4, # _| corner (N to W) EDGE_S_E => 5, # ,- corner (S to E) EDGE_S_W => 6, # -, corner (S to W) # Joints: EDGE_S_E_W => 7, # -,- three-sided corner (S to W/E) EDGE_N_E_W => 8, # -'- three-sided corner (N to W/E) EDGE_E_N_S => 9, # |- three-sided corner (E to S/N) EDGE_W_N_S => 10, # -| three-sided corner (W to S/N) EDGE_HOLE => 11, # a hole (placeholder for the "other" # edge in a crossing section # Holes are inserted in the layout stage # and removed in the optimize stage, before # rendering occurs. # these loop types must come last EDGE_N_W_S => 12, # v--+ loop, northwards EDGE_S_W_N => 13, # ^--+ loop, southwards EDGE_E_S_W => 14, # [_ loop, westwards EDGE_W_S_E => 15, # _] loop, eastwards EDGE_MAX_TYPE => 15, # last valid type EDGE_LOOP_TYPE => 12, # first LOOP type # Flags: EDGE_START_E => 0x0100, # start from East (sorted ESWN) EDGE_START_S => 0x0200, # start from South EDGE_START_W => 0x0400, # start from West EDGE_START_N => 0x0800, # start from North EDGE_END_W => 0x0010, # end points to West (sorted WNES) EDGE_END_N => 0x0020, # end points to North EDGE_END_E => 0x0040, # end points to East EDGE_END_S => 0x0080, # end points to South EDGE_LABEL_CELL => 0x1000, # this cell carries the label EDGE_SHORT_CELL => 0x2000, # a short edge pice (for filling) EDGE_ARROW_MASK => 0x0FF0, # mask out the end/start type EDGE_START_MASK => 0x0F00, # mask out the start type EDGE_END_MASK => 0x00F0, # mask out the end type EDGE_TYPE_MASK => 0x000F, # mask out the basic cell type EDGE_FLAG_MASK => 0xFFF0, # mask out the flags EDGE_MISC_MASK => 0xF000, # mask out the misc. flags EDGE_NO_M_MASK => 0x0FFF, # anything except the misc. flags ARROW_RIGHT => 0, ARROW_LEFT => 1, ARROW_UP => 2, ARROW_DOWN => 3, }; use constant { EDGE_ARROW_HOR => EDGE_END_E() + EDGE_END_W(), EDGE_ARROW_VER => EDGE_END_N() + EDGE_END_S(), # shortcuts to not need to write EDGE_HOR + EDGE_START_W + EDGE_END_E EDGE_SHORT_E => EDGE_HOR + EDGE_END_E + EDGE_START_W, # |-> start/end at this cell EDGE_SHORT_S => EDGE_VER + EDGE_END_S + EDGE_START_N, # v start/end at this cell EDGE_SHORT_W => EDGE_HOR + EDGE_END_W + EDGE_START_E, # <-| start/end at this cell EDGE_SHORT_N => EDGE_VER + EDGE_END_N + EDGE_START_S, # ^ start/end at this cell EDGE_SHORT_BD_EW => EDGE_HOR + EDGE_END_E + EDGE_END_W, # <-> start/end at this cell EDGE_SHORT_BD_NS => EDGE_VER + EDGE_END_S + EDGE_END_N, # ^ # | start/end at this cell # v EDGE_SHORT_UN_EW => EDGE_HOR + EDGE_START_E + EDGE_START_W, # -- EDGE_SHORT_UN_NS => EDGE_VER + EDGE_START_S + EDGE_START_N, # | EDGE_LOOP_NORTH => EDGE_N_W_S + EDGE_END_S + EDGE_START_N + EDGE_LABEL_CELL, EDGE_LOOP_SOUTH => EDGE_S_W_N + EDGE_END_N + EDGE_START_S + EDGE_LABEL_CELL, EDGE_LOOP_WEST => EDGE_W_S_E + EDGE_END_E + EDGE_START_W + EDGE_LABEL_CELL, EDGE_LOOP_EAST => EDGE_E_S_W + EDGE_END_W + EDGE_START_E + EDGE_LABEL_CELL, }; ############################################################################# @EXPORT_OK = qw/ EDGE_START_E EDGE_START_W EDGE_START_N EDGE_START_S EDGE_END_E EDGE_END_W EDGE_END_N EDGE_END_S EDGE_SHORT_E EDGE_SHORT_W EDGE_SHORT_N EDGE_SHORT_S EDGE_SHORT_BD_EW EDGE_SHORT_BD_NS EDGE_SHORT_UN_EW EDGE_SHORT_UN_NS EDGE_HOR EDGE_VER EDGE_CROSS EDGE_HOLE EDGE_N_E EDGE_N_W EDGE_S_E EDGE_S_W EDGE_S_E_W EDGE_N_E_W EDGE_E_N_S EDGE_W_N_S EDGE_LOOP_NORTH EDGE_LOOP_SOUTH EDGE_LOOP_EAST EDGE_LOOP_WEST EDGE_N_W_S EDGE_S_W_N EDGE_E_S_W EDGE_W_S_E EDGE_TYPE_MASK EDGE_FLAG_MASK EDGE_ARROW_MASK EDGE_START_MASK EDGE_END_MASK EDGE_MISC_MASK EDGE_LABEL_CELL EDGE_SHORT_CELL EDGE_NO_M_MASK ARROW_RIGHT ARROW_LEFT ARROW_UP ARROW_DOWN /; my $edge_types = { EDGE_HOR() => 'horizontal', EDGE_VER() => 'vertical', EDGE_CROSS() => 'crossing', EDGE_N_E() => 'north/east corner', EDGE_N_W() => 'north/west corner', EDGE_S_E() => 'south/east corner', EDGE_S_W() => 'south/west corner', EDGE_S_E_W() => 'joint south to east/west', EDGE_N_E_W() => 'joint north to east/west', EDGE_E_N_S() => 'joint east to north/south', EDGE_W_N_S() => 'joint west to north/south', EDGE_N_W_S() => 'selfloop, northwards', EDGE_S_W_N() => 'selfloop, southwards', EDGE_E_S_W() => 'selfloop, eastwards', EDGE_W_S_E() => 'selfloop, westwards', }; my $flag_types = { EDGE_LABEL_CELL() => 'labeled', EDGE_SHORT_CELL() => 'short', EDGE_START_E() => 'starting east', EDGE_START_W() => 'starting west', EDGE_START_N() => 'starting north', EDGE_START_S() => 'starting south', EDGE_END_E() => 'ending east', EDGE_END_W() => 'ending west', EDGE_END_N() => 'ending north', EDGE_END_S() => 'ending south', }; use constant isa_cell => 1; sub edge_type { # convert edge type number to some descriptive text my $type = shift; my $flags = $type & EDGE_FLAG_MASK; $type &= EDGE_TYPE_MASK; my $t = $edge_types->{$type} || ('unknown edge type #' . $type); $flags &= EDGE_FLAG_MASK; my $mask = 0x0010; while ($mask < 0xFFFF) { my $tf = $flags & $mask; $mask <<= 1; $t .= ", $flag_types->{$tf}" if $tf != 0; } $t; } ############################################################################# sub _init { # generic init, override in subclasses my ($self,$args) = @_; $self->{type} = EDGE_SHORT_E(); # --> $self->{style} = 'solid'; $self->{x} = 0; $self->{y} = 0; $self->{w} = undef; $self->{h} = 3; foreach my $k (sort keys %$args) { # don't store "after" and "before" next unless $k =~ /^(graph|edge|x|y|type)\z/; $self->{$k} = $args->{$k}; } $self->_croak("Creating edge cell without a parent edge object") unless defined $self->{edge}; $self->_croak("Creating edge cell without a type") unless defined $self->{type}; # take over settings from edge $self->{style} = $self->{edge}->style(); $self->{class} = $self->{edge}->class(); $self->{graph} = $self->{edge}->{graph}; $self->{group} = $self->{edge}->{group}; weaken($self->{graph}); weaken($self->{group}); $self->{att} = $self->{edge}->{att}; # register ourselves at this edge $self->{edge}->_add_cell ($self, $args->{after}, $args->{before}); $self; } sub arrow_count { # return 0, 1 or 2, depending on the number of end points my $self = shift; return 0 if $self->{edge}->{undirected}; my $count = 0; my $type = $self->{type}; $count ++ if ($type & EDGE_END_N) != 0; $count ++ if ($type & EDGE_END_S) != 0; $count ++ if ($type & EDGE_END_W) != 0; $count ++ if ($type & EDGE_END_E) != 0; if ($self->{edge}->{bidirectional}) { $count ++ if ($type & EDGE_START_N) != 0; $count ++ if ($type & EDGE_START_S) != 0; $count ++ if ($type & EDGE_START_W) != 0; $count ++ if ($type & EDGE_START_E) != 0; } $count; } sub _make_cross { # Upgrade us to a cross-section. my ($self, $edge, $flags) = @_; my $type = $self->{type} & EDGE_TYPE_MASK; $self->_croak("Trying to cross non hor/ver piece at $self->{x},$self->{y}") if (($type != EDGE_HOR) && ($type != EDGE_VER)); $self->{color} = $self->get_color_attribute('color'); $self->{style_ver} = $edge->style(); $self->{color_ver} = $edge->get_color_attribute('color'); # if we are the VER piece, switch styles around if ($type == EDGE_VER) { ($self->{style_ver}, $self->{style}) = ($self->{style},$self->{style_ver}); ($self->{color_ver}, $self->{color}) = ($self->{color},$self->{color}); } $self->{type} = EDGE_CROSS + ($flags || 0); $self; } sub _make_joint { # Upgrade us to a joint my ($self, $edge, $new_type) = @_; my $type = $self->{type} & EDGE_TYPE_MASK; $self->_croak("Trying to join non hor/ver piece (type: $type) at $self->{x},$self->{y}") if $type >= EDGE_S_E_W; $self->{color} = $self->get_color_attribute('color'); $self->{style_ver} = $edge->style(); $self->{color_ver} = $edge->get_color_attribute('color'); # if we are the VER piece, switch styles around if ($type == EDGE_VER) { ($self->{style_ver}, $self->{style}) = ($self->{style},$self->{style_ver}); ($self->{color_ver}, $self->{color}) = ($self->{color},$self->{color}); } print STDERR "# creating joint at $self->{x}, $self->{y} with new type $new_type (old $type)\n" if $self->{graph}->{debug}; $self->{type} = $new_type; $self; } ############################################################################# # conversion to HTML my $edge_end_north = ' <td colspan=2 class="##class## eb" style="##bg####ec##"> </td>' . "\n" . ' <td colspan=2 class="##class## eb" style="##bg####ec##"><span class="su">^</span></td>' . "\n"; my $edge_end_south = ' <td colspan=2 class="##class## eb" style="##bg####ec##"> </td>' . "\n" . ' <td colspan=2 class="##class## eb" style="##bg####ec##"><span class="sv">v</span></td>' . "\n"; my $edge_empty_row = ' <td colspan=4 class="##class## eb"></td>'; my $edge_arrow_west_upper = '<td rowspan=2 class="##class## eb" style="##ec####bg##"><span class="shl"><</span></td>' . "\n"; my $edge_arrow_west_lower = '<td rowspan=2 class="##class## eb"> </td>' . "\n"; my $edge_arrow_east_upper = '<td rowspan=2 class="##class## eb" style="##ec####bg##"><span class="sh">></span></td>' . "\n"; my $edge_arrow_east_lower = '<td rowspan=2 class="##class## eb"></td>' . "\n"; my $edge_html = { # The " " in empty table cells with borders are here to make IE display # the border. I so hate browser bugs :-( EDGE_S_E() => [ ' <td colspan=2 rowspan=2 class="##class## eb"></td>' . "\n" . ' <td colspan=2 rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>', '', ' <td colspan=2 rowspan=2 class="##class## eb"></td>'. "\n" . ' <td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', '', ], EDGE_S_E() + EDGE_START_E() + EDGE_END_S() => [ ' <td colspan=2 rowspan=2 class="##class## eb"></td>' . "\n" . ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . ' <td rowspan=4 class="##class## el"></td>', '', ' <td colspan=2 class="##class## eb"></td>'. "\n" . ' <td class="##class## eb" style="border-left: ##border##;"> </td>', $edge_end_south, ], EDGE_S_E() + EDGE_START_E() => [ ' <td colspan=2 rowspan=2 class="##class## eb"></td>' . "\n" . ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . ' <td rowspan=4 class="##class## el"></td>', '', ' <td colspan=2 rowspan=2 class="##class## eb"></td>'. "\n" . ' <td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', '', ], EDGE_S_E() + EDGE_END_E() => [ ' <td colspan=2 rowspan=2 class="##class## eb"></td>' . "\n" . ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . ' <td rowspan=4 class="##class##"##edgecolor##><span class="sa">></span></td>', '', ' <td colspan=2 rowspan=2 class="##class## eb"></td>'. "\n" . ' <td rowspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', '', ], EDGE_S_E() + EDGE_START_S() => [ ' <td colspan=2 rowspan=2 class="##class## eb"></td>' . "\n" . ' <td colspan=2 rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>', '', ' <td colspan=2 class="##class## eb"></td>'. "\n" . ' <td colspan=2 class="##class## eb" style="border-left: ##border##;"> </td>' . "\n", $edge_empty_row, ], EDGE_S_E() + EDGE_START_S() + EDGE_END_E() => [ ' <td colspan=2 rowspan=2 class="##class## eb"></td>' . "\n" . ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>'. ' <td rowspan=4 class="##class##"##edgecolor##><span class="sa">></span></td>', '', ' <td colspan=2 rowspan=2 class="##class## eb"></td>'. "\n" . ' <td class="##class## eb" style="border-left: ##border##;"> </td>' . "\n", ' <td class="##class## eb"></td>', ], EDGE_S_E() + EDGE_END_S() => [ ' <td colspan=2 rowspan=2 class="##class## eb"></td>' . "\n" . ' <td colspan=2 rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>', '', ' <td colspan=2 class="##class## eb"></td>'. "\n" . ' <td colspan=2 class="##class## eb" style="border-left: ##border##;"> </td>' . "\n", $edge_end_south, ], EDGE_S_E() + EDGE_END_S() + EDGE_END_E() => [ ' <td colspan=2 rowspan=2 class="##class## eb"></td>' . "\n" . ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . ' <td rowspan=4 class="##class## ha"##edgecolor##><span class="sa">></span></td>', '', ' <td colspan=2 class="##class## eb"></td>'. "\n" . ' <td class="##class## eb" style="border-left: ##border##;"> </td>' . "\n", ' <td colspan=3 class="##class## v"##edgecolor##>v</td>', ], ########################################################################### ########################################################################### # S_W EDGE_S_W() => [ ' <td colspan=2 rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . ' <td colspan=2 rowspan=2 class="##class## eb"></td>', '', ' <td colspan=2 rowspan=2 class="##class## eb"></td>'. "\n" . ' <td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', '', ], EDGE_S_W() + EDGE_START_W() => [ ' <td rowspan=2 class="##class## el"></td>' . "\n" . ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . ' <td colspan=2 rowspan=2 class="##class## eb"></td>', '', ' <td colspan=2 rowspan=2 class="##class## eb"></td>'. "\n" . ' <td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', '', ], EDGE_S_W() + EDGE_END_W() => [ ' <td rowspan=2 class="##class## va"##edgecolor##><span class="shl"><</span></td>' . "\n" . ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . ' <td colspan=2 rowspan=2 class="##class## eb"></td>', '', ' <td colspan=2 rowspan=2 class="##class## eb"></td>'. "\n" . ' <td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', '', ], EDGE_S_W() + EDGE_START_S() => [ ' <td colspan=2 rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . ' <td colspan=2 rowspan=2 class="##class## eb"></td>', '', ' <td colspan=2 class="##class## eb"></td>'. "\n" . ' <td colspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', $edge_empty_row, ], EDGE_S_W() + EDGE_END_S() => [ ' <td colspan=2 rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . ' <td colspan=2 rowspan=2 class="##class## eb"></td>', '', ' <td colspan=2 class="##class## eb"></td>'. "\n" . ' <td colspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', $edge_end_south, ], EDGE_S_W() + EDGE_START_W() + EDGE_END_S() => [ ' <td rowspan=2 class="##class## el"></td>' . "\n" . ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . ' <td colspan=2 rowspan=2 class="##class## eb"></td>', '', ' <td colspan=2 class="##class## eb"></td>'. "\n" . ' <td colspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', $edge_end_south, ], EDGE_S_W() + EDGE_START_S() + EDGE_END_W() => [ ' <td rowspan=3 class="##class## sh"##edgecolor##><</td>' . "\n" . ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . ' <td colspan=2 rowspan=2 class="##class## eb"></td>', '', ' <td class="##class## eb"></td>'. "\n" . ' <td colspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', $edge_empty_row, ], ########################################################################### ########################################################################### # N_W EDGE_N_W() => [ ' <td colspan=2 rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . ' <td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', '', ' <td colspan=4 rowspan=2 class="##class## eb"></td>', '', ], EDGE_N_W() + EDGE_START_N() => [ $edge_empty_row, ' <td colspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . ' <td colspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', '', ' <td colspan=4 rowspan=2 class="##class## eb"></td>', ], EDGE_N_W() + EDGE_END_N() => [ $edge_end_north, ' <td colspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . ' <td colspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', ' <td colspan=4 rowspan=2 class="##class## eb"></td>', '', ], EDGE_N_W() + EDGE_END_N() + EDGE_START_W() => [ $edge_end_north, ' <td rowspan=3 class="##class## eb"></td>'. ' <td class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . ' <td colspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', ' <td colspan=4 rowspan=2 class="##class## eb"></td>', '', ], EDGE_N_W() + EDGE_START_W() => [ ' <td rowspan=2 class="##class## el"></td>' . "\n" . ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . ' <td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##border##;"> </td>' . "\n", '', ' <td colspan=4 rowspan=2 class="##class## eb"></td>', '', ], EDGE_N_W() + EDGE_END_W() => [ ' <td rowspan=4 class="##class## sh"##edgecolor##><</td>' . "\n" . ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . ' <td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##border##;"> </td>' . "\n", '', ' <td colspan=3 rowspan=2 class="##class## eb"></td>', '', ], ########################################################################### ########################################################################### # N_E EDGE_N_E() => [ ' <td colspan=2 rowspan=2 class="##class## eb"></td>' . "\n" . ' <td colspan=2 rowspan=2 class="##class## eb" style="border-bottom: ##border##; border-left: ##border##;"> </td>', '', ' <td colspan=4 rowspan=2 class="##class## eb"></td>', '', ], EDGE_N_E() + EDGE_START_E() => [ ' <td colspan=2 rowspan=2 class="##class## eb"></td>' . "\n" . ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##; border-left: ##border##;"> </td>' . "\n" . ' <td rowspan=4 class="##class## el"></td>', '', ' <td colspan=3 rowspan=2 class="##class## eb"></td>', '', ], EDGE_N_E() + EDGE_END_E() => [ ' <td colspan=2 rowspan=2 class="##class## eb"></td>' . "\n" . ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##; border-left: ##border##;"> </td>' . "\n" . ' <td rowspan=4 class="##class## va"##edgecolor##><span class="sa">></span></td>', '', ' <td colspan=3 rowspan=2 class="##class## eb"></td>', '', ], EDGE_N_E() + EDGE_END_E() + EDGE_START_N() => [ $edge_empty_row, ' <td colspan=2 class="##class## eb"></td>' . "\n" . ' <td class="##class## eb" style="border-bottom: ##border##; border-left: ##border##;"> </td>' . "\n" . ' <td rowspan=3 class="##class## va"##edgecolor##><span class="sa">></span></td>', ' <td colspan=3 rowspan=2 class="##class## eb"></td>', '', ], EDGE_N_E() + EDGE_START_E() + EDGE_END_N() => [ $edge_end_north, ' <td colspan=2 class="##class## eb"></td>' . "\n" . ' <td class="##class## eb" style="border-bottom: ##border##; border-left: ##border##;"> </td>' . "\n" . ' <td rowspan=3 class="##class## eb"> </td>', ' <td colspan=3 rowspan=2 class="##class## eb"></td>', '', ], EDGE_N_E() + EDGE_START_N() => [ $edge_empty_row, ' <td colspan=2 rowspan=3 class="##class## eb"></td>' . "\n" . ' <td colspan=2 class="##class## eb" style="border-bottom: ##border##; border-left: ##border##;"> </td>', ' <td colspan=2 class="##class## eb"></td>', '', ], EDGE_N_E() + EDGE_END_N() => [ $edge_end_north, ' <td colspan=2 rowspan=3 class="##class## eb"></td>' . "\n" . ' <td colspan=2 class="##class## eb" style="border-bottom: ##border##; border-left: ##border##;"> </td>', '', ' <td colspan=2 class="##class## eb"></td>', ], ########################################################################### ########################################################################### # self loops EDGE_LOOP_NORTH() - EDGE_LABEL_CELL() => [ '<td rowspan=2 class="##class## eb"> </td>' . "\n". ' <td colspan=2 rowspan=2 class="##class## lh" style="border-bottom: ##border##;##lc####bg##">##label##</td>' . "\n" . ' <td rowspan=2 class="##class## eb"> </td>', '', '<td class="##class## eb"> </td>' . "\n". ' <td colspan=2 class="##class## eb" style="border-left: ##border##;##bg##"> </td>'."\n". ' <td class="##class## eb" style="border-left: ##border##;##bg##"> </td>', '<td colspan=2 class="##class## v" style="##bg##"##edgecolor##>v</td>' . "\n" . ' <td colspan=2 class="##class## eb"> </td>', ], EDGE_LOOP_SOUTH() - EDGE_LABEL_CELL() => [ '<td colspan=2 class="##class## v" style="##bg##"##edgecolor##>^</td>' . "\n" . ' <td colspan=2 class="##class## eb"> </td>', '<td rowspan=2 class="##class## eb"> </td>' . "\n". ' <td colspan=2 rowspan=2 class="##class## lh" style="border-left:##border##;border-bottom:##border##;##lc####bg##">##label##</td>'."\n". ' <td rowspan=2 class="##class## eb" style="border-left:##border##;##bg##"> </td>', '', '<td colspan=4 class="##class## eb"> </td>', ], EDGE_LOOP_WEST() - EDGE_LABEL_CELL() => [ $edge_empty_row. ' <td colspan=2 rowspan=2 class="##class## lh" style="border-bottom: ##border##;##lc####bg##">##label##</td>'."\n". ' <td rowspan=2 class="##class## eb"> </td>', '', '<td colspan=2 class="##class## eb" style="border-left: ##border##; border-bottom: ##border##;##bg##"> </td>' . "\n". ' <td rowspan=2 class="##class## va" style="##bg##"##edgecolor##><span class="sa">></span></td>', '<td colspan=2 class="##class## eb"> </td>', ], EDGE_LOOP_EAST() - EDGE_LABEL_CELL() => [ '<td rowspan=2 class="##class## eb"> </td>' . "\n" . ' <td colspan=2 rowspan=2 class="##class## lh" style="border-bottom: ##border##;##lc####bg##">##label##</td>' ."\n". ' <td rowspan=2 class="##class## eb"> </td>', '', '<td rowspan=2 class="##class## va" style="##bg##"##edgecolor##><span class="sh"><</span></td>' ."\n". ' <td colspan=2 class="##class## eb" style="border-bottom: ##border##;##bg##"> </td>'."\n". ' <td class="##class## eb" style="border-left: ##border##;##bg##"> </td>', '<td colspan=3 class="##class## eb"> </td>', ], ########################################################################### ########################################################################### # joints ########################################################################### # E_N_S EDGE_E_N_S() => [ '<td colspan=2 rowspan=2 class="##class## eb"> </td>' . "\n" . ' <td colspan=2 rowspan=2 class="##class## eb" style="border-left:##borderv##;border-bottom:##border##;##bg##"> </td>', '', '<td colspan=2 rowspan=2 class="##class## eb"> </td>' ."\n". ' <td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##borderv##;##bg##"> </td>', '', ], EDGE_E_N_S() + EDGE_END_E() => [ '<td colspan=2 rowspan=2 class="##class## eb"> </td>' . "\n" . ' <td rowspan=2 class="##class## eb" style="border-left: ##borderv##; border-bottom: ##border##;##bg##"> </td>' . "\n" . ' <td rowspan=4 class="##class## va"##edgecolor##><span class="sa">></span></td>', '', '<td colspan=2 rowspan=2 class="##class## eb"> </td>' ."\n". ' <td rowspan=2 class="##class## eb" style="border-left: ##borderv##;##bg##"> </td>', '', ], ########################################################################### # W_N_S EDGE_W_N_S() => [ '<td colspan=2 rowspan=2 class="##class## eb" style="border-bottom: ##border##;##bg##"> </td>' . "\n" . ' <td colspan=2 rowspan=4 class="##class## eb" style="border-left: ##borderv##;##bg##"> </td>', '', '<td colspan=2 rowspan=2 class="##class## eb"> </td>', '', ], ########################################################################### # S_E_W EDGE_S_E_W() => [ '<td colspan=4 rowspan=2 class="##class## eb" style="border-bottom: ##border##;##bg##"> </td>', '', '<td colspan=2 rowspan=2 class="##class## eb"> </td>' ."\n". ' <td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##borderv##;##bg##"> </td>', '', ], EDGE_S_E_W() + EDGE_END_S() => [ '<td colspan=4 rowspan=2 class="##class## eb" style="border-bottom: ##border##;##bg##"> </td>', '', '<td colspan=2 class="##class## eb"> </td>' ."\n". ' <td colspan=2 class="##class## eb" style="border-left: ##borderv##;##bg##"> </td>', $edge_end_south, ], EDGE_S_E_W() + EDGE_START_S() => [ '<td colspan=4 rowspan=2 class="##class## eb" style="border-bottom: ##border##;##bg##"> </td>', '', '<td colspan=2 class="##class## eb"> </td>' ."\n". ' <td colspan=2 class="##class## eb" style="border-left: ##borderv##;##bg##"> </td>', ' <td colspan=4 class="##class## eb"></td>', ], EDGE_S_E_W() + EDGE_START_W() => [ '<td rowspan=4 class="##class## el"></td>' . "\n" . '<td colspan=3 rowspan=2 class="##class## eb" style="border-bottom: ##border##;##bg##"> </td>', '', '<td rowspan=2 class="##class## eb"> </td>' ."\n". ' <td rowspan=2 class="##class## eb" style="border-left: ##borderv##;##bg##"> </td>', '', ], EDGE_S_E_W() + EDGE_END_E() => [ '<td colspan=3 rowspan=2 class="##class## eb" style="border-bottom: ##border##;##bg##"> </td>' . "\n" . ' <td rowspan=4 class="##class## va"##edgecolor##><span class="sa">></span></td>', '', '<td colspan=2 rowspan=2 class="##class## eb"> </td>' ."\n". ' <td rowspan=2 class="##class## eb" style="border-left: ##borderv##;##bg##"> </td>', '', ], EDGE_S_E_W() + EDGE_END_W() => [ $edge_arrow_west_upper . '<td colspan=3 rowspan=2 class="##class## eb" style="border-bottom: ##border##;##bg##"> </td>' . "\n" , '', '<td colspan=2 rowspan=2 class="##class## eb"> </td>' ."\n" . '<td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##borderv##;##bg##"> </td>', ], ########################################################################### # N_E_W EDGE_N_E_W() => [ ' <td colspan=2 rowspan=2 class="##class## eb" style="border-bottom: ##borderv##;##bg##"> </td>' ."\n". '<td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##borderv##; border-bottom: ##border##;##bg##"> </td>', '', '<td colspan=4 rowspan=2 class="##class## eb"> </td>', '', ], EDGE_N_E_W() + EDGE_END_N() => [ $edge_end_north, ' <td colspan=2 class="##class## eb" style="border-bottom: ##borderv##;##bg##"> </td>' ."\n". '<td colspan=2 class="##class## eb" style="border-left: ##borderv##; border-bottom: ##border##;##bg##"> </td>', '', '<td colspan=4 rowspan=2 class="##class## eb"> </td>', '', ], EDGE_N_E_W() + EDGE_START_N() => [ $edge_empty_row, ' <td colspan=2 class="##class## eb" style="border-bottom: ##borderv##;##bg##"> </td>' ."\n". '<td colspan=2 class="##class## eb" style="border-left: ##borderv##; border-bottom: ##border##;##bg##"> </td>', '', '<td colspan=4 rowspan=2 class="##class## eb"> </td>', '', ], }; sub _html_edge_hor { # Return HTML code for a horizontal edge (with all start/end combinations) # as [], with code for each table row. my ($self, $as) = @_; my $s_flags = $self->{type} & EDGE_START_MASK; my $e_flags = $self->{type} & EDGE_END_MASK; $e_flags = 0 if $as eq 'none'; # XXX TODO: we could skip the output of "eb" parts when this edge doesn't belong # to a group. my $rc = [ ' <td colspan=##mod## rowspan=2 class="##class## lh" style="border-bottom: ##border##;##lc####bg##">##label##</td>', '', '<td colspan=##mod## rowspan=2 class="##class## eb"> </td>', '', ]; # This assumes that only 2 end/start flags are set at the same time: my $mod = 4; # modifier if ($s_flags & EDGE_START_W) { $mod--; $rc->[0] = '<td rowspan=4 class="##class## el"></td>' . "\n" . $rc->[0]; }; if ($s_flags & EDGE_START_E) { $mod--; $rc->[0] .= "\n " . '<td rowspan=4 class="##class## el"></td>'; }; if ($e_flags & EDGE_END_W) { $mod--; $rc->[0] = $edge_arrow_west_upper . $rc->[0]; $rc->[2] = $edge_arrow_west_lower . $rc->[2]; } if ($e_flags & EDGE_END_E) { $mod--; $rc->[0] .= "\n " . $edge_arrow_east_upper; $rc->[2] .= "\n " . $edge_arrow_east_lower; }; # cx == 1: mod = 2..4, cx == 2: mod = 6..8, etc. $self->{cx} ||= 1; $mod = $self->{cx} * 4 - 4 + $mod; for my $e (@$rc) { $e =~ s/##mod##/$mod/g; } $rc; } sub _html_edge_ver { # Return HTML code for a vertical edge (with all start/end combinations) # as [], with code for each table row. my ($self, $as) = @_; my $s_flags = $self->{type} & EDGE_START_MASK; my $e_flags = $self->{type} & EDGE_END_MASK; $e_flags = 0 if $as eq 'none'; my $mod = 4; # modifier # normal vertical edge with no start/end flags my $rc = [ '<td colspan=2 rowspan=##mod## class="##class## el"> </td>' . "\n " . '<td colspan=2 rowspan=##mod## class="##class## lv" style="border-left: ##border##;##lc####bg##">##label##</td>' . "\n", '', '', '', ]; # flag north if ($s_flags & EDGE_START_N) { $mod--; unshift @$rc, '<td colspan=4 class="##class## eb"></td>' . "\n"; delete $rc->[-1]; } elsif ($e_flags & EDGE_END_N) { $mod--; unshift @$rc, $edge_end_north; delete $rc->[-1]; } # flag south if ($s_flags & EDGE_START_S) { $mod--; $rc->[3] = '<td colspan=4 class="##class## eb"></td>' . "\n" } if ($e_flags & EDGE_END_S) { $mod--; $rc->[3] = $edge_end_south; } $self->{cy} ||= 1; $mod = $self->{cy} * 4 - 4 + $mod; for my $e (@$rc) { $e =~ s/##mod##/$mod/g; } $rc; } sub _html_edge_cross { # Return HTML code for a crossingedge (with all start/end combinations) # as [], with code for each table row. my ($self, $N, $S, $E, $W) = @_; # my $s_flags = $self->{type} & EDGE_START_MASK; # my $e_flags = $self->{type} & EDGE_END_MASK; my $rc = [ ' <td colspan=2 rowspan=2 class="##class## eb el" style="border-bottom: ##border##"> </td>' . "\n" . ' <td colspan=2 rowspan=2 class="##class## eb el" style="border-left: ##borderv##; border-bottom: ##border##"> </td>' . "\n", '', ' <td colspan=2 rowspan=2 class="##class## eb el"></td>' . "\n" . ' <td colspan=2 rowspan=2 class="##class## eb el" style="border-left: ##borderv##"> </td>' . "\n", '', ]; $rc; } sub as_html { my ($self) = shift; my $type = $self->{type} & EDGE_NO_M_MASK; my $style = $self->{style}; # none, open, filled, closed my $as; $as = 'none' if $self->{edge}->{undirected}; $as = $self->attribute('arrowstyle') unless $as; # triangle, box, dot, inv, diamond, line etc. my $ashape; $ashape = 'triangle' if $self->{edge}->{undirected}; $ashape = $self->attribute('arrowshape') unless $ashape; my $code = $edge_html->{$type}; if (!defined $code) { my $t = $self->{type} & EDGE_TYPE_MASK; if ($style ne 'invisible') { $code = $self->_html_edge_hor($as) if $t == EDGE_HOR; $code = $self->_html_edge_ver($as) if $t == EDGE_VER; $code = $self->_html_edge_cross($as) if $t == EDGE_CROSS; } else { $code = [ ' <td colspan=4 rowspan=4 class="##class##"> </td>' ]; } if (!defined $code) { $code = [ ' <td colspan=4 rowspan=4 class="##class##">???</td>' ]; warn ("as_html: Unimplemented edge type $self->{type} ($type) at $self->{x},$self->{y} " . edge_type($self->{type})); } } my $id = $self->{graph}->{id}; my $color = $self->get_color_attribute('color'); my $label = ''; my $label_style = ''; # only include the label if we are the label cell if ($style ne 'invisible' && ($self->{type} & EDGE_LABEL_CELL)) { my $switch_to_center; ($label,$switch_to_center) = $self->_label_as_html(); # replace linebreaks by <br>, but remove extra spaces $label =~ s/\s*\\n\s*/<br \/>/g; my $label_color = $self->raw_color_attribute('labelcolor') || $color; $label_color = '' if $label_color eq '#000000'; $label_style = "color: $label_color;" if $label_color; my $font = $self->attribute('font') || ''; $font = '' if $font eq ($self->default_attribute('font') || ''); $label_style = "font-family: $font;" if $font; $label_style .= $self->text_styles_as_css(1,1) unless $label eq ''; $label_style =~ s/^\s*//; my $link = $self->link(); if ($link ne '') { # encode critical entities $link =~ s/\s/\+/g; # space $link =~ s/'/%27/g; # single-quote # put the style on the link $label_style = " style='$label_style'" if $label_style; $label = "<a href='$link'$label_style>$label</a>"; $label_style = ''; } } # without  , IE doesn't draw the cell-border nec. for edges $label = ' ' unless $label ne ''; ########################################################################### # get the border styles/colors: # width for the edge is "2px" my $bow = '2'; my $border = Graph::Easy::_border_attribute_as_html( $self->{style}, $bow, $color); my $border_v = $border; if (($self->{type} & EDGE_TYPE_MASK) == EDGE_CROSS) { $border_v = Graph::Easy::_border_attribute_as_html( $self->{style_ver}, $bow, $self->{color_ver}); } ########################################################################### my $edge_color = ''; $edge_color = " color: $color;" if $color; # If the group doesn't have a fill attribute, then it is defined in the CSS # of the group, and since we get the same class, we can skip the background. # But if the group has a fill, we need to use this as override. # The idea behind is to omit the "background: #daffff;" as much as possible. my $bg = $self->attribute('background') || ''; my $group = $self->{edge}->{group}; $bg = '' if $bg eq 'inherit'; $bg = $group->{att}->{fill} if $group->{att}->{fill} && $bg eq ''; $bg = '' if $bg eq 'inherit'; $bg = " background: $bg;" if $bg; my $title = $self->title(); $title =~ s/"//g; # replace quotation marks $title = " title=\"$title\"" if $title ne ''; # add mouse-over title ########################################################################### # replace templates require Graph::Easy::As_ascii if $as ne 'none'; # for _unicode_arrow() # replace borderv with the border for the vertical edge on CROSS sections $border =~ s/\s+/ /g; # collapse multiple spaces $border_v =~ s/\s+/ /g; my $cl = $self->class(); $cl =~ s/\./_/g; # group.cities => group_cities my $rc; for my $a (@$code) { if (ref($a)) { for my $c (@$a) { push @$rc, $self->_format_td($c, $border, $border_v, $label_style, $edge_color, $bg, $as, $ashape, $title, $label, $cl); } } else { push @$rc, $self->_format_td($a, $border, $border_v, $label_style, $edge_color, $bg, $as, $ashape, $title, $label, $cl); } } $rc; } sub _format_td { my ($self, $c, $border, $border_v, $label_style, $edge_color, $bg, $as, $ashape, $title, $label, $cl) = @_; # insert 'style="##bg##"' unless there is already a style $c =~ s/( e[bl]")(>( )?<\/td>)/$1 style="##bg##"$2/g; # insert missing "##bg##" $c =~ s/style="border/style="##bg##border/g; $c =~ s/##class##/$cl/g; $c =~ s/##border##/$border/g; $c =~ s/##borderv##/$border_v/g; $c =~ s/##lc##/$label_style/g; $c =~ s/##edgecolor##/ style="$edge_color"/g; $c =~ s/##ec##/$edge_color/g; $c =~ s/##bg##/$bg/g; $c =~ s/ style=""//g; # remove empty styles # remove arrows if edge is undirected $c =~ s/>(v|\^|<|>)/>/g if $as eq 'none'; # insert "nice" looking Unicode arrows $c =~ s/>(v|\^|<|>)/'>' . $self->_unicode_arrow($ashape, $as, $1); /eg; # insert the label last, other "v" as label might get replaced above $c =~ s/>##label##/$title>$label/; # for empty labels use a different class $c =~ s/ lh"/ eb"/ if $label eq ''; $c .= "\n" unless $c =~ /\n\z/; $self->quoted_comment() . $c; } sub class { my $self = shift; my $c = $self->{class} . ($self->{cell_class} || ''); $c = $self->{edge}->{group}->class() . ' ' . $c if ref($self->{edge}->{group}); $c; } sub group { # return the group we belong to as the group of our parent-edge my $self = shift; $self->{edge}->{group}; } ############################################################################# # accessor methods sub type { # get/set type of this path element # type - EDGE_START, EDGE_END, EDGE_HOR, EDGE_VER, etc my ($self,$type) = @_; if (defined $type) { if (defined $type && $type < 0 || $type > EDGE_MAX_TYPE) { require Carp; Carp::confess ("Cell type $type for cell $self->{x},$self->{y} is not valid."); } $self->{type} = $type; } $self->{type}; } ############################################################################# # For rendering this path element as ASCII, we need to correct our width based # on whether we have a border or not. But this is only known after parsing is # complete. sub _correct_size { my ($self,$format) = @_; return if defined $self->{w}; # min-size is this $self->{w} = 5; $self->{h} = 3; # make short cell pieces very small if (($self->{type} & EDGE_SHORT_CELL) != 0) { $self->{w} = 1; $self->{h} = 1; return; } my $arrows = ($self->{type} & EDGE_ARROW_MASK); my $type = ($self->{type} & EDGE_TYPE_MASK); if ($self->{edge}->{bidirectional} && $arrows != 0) { $self->{w}++ if $type == EDGE_HOR; $self->{h}++ if $type == EDGE_VER; } # make joints bigger if they got arrows my $ah = $self->{type} & EDGE_ARROW_HOR; my $av = $self->{type} & EDGE_ARROW_VER; $self->{w}++ if $ah && ($type == EDGE_S_E_W || $type == EDGE_N_E_W); $self->{h}++ if $av && ($type == EDGE_E_N_S || $type == EDGE_W_N_S); my $style = $self->{edge}->attribute('style') || 'solid'; # make the edge to display ' ..-> ' instead of ' ..> ': $self->{w}++ if $style eq 'dot-dot-dash'; if ($type >= EDGE_LOOP_TYPE) { # +---+ # | V # + # +--> | # | | # +--- | # + $self->{w} = 7; $self->{w} = 8 if $type == EDGE_N_W_S || $type == EDGE_S_W_N; $self->{h} = 3; $self->{h} = 5 if $type != EDGE_N_W_S && $type != EDGE_S_W_N; } if ($self->{type} == EDGE_HOR) { $self->{w} = 0; } elsif ($self->{type} == EDGE_VER) { $self->{h} = 0; } elsif ($self->{type} & EDGE_LABEL_CELL) { # edges do not have borders my ($w,$h) = $self->dimensions(); $h-- unless $h == 0; $h += $self->{h}; $w += $self->{w}; $self->{w} = $w; $self->{h} = $h; } } ############################################################################# # attribute handling sub attribute { my ($self, $name) = @_; my $edge = $self->{edge}; # my $native = $edge->{att}->{$name}; # return $native if defined $native && $native ne 'inherit'; # shortcut, look up the attribute directly return $edge->{att}->{$name} if defined $edge->{att}->{$name} && $edge->{att}->{$name} ne 'inherit'; return $edge->attribute($name); # XXX TODO This does not work, since caching the attribute doesn't get invalidated # upon set_attribute(). # $edge->{cache} = {} unless exists $edge->{cache}; # $edge->{cache}->{att} = {} unless exists $edge->{cache}->{att}; # # my $cache = $edge->{cache}->{att}; # return $cache->{$name} if exists $cache->{$name}; # # my $rc = $edge->attribute($name); # # only cache values that weren't inherited to avoid cache problems # $cache->{$name} = $rc unless defined $native && $native eq 'inherit'; # # $rc; } 1; ############################################################################# ############################################################################# package Graph::Easy::Edge::Cell::Empty; require Graph::Easy::Node::Cell; our @ISA = qw/Graph::Easy::Node::Cell/; #use vars qw/$VERSION/; our $VERSION = '0.02'; use constant isa_cell => 1; 1; __END__ =head1 NAME Graph::Easy::Edge::Cell - A cell in an edge in Graph::Easy =head1 SYNOPSIS use Graph::Easy; my $ssl = Graph::Easy::Edge->new( label => 'encrypted connection', style => 'solid', color => 'red', ); my $src = Graph::Easy::Node->new( 'source' ); my $dst = Graph::Easy::Node->new( 'destination' ); $graph = Graph::Easy->new(); $graph->add_edge($src, $dst, $ssl); print $graph->as_ascii(); =head1 DESCRIPTION A C<Graph::Easy::Edge::Cell> represents an edge between two (or more) nodes in a simple graph. Each edge has a direction (from source to destination, or back and forth), plus a style (line width and style), colors etc. It can also have a name, e.g. a text label associated with it. There should be no need to use this package directly. =head1 METHODS =head2 error() $last_error = $edge->error(); $cvt->error($error); # set new messags $cvt->error(''); # clear error Returns the last error message, or '' for no error. =head2 as_ascii() my $ascii = $path->as_ascii(); Returns the path-cell as a little ascii representation. =head2 as_html() my $html = $path->as_html($tag,$id); eturns the path-cell as HTML code. =head2 label() my $label = $path->label(); Returns the name (also known as 'label') of the path-cell. =head2 style() my $style = $edge->style(); Returns the style of the edge. =head1 EXPORT None by default. Can export the following on request: EDGE_START_E EDGE_START_W EDGE_START_N EDGE_START_S EDGE_END_E EDGE_END_W EDGE_END_N EDGE_END_S EDGE_SHORT_E EDGE_SHORT_W EDGE_SHORT_N EDGE_SHORT_S EDGE_SHORT_BD_EW EDGE_SHORT_BD_NS EDGE_SHORT_UN_EW EDGE_SHORT_UN_NS EDGE_HOR EDGE_VER EDGE_CROSS EDGE_N_E EDGE_N_W EDGE_S_E EDGE_S_W EDGE_S_E_W EDGE_N_E_W EDGE_E_N_S EDGE_W_N_S EDGE_LOOP_NORTH EDGE_LOOP_SOUTH EDGE_LOOP_EAST EDGE_LOOP_WEST EDGE_N_W_S EDGE_S_W_N EDGE_E_S_W EDGE_W_S_E EDGE_TYPE_MASK EDGE_FLAG_MASK EDGE_ARROW_MASK EDGE_START_MASK EDGE_END_MASK EDGE_MISC_MASK ARROW_RIGHT ARROW_LEFT ARROW_UP ARROW_DOWN =head1 SEE ALSO L<Graph::Easy>. =head1 AUTHOR Copyright (C) 2004 - 2007 by Tels L<http://bloodgate.com>. See the LICENSE file for more details. =cut ���Graph-Easy-0.73/lib/Graph/Easy/Node.pm��������������������������������������������������������������0000644�0000764�0000764�00000213566�12150074533�017247� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������############################################################################# # Represents one node in a Graph::Easy graph. # # (c) by Tels 2004-2008. Part of Graph::Easy. ############################################################################# package Graph::Easy::Node; $VERSION = '0.38'; use Graph::Easy::Base; use Graph::Easy::Attributes; @ISA = qw/Graph::Easy::Base/; use Graph::Easy::Util qw(ord_values); # to map "arrow-shape" to "arrowshape" my $att_aliases; use strict; use constant isa_cell => 0; sub _init { # Generic init routine, to be overridden in subclasses. my ($self,$args) = @_; $self->{name} = 'Node #' . $self->{id}; $self->{att} = { }; $self->{class} = 'node'; # default class foreach my $k (sort keys %$args) { if ($k !~ /^(label|name)\z/) { require Carp; Carp::confess ("Invalid argument '$k' passed to Graph::Easy::Node->new()"); } $self->{$k} = $args->{$k} if $k eq 'name'; $self->{att}->{$k} = $args->{$k} if $k eq 'label'; } # These are undef (to save memory) until needed: # $self->{children} = {}; # $self->{dx} = 0; # relative to no other node # $self->{dy} = 0; # $self->{origin} = undef; # parent node (for relative placement) # $self->{group} = undef; # $self->{parent} = $graph or $group; # Mark as not yet laid out: # $self->{x} = 0; # $self->{y} = 0; $self; } my $merged_borders = { 'dotteddashed' => 'dot-dash', 'dasheddotted' => 'dot-dash', 'double-dashdouble' => 'double', 'doubledouble-dash' => 'double', 'doublesolid' => 'double', 'soliddouble' => 'double', 'dotteddot-dash' => 'dot-dash', 'dot-dashdotted' => 'dot-dash', }; sub _collapse_borders { # Given a right border from node one, and the left border of node two, # return what border we need to draw on node two: my ($self, $one, $two, $swapem) = @_; ($one,$two) = ($two,$one) if $swapem; $one = 'none' unless $one; $two = 'none' unless $two; # If the border of the left/top node is defined, we don't draw the # border of the right/bottom node. return 'none' if $one ne 'none' || $two ne 'none'; # otherwise, we draw simple the right border $two; } sub _merge_borders { my ($self, $one, $two) = @_; $one = 'none' unless $one; $two = 'none' unless $two; # "nonenone" => "none" or "dotteddotted" => "dotted" return $one if $one eq $two; # none + solid == solid + none == solid return $one if $two eq 'none'; return $two if $one eq 'none'; for my $b (qw/broad wide bold double solid/) { # the stronger one overrides the weaker one return $b if $one eq $b || $two eq $b; } my $both = $one . $two; return $merged_borders->{$both} if exists $merged_borders->{$both}; # fallback $two; } sub _border_to_draw { # Return the border style we need to draw, taking the shape (none) into # account my ($self, $shape) = @_; my $cache = $self->{cache}; return $cache->{border_style} if defined $cache->{border_style}; $shape = $self->{att}->{shape} unless defined $shape; $shape = $self->attribute('shape') unless defined $shape; $cache->{border_style} = $self->{att}->{borderstyle}; $cache->{border_style} = $self->attribute('borderstyle') unless defined $cache->{border_style}; $cache->{border_style} = 'none' if $shape =~ /^(none|invisible)\z/; $cache->{border_style}; } sub _border_styles { # Return the four border styles (right, bottom, left, top). This takes # into account the neighbouring nodes and their borders, so that on # ASCII output the borders can be properly collapsed. my ($self, $border, $collapse) = @_; my $cache = $self->{cache}; # already computed values? return if defined $cache->{left_border}; $cache->{left_border} = $border; $cache->{top_border} = $border; $cache->{right_border} = $border; $cache->{bottom_border} = $border; return unless $collapse; # print STDERR " border_styles: $self->{name} border=$border\n"; my $EM = 14; my $border_width = Graph::Easy::_border_width_in_pixels($self,$EM); # convert overly broad borders to the correct style $border = 'bold' if $border_width > 2; $border = 'broad' if $border_width > $EM * 0.2 && $border_width < $EM * 0.75; $border = 'wide' if $border_width >= $EM * 0.75; # XXX TODO # handle different colors, too: # my $color = $self->color_attribute('bordercolor'); # Draw border on A (left), and C (left): # # +---+ # B | A | C # +---+ # Ditto, plus C's border: # # +---+---+ # B | A | C | # +---+---+ # # If no left neighbour, draw border normally # XXX TODO: ->{parent} ? my $parent = $self->{parent} || $self->{graph}; return unless ref $parent; my $cells = $parent->{cells}; return unless ref $cells; my $x = $self->{x}; my $y = $self->{y}; $x -= 1; my $left = $cells->{"$x,$y"}; $x += 1; $y-= 1; my $top = $cells->{"$x,$y"}; $x += 1; $y += 1; my $right = $cells->{"$x,$y"}; $x -= 1; $y += 1; my $bottom = $cells->{"$x,$y"}; # where to store the result my @where = ('left', 'top', 'right', 'bottom'); # need to swap arguments to _collapse_borders()? my @swapem = (0, 0, 1, 1); for my $other ($left, $top, $right, $bottom) { my $side = shift @where; my $swap = shift @swapem; # see if we have a (visible) neighbour on the left side if (ref($other) && !$other->isa('Graph::Easy::Edge') && !$other->isa_cell() && !$other->isa('Graph::Easy::Node::Empty')) { $other = $other->{node} if ref($other->{node}); # print STDERR "$side node $other ", $other->_border_to_draw(), " vs. $border (swap $swap)\n"; if ($other->attribute('shape') ne 'invisible') { # yes, so take its border style my $result; if ($swap) { $result = $self->_merge_borders($other->_border_to_draw(), $border); } else { $result = $self->_collapse_borders($border, $other->_border_to_draw()); } $cache->{$side . '_border'} = $result; # print STDERR "# result: $result\n"; } } } } sub _correct_size { # Correct {w} and {h} after parsing. This is a fallback in case # the output specific routines (_correct_site_ascii() etc) do # not exist. my $self = shift; return if defined $self->{w}; my $shape = $self->attribute('shape'); if ($shape eq 'point') { $self->{w} = 5; $self->{h} = 3; my $style = $self->attribute('pointstyle'); my $shape = $self->attribute('pointshape'); if ($style eq 'invisible' || $shape eq 'invisible') { $self->{w} = 0; $self->{h} = 0; return; } } elsif ($shape eq 'invisible') { $self->{w} = 3; $self->{h} = 3; } else { my ($w,$h) = $self->dimensions(); $self->{h} = $h; $self->{w} = $w + 2; } my $border = $self->_border_to_draw($shape); $self->_border_styles($border, 'collapse'); # print STDERR "# $self->{name} $self->{w} $self->{h} $shape\n"; # use Data::Dumper; print Dumper($self->{cache}); if ($shape !~ /^(invisible|point)/) { $self->{w} ++ if $self->{cache}->{right_border} ne 'none'; $self->{w} ++ if $self->{cache}->{left_border} ne 'none'; $self->{h} ++ if $self->{cache}->{top_border} ne 'none'; $self->{h} ++ if $self->{cache}->{bottom_border} ne 'none'; $self->{h} += 2 if $border eq 'none' && $shape !~ /^(invisible|point)/; } $self; } sub _unplace { # free the cells this node occupies from $cells my ($self,$cells) = @_; my $x = $self->{x}; my $y = $self->{y}; delete $cells->{"$x,$y"}; $self->{x} = undef; $self->{y} = undef; $self->{cache} = {}; $self->_calc_size() unless defined $self->{cx}; if ($self->{cx} + $self->{cy} > 2) # one of them > 1! { for my $ax (1..$self->{cx}) { my $sx = $x + $ax - 1; for my $ay (1..$self->{cy}) { my $sy = $y + $ay - 1; # free cell delete $cells->{"$sx,$sy"}; } } } # end handling multi-celled node # unplace all edges leading to/from this node, too: for my $e (ord_values ( $self->{edges} )) { $e->_unplace($cells); } $self; } sub _mark_as_placed { # for creating an action on the action stack we also need to recursively # mark all our children as already placed: my ($self) = @_; no warnings 'recursion'; delete $self->{_todo}; for my $child (ord_values ( $self->{children} )) { $child->_mark_as_placed(); } $self; } sub _place_children { # recursively place node and its children my ($self, $x, $y, $parent) = @_; no warnings 'recursion'; return 0 unless $self->_check_place($x,$y,$parent); print STDERR "# placing children of $self->{name} based on $x,$y\n" if $self->{debug}; for my $child (ord_values ( $self->{children} )) { # compute place of children (depending on whether we are multicelled or not) my $dx = $child->{dx} > 0 ? $self->{cx} - 1 : 0; my $dy = $child->{dy} > 0 ? $self->{cy} - 1 : 0; my $rc = $child->_place_children($x + $dx + $child->{dx},$y + $dy + $child->{dy},$parent); return $rc if $rc == 0; } $self->_place($x,$y,$parent); } sub _place { # place this node at the requested position (without checking) my ($self, $x, $y, $parent) = @_; my $cells = $parent->{cells}; $self->{x} = $x; $self->{y} = $y; $cells->{"$x,$y"} = $self; # store our position if we are the first node in that rank my $r = abs($self->{rank} || 0); my $what = $parent->{_rank_coord} || 'x'; # 'x' or 'y' $parent->{_rank_pos}->{ $r } = $self->{$what} unless defined $parent->{_rank_pos}->{ $r }; # a multi-celled node will be stored like this: # [ node ] [ filler ] # [ filler ] [ filler ] # [ filler ] [ filler ] etc. # $self->_calc_size() unless defined $self->{cx}; if ($self->{cx} + $self->{cy} > 2) # one of them > 1! { for my $ax (1..$self->{cx}) { my $sx = $x + $ax - 1; for my $ay (1..$self->{cy}) { next if $ax == 1 && $ay == 1; # skip left-upper most cell my $sy = $y + $ay - 1; # We might even get away with creating only one filler cell # although then its "x" and "y" values would be "wrong". my $filler = Graph::Easy::Node::Cell->new ( node => $self, x => $sx, y => $sy ); $cells->{"$sx,$sy"} = $filler; } } } # end handling of multi-celled node $self->_update_boundaries($parent); 1; # did place us } sub _check_place { # chack that a node can be placed at $x,$y (w/o checking its children) my ($self,$x,$y,$parent) = @_; my $cells = $parent->{cells}; # node cannot be placed here return 0 if exists $cells->{"$x,$y"}; $self->_calc_size() unless defined $self->{cx}; if ($self->{cx} + $self->{cy} > 2) # one of them > 1! { for my $ax (1..$self->{cx}) { my $sx = $x + $ax - 1; for my $ay (1..$self->{cy}) { my $sy = $y + $ay - 1; # node cannot be placed here return 0 if exists $cells->{"$sx,$sy"}; } } } 1; # can place it here } sub _do_place { # Tries to place the node at position ($x,$y) by checking that # $cells->{"$x,$y"} is still free. If the node belongs to a cluster, # checks all nodes of the cluster (and when all of them can be # placed simultanously, does so). # Returns true if the operation succeeded, otherwise false. my ($self,$x,$y,$parent) = @_; my $cells = $parent->{cells}; # inlined from _check() for speed reasons: # node cannot be placed here return 0 if exists $cells->{"$x,$y"}; $self->_calc_size() unless defined $self->{cx}; if ($self->{cx} + $self->{cy} > 2) # one of them > 1! { for my $ax (1..$self->{cx}) { my $sx = $x + $ax - 1; for my $ay (1..$self->{cy}) { my $sy = $y + $ay - 1; # node cannot be placed here return 0 if exists $cells->{"$sx,$sy"}; } } } my $children = 0; $children = scalar keys %{$self->{children}} if $self->{children}; # relativ to another, or has children (relativ to us) if (defined $self->{origin} || $children > 0) { # The coordinates of the origin node. Because 'dx' and 'dy' give # our distance from the origin, we can compute the origin by doing # "$x - $dx" my $grandpa = $self; my $ox = 0; my $oy = 0; # Find our grandparent (e.g. the root of origin chain), and the distance # from $x,$y to it: ($grandpa,$ox,$oy) = $self->find_grandparent() if $self->{origin}; # Traverse all children and check their places, place them if poss. # This will also place ourselves, because we are a grandchild of $grandpa return $grandpa->_place_children($x + $ox,$y + $oy,$parent); } # finally place this node at the requested position $self->_place($x,$y,$parent); } ############################################################################# sub _wrapped_label { # returns the label wrapped automatically to use the least space my ($self, $label, $align, $wrap) = @_; return (@{$self->{cache}->{label}}) if $self->{cache}->{label}; # XXX TODO: handle "paragraphs" $label =~ s/\\(n|r|l|c)/ /g; # replace line splits by spaces # collapse multiple spaces $label =~ s/\s+/ /g; # find out where to wrap if ($wrap eq 'auto') { $wrap = int(sqrt(length($label)) * 1.4); } $wrap = 2 if $wrap < 2; # run through the text and insert linebreaks my $i = 0; my $line_len = 0; my $last_space = 0; my $last_hyphen = 0; my @lines; while ($i < length($label)) { my $c = substr($label,$i,1); $last_space = $i if $c eq ' '; $last_hyphen = $i if $c eq '-'; $line_len ++; if ($line_len >= $wrap && ($last_space != 0 || $last_hyphen != 0)) { # print STDERR "# wrap at $line_len\n"; my $w = $last_space; my $replace = ''; if ($last_hyphen > $last_space) { $w = $last_hyphen; $replace = '-'; } # print STDERR "# wrap at $w\n"; # "foo bar-baz" => "foo bar" (lines[0]) and "baz" (label afterwards) # print STDERR "# first part '". substr($label, 0, $w) . "'\n"; push @lines, substr($label, 0, $w) . $replace; substr($label, 0, $w+1) = ''; # reset counters $line_len = 0; $i = 0; $last_space = 0; $last_hyphen = 0; next; } $i++; } # handle what is left over push @lines, $label if $label ne ''; # generate the align array my @aligns; my $al = substr($align,0,1); for my $i (0.. scalar @lines) { push @aligns, $al; } # cache the result to avoid costly recomputation $self->{cache}->{label} = [ \@lines, \@aligns ]; (\@lines, \@aligns); } sub _aligned_label { # returns the label lines and for each one the alignment l/r/c my ($self, $align, $wrap) = @_; $align = 'center' unless $align; $wrap = $self->attribute('textwrap') unless defined $wrap; my $name = $self->label(); return $self->_wrapped_label($name,$align,$wrap) unless $wrap eq 'none'; my (@lines,@aligns); my $al = substr($align,0,1); my $last_align = $al; # split up each line from the front while ($name ne '') { $name =~ s/^(.*?([^\\]|))(\z|\\(n|r|l|c))//; my $part = $1; my $a = $3 || '\n'; $part =~ s/\\\|/\|/g; # \| => | $part =~ s/\\\\/\\/g; # '\\' to '\' $part =~ s/^\s+//; # remove spaces at front $part =~ s/\s+\z//; # remove spaces at end $a =~ s/\\//; # \n => n $a = $al if $a eq 'n'; push @lines, $part; push @aligns, $last_align; $last_align = $a; } # XXX TODO: should remove empty lines at start/end? (\@lines, \@aligns); } ############################################################################# # as_html conversion and helper functions related to that my $remap = { node => { align => undef, background => undef, basename => undef, border => undef, borderstyle => undef, borderwidth => undef, bordercolor => undef, columns => undef, fill => 'background', origin => undef, offset => undef, pointstyle => undef, pointshape => undef, rows => undef, size => undef, shape => undef, }, edge => { fill => undef, border => undef, }, all => { align => 'text-align', autolink => undef, autotitle => undef, comment => undef, fontsize => undef, font => 'font-family', flow => undef, format => undef, label => undef, link => undef, linkbase => undef, style => undef, textstyle => undef, title => undef, textwrap => \&Graph::Easy::_remap_text_wrap, group => undef, }, }; sub _extra_params { # return text with a leading " ", that will be appended to "td" when # generating HTML ''; } # XXX TODO: <span class="o">? my $pod = { B => [ '<b>', '</b>' ], O => [ '<span style="text-decoration: overline">', '</span>' ], S => [ '<span style="text-decoration: line-through">', '</span>' ], U => [ '<span style="text-decoration: underline">', '</span>' ], C => [ '<code>', '</code>' ], I => [ '<i>', '</i>' ], }; sub _convert_pod { my ($self, $type, $text) = @_; my $t = $pod->{$type} or return $text; # "<b>" . "text" . "</b>" $t->[0] . $text . $t->[1]; } sub _label_as_html { # Build the text from the lines, by inserting <b> for each break # Also align each line, and if nec., convert B<bold> to <b>bold</b>. my ($self) = @_; my $align = $self->attribute('align'); my $text_wrap = $self->attribute('textwrap'); my ($lines,$aligns); if ($text_wrap eq 'auto') { # set "white-space: nowrap;" in CSS and ignore linebreaks in label $lines = [ $self->label() ]; $aligns = [ substr($align,0,1) ]; } else { ($lines,$aligns) = $self->_aligned_label($align,$text_wrap); } # Since there is no "float: center;" in CSS, we must set the general # text-align to center when we encounter any \c and the default is # left or right: my $switch_to_center = 0; if ($align ne 'center') { local $_; $switch_to_center = grep /^c/, @$aligns; } $align = 'center' if $switch_to_center; my $a = substr($align,0,1); # center => c my $format = $self->attribute('format'); my $name = ''; my $i = 0; while ($i < @$lines) { my $line = $lines->[$i]; my $al = $aligns->[$i]; # This code below will not handle B<bold\n and bolder> due to the # line break. Also, nesting does not work due to returned "<" and ">". if ($format eq 'pod') { # first inner-most, then go outer until there are none left $line =~ s/([BOSUCI])<([^<>]+)>/ $self->_convert_pod($1,$2);/eg while ($line =~ /[BOSUCI]<[^<>]+>/) } else { $line =~ s/&/&/g; # quote & $line =~ s/>/>/g; # quote > $line =~ s/</</g; # quote < $line =~ s/\\\\/\\/g; # "\\" to "\" } # insert a span to align the line unless the default already covers it $line = '<span class="' . $al . '">' . $line . '</span>' if $a ne $al; $name .= '<br>' . $line; $i++; # next line } $name =~ s/^<br>//; # remove first <br> ($name, $switch_to_center); } sub quoted_comment { # Comment of this object, quoted suitable as to be embedded into HTML/SVG my $self = shift; my $cmt = $self->attribute('comment'); if ($cmt ne '') { $cmt =~ s/&/&/g; $cmt =~ s/</</g; $cmt =~ s/>/>/g; $cmt = '<!-- ' . $cmt . " -->\n"; } $cmt; } sub as_html { # return node as HTML my ($self) = @_; my $shape = 'rect'; $shape = $self->attribute('shape') unless $self->isa_cell(); if ($shape eq 'edge') { my $edge = Graph::Easy::Edge->new(); my $cell = Graph::Easy::Edge::Cell->new( edge => $edge ); $cell->{w} = $self->{w}; $cell->{h} = $self->{h}; $cell->{att}->{label} = $self->label(); $cell->{type} = Graph::Easy::Edge::Cell->EDGE_HOR + Graph::Easy::Edge::Cell->EDGE_LABEL_CELL; return $cell->as_html(); } my $extra = $self->_extra_params(); my $taga = "td$extra"; my $tagb = 'td'; my $id = $self->{graph}->{id}; my $a = $self->{att}; my $g = $self->{graph}; my $class = $self->class(); # how many rows/columns will this node span? my $rs = ($self->{cy} || 1) * 4; my $cs = ($self->{cx} || 1) * 4; # shape: invisible; must result in an empty cell if ($shape eq 'invisible' && $class ne 'node.anon') { return " <$taga colspan=$cs rowspan=$rs style=\"border: none; background: inherit;\"></$tagb>\n"; } my $c = $class; $c =~ s/\./_/g; # node.city => node_city my $html = " <$taga colspan=$cs rowspan=$rs##class####style##"; my $title = $self->title(); $title =~ s/'//g; # replace quotation marks $html .= " title='$title'" if $title ne '' && $shape ne 'img'; # add mouse-over title my ($name, $switch_to_center); if ($shape eq 'point') { require Graph::Easy::As_ascii; # for _u8 and point-style local $self->{graph}->{_ascii_style} = 1; # use utf-8 $name = $self->_point_style( $self->attribute('pointshape'), $self->attribute('pointstyle') ); } elsif ($shape eq 'img') { # take the label as the URL, but escape critical characters $name = $self->label(); $name =~ s/\s/\+/g; # space $name =~ s/'/%27/g; # replace quotation marks $name =~ s/[\x0d\x0a]//g; # remove 0x0d0x0a and similiar my $t = $title; $t = $name if $t eq ''; $name = "<img src='$name' alt='$t' title='$t' border='0' />"; } else { ($name,$switch_to_center) = $self->_label_as_html(); } # if the label is "", the link wouldn't be clickable my $link = ''; $link = $self->link() unless $name eq ''; # the attributes in $out will be applied to either the TD, or the inner DIV, # unless if we have a link, then most of them will be moved to the A HREF my $att = $self->raw_attributes(); my $out = $self->{graph}->_remap_attributes( $self, $att, $remap, 'noquote', 'encode', 'remap_colors'); $out->{'text-align'} = 'center' if $switch_to_center; # only for nodes, not for edges if (!$self->isa('Graph::Easy::Edge')) { my $bc = $self->attribute('bordercolor'); my $bw = $self->attribute('borderwidth'); my $bs = $self->attribute('borderstyle'); $out->{border} = Graph::Easy::_border_attribute_as_html( $bs, $bw, $bc ); # we need to specify the border again for the inner div if ($shape !~ /(rounded|ellipse|circle)/) { my $DEF = $self->default_attribute('border'); delete $out->{border} if $out->{border} =~ /^\s*\z/ || $out->{border} eq $DEF; } delete $out->{border} if $class eq 'node.anon' && $out->{border} && $out->{border} eq 'none'; } # we compose the inner part as $inner_start . $label . $inner_end: my $inner_start = ''; my $inner_end = ''; if ($shape =~ /(rounded|ellipse|circle)/) { # set the fill on the inner part, but the background and no border on the <td>: my $inner_style = ''; my $fill = $self->color_attribute('fill'); $inner_style = 'background:' . $fill if $fill; $inner_style .= ';border:' . $out->{border} if $out->{border}; $inner_style =~ s/;\s?\z$//; # remove '; ' at end delete $out->{background}; delete $out->{border}; my $td_style = ''; $td_style = ' style="border: none;'; my $bg = $self->color_attribute('background'); $td_style .= "background: $bg\""; $html =~ s/##style##/$td_style/; $inner_end = '</span></div>'; my $c = substr($shape, 0, 1); $c = 'c' if $c eq 'e'; # 'r' or 'c' my ($w,$h) = $self->dimensions(); if ($shape eq 'circle') { # set both to the biggest size to enforce a circle shape my $r = $w; $r = $h if $h > $w; $w = $r; $h = $r; } $out->{top} = ($h / 2 + 0.5) . 'em'; delete $out->{top} if $out->{top} eq '1.5em'; $h = ($h + 2) . 'em'; $w = ($w + 2) . 'em'; $inner_style .= ";width: $w; height: $h"; $inner_style = " style='$inner_style'"; $inner_start = "<div class='$c'$inner_style><span class='c'##style##>"; } if ($class =~ /^group/) { delete $out->{border}; delete $out->{background}; my $group_class = $class; $group_class =~ s/\s.*//; # "group gt" => "group" my @atr = qw/bordercolor borderwidth fill/; # transform "group_foo gr" to "group_foo" if border eq 'none' (for anon groups) my $border_style = $self->attribute('borderstyle'); $c =~ s/\s+.*// if $border_style eq 'none'; # only need the color for the label cell push @atr, 'color' if $self->{has_label}; $name = ' ' unless $self->{has_label}; for my $b (@atr) { my $def = $g->attribute($group_class,$b); my $v = $self->attribute($b); my $n = $b; $n = 'background' if $b eq 'fill'; $out->{$n} = $v unless $v eq '' || $v eq $def; } $name = ' ' unless $name ne ''; } # "shape: none;" or point means no border, and background instead fill color if ($shape =~ /^(point|none)\z/) { $out->{background} = $self->color_attribute('background'); $out->{border} = 'none'; } my $style = ''; for my $atr (sort keys %$out) { if ($link ne '') { # put certain styles on the outer container, and not on the link next if $atr =~ /^(background|border)\z/; } $style .= "$atr: $out->{$atr}; "; } # bold, italic, underline etc. (but not for empty cells) $style .= $self->text_styles_as_css(1,1) if $name !~ /^(| )\z/; $style =~ s/;\s?\z$//; # remove '; ' at end $style =~ s/\s+/ /g; # ' ' => ' ' $style =~ s/^\s+//; # remove ' ' at front $style = " style=\"$style\"" if $style; my $end_tag = "</$tagb>\n"; if ($link ne '') { # encode critical entities $link =~ s/\s/\+/g; # space $link =~ s/'/%27/g; # replace quotation marks my $outer_style = ''; # put certain styles like border and background on the table cell: for my $s (qw/background border/) { $outer_style .= "$s: $out->{$s};" if exists $out->{$s}; } $outer_style =~ s/;\s?\z$//; # remove '; ' at end $outer_style = ' style="'.$outer_style.'"' if $outer_style; $inner_start =~ s/##style##/$outer_style/; # remove from inner_start $html =~ s/##style##/$outer_style/; # or HTML, depending $inner_start .= "<a href='$link'##style##>"; # and put on link $inner_end = '</a>'.$inner_end; } $c = " class='$c'" if $c ne ''; $html .= ">$inner_start$name$inner_end$end_tag"; $html =~ s/##class##/$c/; $html =~ s/##style##/$style/; $self->quoted_comment() . $html; } sub angle { # return the rotation of the node, dependend on the rotate attribute # (and if relative, on the flow) my $self = shift; my $angle = $self->{att}->{rotate} || 0; $angle = 180 if $angle =~ /^(south|down)\z/; $angle = 0 if $angle =~ /^(north|up)\z/; $angle = 270 if $angle eq 'west'; $angle = 90 if $angle eq 'east'; # convert relative angles if ($angle =~ /^([+-]\d+|left|right|back|front|forward)\z/) { my $base_rot = $self->flow(); $angle = 0 if $angle =~ /^(front|forward)\z/; $angle = 180 if $angle eq 'back'; $angle = -90 if $angle eq 'left'; $angle = 90 if $angle eq 'right'; $angle = $base_rot + $angle + 0; # 0 points up, so front points right $angle += 360 while $angle < 0; } $self->_croak("Illegal node angle $angle") if $angle !~ /^\d+\z/; $angle %= 360 if $angle > 359; $angle; } # for determining the absolute parent flow my $p_flow = { 'east' => 90, 'west' => 270, 'north' => 0, 'south' => 180, 'up' => 0, 'down' => 180, 'back' => 270, 'left' => 270, 'right' => 90, 'front' => 90, 'forward' => 90, }; sub _parent_flow_absolute { # make parent flow absolute my ($self, $def) = @_; return '90' if ref($self) eq 'Graph::Easy'; my $flow = $self->parent()->raw_attribute('flow') || $def; return unless defined $flow; # in case of relative flow at parent, convert to absolute (right: east, left: west etc) # so that "graph { flow: left; }" results in a westward flow my $f = $p_flow->{$flow}; $f = $flow unless defined $f; $f; } sub flow { # Calculate the outgoing flow from the incoming flow and the flow at this # node (either from edge(s) or general flow). Returns an absolute flow: # See the online manual about flow for a reference and details. my $self = shift; no warnings 'recursion'; my $cache = $self->{cache}; return $cache->{flow} if exists $cache->{flow}; # detected cycle, so break it return $cache->{flow} = $self->_parent_flow_absolute('90') if exists $self->{_flow}; local $self->{_flow} = undef; # endless loops really ruin our day my $in; my $flow = $self->{att}->{flow}; $flow = $self->_parent_flow_absolute() if !defined $flow || $flow eq 'inherit'; # if flow is absolute, return it early return $cache->{flow} = $flow if defined $flow && $flow =~ /^(0|90|180|270)\z/; return $cache->{flow} = Graph::Easy->_direction_as_number($flow) if defined $flow && $flow =~ /^(south|north|east|west|up|down)\z/; # for relative flows, compute the incoming flow as base flow # check all edges for my $e (ord_values ( $self->{edges} )) { # only count incoming edges next unless $e->{from} != $self && $e->{to} == $self; # if incoming edge has flow, we take this $in = $e->flow(); # take the first match last if defined $in; } if (!defined $in) { # check all predecessors for my $e (ord_values ( $self->{edges} )) { my $pre = $e->{from}; $pre = $e->{to} if $e->{bidirectional}; if ($pre != $self) { $in = $pre->flow(); # take the first match last if defined $in; } } } $in = $self->_parent_flow_absolute('90') unless defined $in; $flow = Graph::Easy->_direction_as_number($in) unless defined $flow; $cache->{flow} = Graph::Easy->_flow_as_direction($in,$flow); } ############################################################################# # multi-celled nodes sub _calc_size { # Calculate the base size in cells from the attributes (before grow()) # Will return a hash that denotes in which direction the node should grow. my $self = shift; # If specified only one of "rows" or "columns", then grow the node # only in the unspecified direction. Default is grow both. my $grow_sides = { cx => 1, cy => 1 }; my $r = $self->{att}->{rows}; my $c = $self->{att}->{columns}; delete $grow_sides->{cy} if defined $r && !defined $c; delete $grow_sides->{cx} if defined $c && !defined $r; $r = $self->attribute('rows') unless defined $r; $c = $self->attribute('columns') unless defined $c; $self->{cy} = abs($r || 1); $self->{cx} = abs($c || 1); $grow_sides; } sub _grow { # Grows the node until it has sufficient cells for all incoming/outgoing # edges. The initial size will be based upon the attributes 'size' (or # 'rows' or 'columns', depending on which is set) my $self = shift; # XXX TODO: grow the node based on its label dimensions # my ($w,$h) = $self->dimensions(); # # my $cx = int(($w+2) / 5) || 1; # my $cy = int(($h) / 3) || 1; # # $self->{cx} = $cx if $cx > $self->{cx}; # $self->{cy} = $cy if $cy > $self->{cy}; # satisfy the edge start/end port constraints: # We calculate a bitmap (vector) for each side, and mark each # used port. Edges that have an unspecified port will just be # counted. # bitmap for each side: my $vec = { north => '', south => '', east => '', west => '' }; # number of edges constrained to one side, but without port number my $cnt = { north => 0, south => 0, east => 0, west => 0 }; # number of edges constrained to one side, with port number my $portnr = { north => 0, south => 0, east => 0, west => 0 }; # max number of ports for each side my $max = { north => 0, south => 0, east => 0, west => 0 }; my @idx = ( [ 'start', 'from' ], [ 'end', 'to' ] ); # number of slots we need to edges without port restrictions my $unspecified = 0; # count of outgoing edges my $outgoing = 0; for my $e (ord_values ( $self->{edges} )) { # count outgoing edges $outgoing++ if $e->{from} == $self; # do always both ends, because self-loops can start AND end at this node: for my $end (0..1) { # if the edge starts/ends here if ($e->{$idx[$end]->[1]} == $self) # from/to { my ($side, $nr) = $e->port($idx[$end]->[0]); # start/end if (defined $side) { if (!defined $nr || $nr eq '') { # no port number specified, so just count $cnt->{$side}++; } else { # mark the bit in the vector # limit to four digits $nr = 9999 if abs($nr) > 9999; # if slot was not used yet, count it $portnr->{$side} ++ if vec($vec->{$side}, $nr, 1) == 0x0; # calculate max number of ports $nr = abs($nr) - 1 if $nr < 0; # 3 => 3, -3 => 2 $nr++; # 3 => 4, -3 => 3 # mark as used vec($vec->{$side}, $nr - 1, 1) = 0x01; $max->{$side} = $nr if $nr > $max->{$side}; } } else { $unspecified ++; } } # end if port is constrained } # end for start/end port } # end for all edges for my $e (ord_values ( $self->{edges} )) { # the loop above will count all self-loops twice when they are # unrestricted. So subtract these again. Restricted self-loops # might start at one port and end at another, and this case is # covered correctly by the code above. $unspecified -- if $e->{to} == $e->{from}; } # Shortcut, if the number of edges is < 4 and we have not restrictions, # then a 1x1 node suffices if ($unspecified < 4 && ($unspecified == keys %{$self->{edges}})) { $self->_calc_size(); return $self; } my $need = {}; my $free = {}; for my $side (qw/north south east west/) { # maximum number of ports we need to reserve, minus edges constrained # to unique ports: free ports on that side $free->{$side} = $max->{$side} - $portnr->{$side}; $need->{$side} = $max->{$side}; if ($free->{$side} < 2 * $cnt->{$side}) { $need->{$side} += 2 * $cnt->{$side} - $free->{$side} - 1; } } # now $need contains for each side the absolute min. number of ports we need # use Data::Dumper; # print STDERR "# port contraints for $self->{name}:\n"; # print STDERR "# count: ", Dumper($cnt), "# max: ", Dumper($max),"\n"; # print STDERR "# ports: ", Dumper($portnr),"\n"; # print STDERR "# need : ", Dumper($need),"\n"; # print STDERR "# free : ", Dumper($free),"\n"; # calculate min. size in X and Y direction my $min_x = $need->{north}; $min_x = $need->{south} if $need->{south} > $min_x; my $min_y = $need->{west}; $min_y = $need->{east} if $need->{east} > $min_y; my $grow_sides = $self->_calc_size(); # increase the size if the minimum required size is not met $self->{cx} = $min_x if $min_x > $self->{cx}; $self->{cy} = $min_y if $min_y > $self->{cy}; my $flow = $self->flow(); # if this is a sink node, grow it more by ignoring free ports on the front side my $front_side = 'east'; $front_side = 'west' if $flow == 270; $front_side = 'south' if $flow == 180; $front_side = 'north' if $flow == 0; # now grow the node based on the general flow first VER, then HOR my $grow = 0; # index into @grow_what my @grow_what = sort keys %$grow_sides; # 'cx', 'cy' or 'cx' or 'cy' if (keys %$grow_sides > 1) { # for left/right flow, swap the growing around @grow_what = ( 'cy', 'cx' ) if $flow == 90 || $flow == 270; } # fake a non-sink node for nodes with an offset/children $outgoing = 1 if ref($self->{origin}) || keys %{$self->{children}} > 0; while ( 3 < 5 ) { # calculate whether we already found a space for all edges my $free_ports = 0; for my $side (qw/north south/) { # if this is a sink node, grow it more by ignoring free ports on the front side next if $outgoing == 0 && $front_side eq $side; $free_ports += 1 + int(($self->{cx} - $cnt->{$side} - $portnr->{$side}) / 2); } for my $side (qw/east west/) { # if this is a sink node, grow it more by ignoring free ports on the front side next if $outgoing == 0 && $front_side eq $side; $free_ports += 1 + int(($self->{cy} - $cnt->{$side} - $portnr->{$side}) / 2); } last if $free_ports >= $unspecified; $self->{ $grow_what[$grow] } += 2; $grow ++; $grow = 0 if $grow >= @grow_what; } $self; } sub is_multicelled { # return true if node consist of more than one cell my $self = shift; $self->_calc_size() unless defined $self->{cx}; $self->{cx} + $self->{cy} <=> 2; # 1 + 1 == 2: no, cx + xy != 2: yes } sub is_anon { # normal nodes are not anon nodes (but "::Anon" are) 0; } ############################################################################# # accessor methods sub _un_escape { # replace \N, \G, \T, \H and \E (depending on type) # if $label is false, also replace \L with the label my ($self, $txt, $do_label) = @_; # for edges: if (exists $self->{edge}) { my $e = $self->{edge}; $txt =~ s/\\E/$e->{from}->{name}\->$e->{to}->{name}/g; $txt =~ s/\\T/$e->{from}->{name}/g; $txt =~ s/\\H/$e->{to}->{name}/g; # \N for edges is the label of the edge if ($txt =~ /\\N/) { my $l = $self->label(); $txt =~ s/\\N/$l/g; } } else { # \N for nodes $txt =~ s/\\N/$self->{name}/g; } # \L with the label if ($txt =~ /\\L/ && $do_label) { my $l = $self->label(); $txt =~ s/\\L/$l/g; } # \G for edges and nodes if ($txt =~ /\\G/) { my $g = ''; # the graph itself $g = $self->attribute('title') unless ref($self->{graph}); # any nodes/edges/groups in it $g = $self->{graph}->label() if ref($self->{graph}); $txt =~ s/\\G/$g/g; } $txt; } sub title { # Returns a title of the node (or '', if none was set), which can be # used for mouse-over titles my $self = shift; my $title = $self->attribute('title'); if ($title eq '') { my $autotitle = $self->attribute('autotitle'); if (defined $autotitle) { $title = ''; # default is none if ($autotitle eq 'name') # use name { $title = $self->{name}; # edges do not have a name and fall back on their label $title = $self->{att}->{label} unless defined $title; } if ($autotitle eq 'label') { $title = $self->{name}; # fallback to name # defined to avoid overriding "name" with the non-existant label attribute # do not use label() here, but the "raw" label of the edge: my $label = $self->label(); $title = $label if defined $label; } $title = $self->link() if $autotitle eq 'link'; } $title = '' unless defined $title; } $title = $self->_un_escape($title, 1) if !$_[0] && $title =~ /\\[EGHNTL]/; $title; } sub background { # get the background for this group/edge cell, honouring group membership. my $self = shift; $self->color_attribute('background'); } sub label { my $self = shift; # shortcut to speed it up a bit: my $label = $self->{att}->{label}; $label = $self->attribute('label') unless defined $label; # for autosplit nodes, use their auto-label first (unless already got # a label from the class): $label = $self->{autosplit_label} unless defined $label; $label = $self->{name} unless defined $label; return '' unless defined $label; if ($label ne '') { my $len = $self->attribute('autolabel'); if ($len ne '') { # allow the old format (pre v0.49), too: "name,12" => 12 $len =~ s/^name\s*,\s*//; # restrict to sane values $len = abs($len || 0); $len = 99999 if $len > 99999; if (length($label) > $len) { my $g = $self->{graph} || {}; if ((($g->{_ascii_style}) || 0) == 0) { # ASCII output $len = int($len / 2) - 3; $len = 0 if $len < 0; $label = substr($label, 0, $len) . ' ... ' . substr($label, -$len, $len); } else { $len = int($len / 2) - 2; $len = 0 if $len < 0; $label = substr($label, 0, $len) . ' … ' . substr($label, -$len, $len); } } } } $label = $self->_un_escape($label) if !$_[0] && $label =~ /\\[EGHNT]/; $label; } sub name { my $self = shift; $self->{name}; } sub x { my $self = shift; $self->{x}; } sub y { my $self = shift; $self->{y}; } sub width { my $self = shift; $self->{w}; } sub height { my $self = shift; $self->{h}; } sub origin { # Returns node that this node is relative to or undef, if not. my $self = shift; $self->{origin}; } sub pos { my $self = shift; ($self->{x} || 0, $self->{y} || 0); } sub offset { my $self = shift; ($self->{dx} || 0, $self->{dy} || 0); } sub columns { my $self = shift; $self->_calc_size() unless defined $self->{cx}; $self->{cx}; } sub rows { my $self = shift; $self->_calc_size() unless defined $self->{cy}; $self->{cy}; } sub size { my $self = shift; $self->_calc_size() unless defined $self->{cx}; ($self->{cx}, $self->{cy}); } sub shape { my $self = shift; my $shape; $shape = $self->{att}->{shape} if exists $self->{att}->{shape}; $shape = $self->attribute('shape') unless defined $shape; $shape; } sub dimensions { # Returns the minimum dimensions of the node/cell derived from the # label or name, in characters. my $self = shift; my $align = $self->attribute('align'); my ($lines,$aligns) = $self->_aligned_label($align); my $w = 0; my $h = scalar @$lines; foreach my $line (@$lines) { $w = length($line) if length($line) > $w; } ($w,$h); } ############################################################################# # edges and connections sub edges_to { # Return all the edge objects that start at this vertex and go to $other. my ($self, $other) = @_; # no graph, no dice return unless ref $self->{graph}; my @edges; for my $edge (ord_values ( $self->{edges} )) { push @edges, $edge if $edge->{from} == $self && $edge->{to} == $other; } @edges; } sub edges_at_port { # return all edges that share the same given port my ($self, $attr, $side, $port) = @_; # Must be "start" or "end" return () unless $attr =~ /^(start|end)\z/; $self->_croak('side not defined') unless defined $side; $self->_croak('port not defined') unless defined $port; my @edges; for my $e (ord_values ( $self->{edges} )) { # skip edges ending here if we look at start next if $e->{to} eq $self && $attr eq 'start'; # skip edges starting here if we look at end next if $e->{from} eq $self && $attr eq 'end'; my ($s_p,@ss_p) = $e->port($attr); next unless defined $s_p; # same side and same port number? push @edges, $e if $s_p eq $side && @ss_p == 1 && $ss_p[0] eq $port; } @edges; } sub shared_edges { # return all edges that share one port with another edge my ($self) = @_; my @edges; for my $e (ord_values ( $self->{edges} )) { my ($s_p,@ss_p) = $e->port('start'); push @edges, $e if defined $s_p; my ($e_p,@ee_p) = $e->port('end'); push @edges, $e if defined $e_p; } @edges; } sub nodes_sharing_start { # return all nodes that share an edge start with an # edge from that node my ($self, $side, @port) = @_; my @edges = $self->edges_at_port('start',$side,@port); my $nodes; for my $e (@edges) { # ignore self-loops my $to = $e->{to}; next if $to == $self; # remove duplicates $nodes->{ $to->{name} } = $to; } return (ord_values $nodes); } sub nodes_sharing_end { # return all nodes that share an edge end with an # edge from that node my ($self, $side, @port) = @_; my @edges = $self->edges_at_port('end',$side,@port); my $nodes; for my $e (@edges) { # ignore self-loops my $from = $e->{from}; next if $from == $self; # remove duplicates $nodes->{ $from->{name} } = $from; } return (ord_values $nodes); } sub incoming { # return all edges that end at this node my $self = shift; # no graph, no dice return unless ref $self->{graph}; if (!wantarray) { my $count = 0; for my $edge (ord_values ( $self->{edges} )) { $count++ if $edge->{to} == $self; } return $count; } my @edges; for my $edge (ord_values ( $self->{edges} )) { push @edges, $edge if $edge->{to} == $self; } @edges; } sub outgoing { # return all edges that start at this node my $self = shift; # no graph, no dice return unless ref $self->{graph}; if (!wantarray) { my $count = 0; for my $edge (ord_values ( $self->{edges} )) { $count++ if $edge->{from} == $self; } return $count; } my @edges; for my $edge (ord_values ( $self->{edges} )) { push @edges, $edge if $edge->{from} == $self; } @edges; } sub connections { # return number of connections (incoming+outgoing) my $self = shift; return 0 unless defined $self->{graph}; # We need to count the connections, because "[A]->[A]" creates # two connections on "A", but only one edge! my $con = 0; for my $edge (ord_values ( $self->{edges} )) { $con ++ if $edge->{to} == $self; $con ++ if $edge->{from} == $self; } $con; } sub edges { # return all the edges my $self = shift; # no graph, no dice return unless ref $self->{graph}; return (wantarray ? ord_values ( $self->{edges} ) : scalar keys %{$self->{edges}} ); } sub sorted_successors { # return successors of the node sorted by their chain value # (e.g. successors with more successors first) my $self = shift; my @suc = sort { scalar $b->successors() <=> scalar $a->successors() || scalar $a->{name} cmp scalar $b->{name} } $self->successors(); @suc; } sub successors { # return all nodes (as objects) we are linked to my $self = shift; return () unless defined $self->{graph}; my %suc; for my $edge (ord_values ( $self->{edges} )) { next unless $edge->{from} == $self; $suc{$edge->{to}->{id}} = $edge->{to}; # weed out doubles } return ord_values( \%suc ); } sub predecessors { # return all nodes (as objects) that link to us my $self = shift; return () unless defined $self->{graph}; my %pre; for my $edge (ord_values ( $self->{edges} )) { next unless $edge->{to} == $self; $pre{$edge->{from}->{id}} = $edge->{from}; # weed out doubles } return ord_values(\%pre); } sub has_predecessors { # return true if node has incoming edges (even from itself) my $self = shift; return undef unless defined $self->{graph}; for my $edge (ord_values ( $self->{edges} )) { return 1 if $edge->{to} == $self; # found one } 0; # found none } sub has_as_predecessor { # return true if other is a predecessor of node my ($self,$other) = @_; return () unless defined $self->{graph}; for my $edge (ord_values ( $self->{edges} )) { return 1 if $edge->{to} == $self && $edge->{from} == $other; # found one } 0; # found none } sub has_as_successor { # return true if other is a successor of node my ($self,$other) = @_; return () unless defined $self->{graph}; for my $edge (ord_values ( $self->{edges} )) { return 1 if $edge->{from} == $self && $edge->{to} == $other; # found one } 0; # found none } ############################################################################# # relatively placed nodes sub relative_to { # Sets the new origin if passed a Graph::Easy::Node object. my ($self,$parent,$dx,$dy) = @_; if (!ref($parent) || !$parent->isa('Graph::Easy::Node')) { require Carp; Carp::confess("Can't set origin to non-node object $parent"); } my $grandpa = $parent->find_grandparent(); if ($grandpa == $self) { require Carp; Carp::confess( "Detected loop in origin-chain:" ." tried to set origin of '$self->{name}' to my own grandchild $parent->{name}"); } # unregister us with our old parent delete $self->{origin}->{children}->{$self->{id}} if defined $self->{origin}; $self->{origin} = $parent; $self->{dx} = $dx if defined $dx; $self->{dy} = $dy if defined $dy; $self->{dx} = 0 unless defined $self->{dx}; $self->{dy} = 0 unless defined $self->{dy}; # register us as a new child $parent->{children}->{$self->{id}} = $self; $self; } sub find_grandparent { # For a node that has no origin (is not relative to another), returns # $self. For all others, follows the chain of origin back until we # hit a node without a parent. This code assumes there are no loops, # which origin() prevents from happening. my $cur = shift; if (wantarray) { my $ox = 0; my $oy = 0; while (defined($cur->{origin})) { $ox -= $cur->{dx}; $oy -= $cur->{dy}; $cur = $cur->{origin}; } return ($cur,$ox,$oy); } while (defined($cur->{origin})) { $cur = $cur->{origin}; } $cur; } ############################################################################# # attributes sub del_attribute { my ($self, $name) = @_; # font-size => fontsize $name = $att_aliases->{$name} if exists $att_aliases->{$name}; $self->{cache} = {}; my $a = $self->{att}; delete $a->{$name}; if ($name eq 'size') { delete $a->{rows}; delete $a->{columns}; } if ($name eq 'border') { delete $a->{borderstyle}; delete $a->{borderwidth}; delete $a->{bordercolor}; } $self; } sub set_attribute { my ($self, $name, $v, $class) = @_; $self->{cache} = {}; $name = 'undef' unless defined $name; $v = 'undef' unless defined $v; # font-size => fontsize $name = $att_aliases->{$name} if exists $att_aliases->{$name}; # edge.cities => edge $class = $self->main_class() unless defined $class; # remove quotation marks, but not for titles, labels etc my $val = Graph::Easy->unquote_attribute($class,$name,$v); my $g = $self->{graph}; $g->{score} = undef if $g; # invalidate layout to force a new layout my $strict = 0; $strict = $g->{strict} if $g; if ($strict) { my ($rc, $newname, $v) = $g->validate_attribute($name,$val,$class); return if defined $rc; # error? $val = $v; } if ($name eq 'class') { $self->sub_class($val); return $val; } elsif ($name eq 'group') { $self->add_to_group($val); return $val; } elsif ($name eq 'border') { my $c = $self->{att}; ($c->{borderstyle}, $c->{borderwidth}, $c->{bordercolor}) = $g->split_border_attributes( $val ); return $val; } if ($name =~ /^(columns|rows|size)\z/) { if ($name eq 'size') { $val =~ /^(\d+)\s*,\s*(\d+)\z/; my ($cx, $cy) = (abs(int($1)),abs(int($2))); ($self->{att}->{columns}, $self->{att}->{rows}) = ($cx, $cy); } else { $self->{att}->{$name} = abs(int($val)); } return $self; } if ($name =~ /^(origin|offset)\z/) { # Only the first autosplit node get the offset/origin return $self if exists $self->{autosplit} && !defined $self->{autosplit}; if ($name eq 'origin') { # if it doesn't exist, add it my $org = $self->{graph}->add_node($val); $self->relative_to($org); # set the attributes, too, so get_attribute('origin') works, too: $self->{att}->{origin} = $org->{name}; } else { # offset # if it doesn't exist, add it my ($x,$y) = split/\s*,\s*/, $val; $x = int($x); $y = int($y); if ($x == 0 && $y == 0) { $g->error("Error in attribute: 'offset' is 0,0 in node $self->{name} with class '$class'"); return; } $self->{dx} = $x; $self->{dy} = $y; # set the attributes, too, so get_attribute('origin') works, too: $self->{att}->{offset} = "$self->{dx},$self->{dy}"; } return $self; } $self->{att}->{$name} = $val; } sub set_attributes { my ($self, $atr, $index) = @_; foreach my $n (sort keys %$atr) { my $val = $atr->{$n}; $val = $val->[$index] if ref($val) eq 'ARRAY' && defined $index; next if !defined $val || $val eq ''; $n eq 'class' ? $self->sub_class($val) : $self->set_attribute($n, $val); } $self; } BEGIN { # some handy aliases *text_styles_as_css = \&Graph::Easy::text_styles_as_css; *text_styles = \&Graph::Easy::text_styles; *_font_size_in_pixels = \&Graph::Easy::_font_size_in_pixels; *get_color_attribute = \&color_attribute; *link = \&Graph::Easy::link; *border_attribute = \&Graph::Easy::border_attribute; *get_attributes = \&Graph::Easy::get_attributes; *get_attribute = \&Graph::Easy::attribute; *raw_attribute = \&Graph::Easy::raw_attribute; *get_raw_attribute = \&Graph::Easy::raw_attribute; *raw_color_attribute = \&Graph::Easy::raw_color_attribute; *raw_attributes = \&Graph::Easy::raw_attributes; *raw_attributes = \&Graph::Easy::raw_attributes; *attribute = \&Graph::Easy::attribute; *color_attribute = \&Graph::Easy::color_attribute; *default_attribute = \&Graph::Easy::default_attribute; $att_aliases = Graph::Easy::_att_aliases(); } ############################################################################# sub group { # return the group this object belongs to my $self = shift; $self->{group}; } sub add_to_group { my ($self,$group) = @_; my $graph = $self->{graph}; # shortcut # delete from old group if nec. $self->{group}->del_member($self) if ref $self->{group}; # if passed a group name, create or find group object $group = $graph->add_group($group) if (!ref($group) && $graph); # To make attribute('group') work: $self->{att}->{group} = $group->{name}; $group->add_member($self); $self; } sub parent { # return parent object, either the group the node belongs to, or the graph my $self = shift; my $p = $self->{graph}; $p = $self->{group} if ref($self->{group}); $p; } sub _update_boundaries { my ($self, $parent) = @_; # XXX TODO: use current layout parent for recursive layouter: $parent = $self->{graph}; # cache max boundaries for A* algorithmn: my $x = $self->{x}; my $y = $self->{y}; # create the cache if it doesn't already exist $parent->{cache} = {} unless ref($parent->{cache}); my $cache = $parent->{cache}; $cache->{min_x} = $x if !defined $cache->{min_x} || $x < $cache->{min_x}; $cache->{min_y} = $y if !defined $cache->{min_y} || $y < $cache->{min_y}; $x = $x + ($self->{cx}||1) - 1; $y = $y + ($self->{cy}||1) - 1; $cache->{max_x} = $x if !defined $cache->{max_x} || $x > $cache->{max_x}; $cache->{max_y} = $y if !defined $cache->{max_y} || $y > $cache->{max_y}; if (($parent->{debug}||0) > 1) { my $n = $self->{name}; $n = $self unless defined $n; print STDERR "Update boundaries for $n (parent $parent) at $x, $y\n"; print STDERR "Boundaries are now: " . "$cache->{min_x},$cache->{min_y} => $cache->{max_x},$cache->{max_y}\n"; } $self; } 1; __END__ =head1 NAME Graph::Easy::Node - Represents a node in a Graph::Easy graph =head1 SYNOPSIS use Graph::Easy::Node; my $bonn = Graph::Easy::Node->new('Bonn'); $bonn->set_attribute('border', 'solid 1px black'); my $berlin = Graph::Easy::Node->new( name => 'Berlin' ); =head1 DESCRIPTION A C<Graph::Easy::Node> represents a node in a simple graph. Each node has contents (a text, an image or another graph), and dimension plus an origin. The origin is typically determined by a graph layouter module like L<Graph::Easy>. =head1 METHODS Apart from the methods of the base class L<Graph::Easy::Base>, a C<Graph::Easy::Node> has the following methods: =head2 new() my $node = Graph::Easy::Node->new( name => 'node name' ); my $node = Graph::Easy::Node->new( 'node name' ); Creates a new node. If you want to add the node to a Graph::Easy object, then please use the following to create the node object: my $node = $graph->add_node('Node name'); You can then use C<< $node->set_attribute(); >> or C<< $node->set_attributes(); >> to set the new Node's attributes. =head2 as_ascii() my $ascii = $node->as_ascii(); Return the node as a little box drawn in ASCII art as a string. =head2 as_txt() my $txt = $node->as_txt(); Return the node in simple txt format, including attributes. =head2 as_svg() my $svg = $node->as_svg(); Returns the node as Scalable Vector Graphic. The actual code for that routine is defined L<Graph::Easy::As_svg.pm>. =head2 as_graphviz() B<For internal use> mostly - use at your own risk. my $txt = $node->as_graphviz(); Returns the node as graphviz compatible text which can be fed to dot etc to create images. One needs to load L<Graph::Easy::As_graphviz> first before this method can be called. =head2 as_graphviz_txt() B<For internal use> mostly - use at your own risk. my $txt = $node->as_graphviz_txt(); Return only the node itself (without attributes) as a graphviz representation. One needs to load L<Graph::Easy::As_graphviz> first before this method can be called. =head2 as_pure_txt() my $txt = $node->as_pure_txt(); Return the node in simple txt format, without the attributes. =head2 text_styles_as_css() my $styles = $graph->text_styles_as_css(); # or $edge->...() etc. Return the text styles as a chunk of CSS styling that can be embedded into a C< style="" > parameter. =head2 as_html() my $html = $node->as_html(); Return the node as HTML code. =head2 attribute(), get_attribute() $node->attribute('border-style'); Returns the respective attribute of the node or undef if it was not set. If there is a default attribute for all nodes of the specific class the node is in, then this will be returned. =head2 get_attributes() my $att = $object->get_attributes(); Return all effective attributes on this object (graph/node/group/edge) as an anonymous hash ref. This respects inheritance and default values. Note that this does not include custom attributes. See also L<get_custom_attributes> and L<raw_attributes()>. =head2 get_custom_attributes() my $att = $object->get_custom_attributes(); Return all the custom attributes on this object (graph/node/group/edge) as an anonymous hash ref. =head2 custom_attributes() my $att = $object->custom_attributes(); C<< custom_attributes() >> is an alias for L<< get_custom_attributes >>. =head2 raw_attributes() my $att = $object->get_attributes(); Return all set attributes on this object (graph/node/group/edge) as an anonymous hash ref. This respects inheritance, but does not include default values for unset attributes. See also L<get_attributes()>. =head2 default_attribute() my $def = $graph->default_attribute($class, 'fill'); Returns the default value for the given attribute B<in the class> of the object. The default attribute is the value that will be used if the attribute on the object itself, as well as the attribute on the class is unset. To find out what attribute is on the class, use the three-arg form of L<attribute> on the graph: my $g = Graph::Easy->new(); my $node = $g->add_node('Berlin'); print $node->attribute('fill'), "\n"; # print "white" print $node->default_attribute('fill'), "\n"; # print "white" print $g->attribute('node','fill'), "\n"; # print "white" $g->set_attribute('node','fill','red'); # class is "red" $node->set_attribute('fill','green'); # this object is "green" print $node->attribute('fill'), "\n"; # print "green" print $node->default_attribute('fill'), "\n"; # print "white" print $g->attribute('node','fill'), "\n"; # print "red" See also L<raw_attribute()>. =head2 attributes_as_txt my $txt = $node->attributes_as_txt(); Return the attributes of this node as text description. This is used by the C<< $graph->as_txt() >> code and there should be no reason to use this function on your own. =head2 set_attribute() $node->set_attribute('border-style', 'none'); Sets the specified attribute of this (and only this!) node to the specified value. =head2 del_attribute() $node->del_attribute('border-style'); Deletes the specified attribute of this (and only this!) node. =head2 set_attributes() $node->set_attributes( $hash ); Sets all attributes specified in C<$hash> as key => value pairs in this (and only this!) node. =head2 border_attribute() my $border = $node->border_attribute(); Assembles the C<border-width>, C<border-color> and C<border-style> attributes into a string like "solid 1px red". =head2 color_attribute() # returns f.i. #ff0000 my $color = $node->get_color_attribute( 'fill' ); Just like get_attribute(), but only for colors, and returns them as hex, using the current colorscheme. =head2 get_color_attribute() Is an alias for L<color_attribute()>. =head2 raw_attribute(), get_raw_attribute() my $value = $object->raw_attribute( $name ); Return the value of attribute C<$name> from the object it this method is called on (graph, node, edge, group etc.). If the attribute is not set on the object itself, returns undef. This method respects inheritance, so an attribute value of 'inherit' on an object will make the method return the inherited value: my $g = Graph::Easy->new(); my $n = $g->add_node('A'); $g->set_attribute('color','red'); print $n->raw_attribute('color'); # undef $n->set_attribute('color','inherit'); print $n->raw_attribute('color'); # 'red' See also L<attribute()>. =head2 raw_color_attribute() # returns f.i. #ff0000 my $color = $graph->raw_color_attribute('color' ); Just like L<raw_attribute()>, but only for colors, and returns them as hex, using the current colorscheme. If the attribute is not set on the object, returns C<undef>. =head2 text_styles() my $styles = $node->text_styles(); if ($styles->{'italic'}) { print 'is italic\n'; } Return a hash with the given text-style properties, aka 'underline', 'bold' etc. =head2 find_grandparent() my $grandpa = $node->find_grandparent(); For a node that has no origin (is not relative to another), returns C<$node>. For all others, follows the chain of origin back until a node without a parent is found and returns this node. This code assumes there are no loops, which C<origin()> prevents from happening. =head2 name() my $name = $node->name(); Return the name of the node. In a graph, each node has a unique name, which, unless a node label is set, will be displayed when rendering the graph. =head2 label() my $label = $node->label(); my $label = $node->label(1); # raw Return the label of the node. If no label was set, returns the C<name> of the node. If the optional parameter is true, then the label will returned 'raw', that is any potential escape of the form C<\N>, C<\E>, C<\G>, C<\T> or C<\H> will not be left alone and not be replaced. =head2 background() my $bg = $node->background(); Returns the background color. This method honours group membership and inheritance. =head2 quoted_comment() my $cmt = $node->comment(); Comment of this object, quoted suitable as to be embedded into HTML/SVG. Returns the empty string if this object doesn't have a comment set. =head2 title() my $title = $node->title(); my $title = $node->title(1); # raw Returns a potential title that can be used for mouse-over effects. If no title was set (or autogenerated), will return an empty string. If the optional parameter is true, then the title will returned 'raw', that is any potential escape of the form C<\N>, C<\E>, C<\G>, C<\T> or C<\H> will be left alone and not be replaced. =head2 link() my $link = $node->link(); my $link = $node->link(1); # raw Returns the URL, build from the C<linkbase> and C<link> (or C<autolink>) attributes. If the node has no link associated with it, return an empty string. If the optional parameter is true, then the link will returned 'raw', that is any potential escape of the form C<\N>, C<\E>, C<\G>, C<\T> or C<\H> will not be left alone and not be replaced. =head2 dimensions() my ($w,$h) = $node->dimensions(); Returns the dimensions of the node/cell derived from the label (or name) in characters. Assumes the label/name has literal '\n' replaced by "\n". =head2 size() my ($cx,$cy) = $node->size(); Returns the node size in cells. =head2 contents() my $contents = $node->contents(); For nested nodes, returns the contents of the node. =head2 width() my $width = $node->width(); Returns the width of the node. This is a unitless number. =head2 height() my $height = $node->height(); Returns the height of the node. This is a unitless number. =head2 columns() my $cols = $node->columns(); Returns the number of columns (in cells) that this node occupies. =head2 rows() my $cols = $node->rows(); Returns the number of rows (in cells) that this node occupies. =head2 is_multicelled() if ($node->is_multicelled()) { ... } Returns true if the node consists of more than one cell. See als L<rows()> and L<cols()>. =head2 is_anon() if ($node->is_anon()) { ... } Returns true if the node is an anonymous node. False for C<Graph::Easy::Node> objects, and true for C<Graph::Easy::Node::Anon>. =head2 pos() my ($x,$y) = $node->pos(); Returns the position of the node. Initially, this is undef, and will be set from L<Graph::Easy::layout()>. Only valid during the layout phase. =head2 offset() my ($dx,$dy) = $node->offset(); Returns the position of the node relativ to the origin. Returns C<(0,0)> if the origin node was not sset. =head2 x() my $x = $node->x(); Returns the X position of the node. Initially, this is undef, and will be set from L<Graph::Easy::layout()>. Only valid during the layout phase. =head2 y() my $y = $node->y(); Returns the Y position of the node. Initially, this is undef, and will be set from L<Graph::Easy::layout()>. Only valid during the layout phase. =head2 id() my $id = $node->id(); Returns the node's unique, internal ID number. =head2 connections() my $cnt = $node->connections(); Returns the count of incoming and outgoing connections of this node. Self-loops count as two connections, so in the following example, node C<N> has B<four> connections, but only B<three> edges: +--+ v | +---+ +------+ +---+ | 1 | --> | N | --> | 2 | +---+ +------+ +---+ See also L<edges()>. =head2 edges() my $edges = $node->edges(); Returns a list of all the edges (as L<Graph::Easy::Edge> objects) at this node, in no particular order. =head2 predecessors() my @pre = $node->predecessors(); Returns all nodes (as objects) that link to us. =head2 has_predecessors() if ($node->has_predecessors()) { ... } Returns true if the node has one or more predecessors. Will return true for nodes with selfloops. =head2 successors() my @suc = $node->successors(); Returns all nodes (as objects) that we are linking to. =head2 sorted_successors() my @suc = $node->sorted_successors(); Return successors of the node sorted by their chain value (e.g. successors with more successors first). =head2 has_as_successor() if ($node->has_as_successor($other)) { ... } Returns true if C<$other> ( a node or group) is a successor of node, that is if there is an edge leading from node to C<$other>. =head2 has_as_predecessor() if ($node->has_as_predecessor($other)) { ... } Returns true if the node has C<$other> (a group or node) as predecessor, that is if there is an edge leading from C<$other> to node. =head2 edges_to() my @edges = $node->edges_to($other_node); Returns all the edges (as objects) that start at C<< $node >> and go to C<< $other_node >>. =head2 shared_edges() my @edges = $node->shared_edges(); Return a list of all edges starting/ending at this node, that share a port with another edge. =head2 nodes_sharing_start() my @nodes = $node->nodes_sharing_start($side, $port); Return a list of unique nodes that share a start point with an edge from this node, on the specified side (absolute) and port number. =head2 nodes_sharing_end() my @nodes = $node->nodes_sharing_end($side, $port); Return a list of unique nodes that share an end point with an edge from this node, on the specified side (absolute) and port number. =head2 edges_at_port() my @edges = $node->edges_to('start', 'south', '0'); Returns all the edge objects that share the same C<start> or C<end> port at the specified side and port number. The side must be one of C<south>, C<north>, C<west> or C<east>. The port number must be positive. =head2 incoming() my @edges = $node->incoming(); Return all edges that end at this node. =head2 outgoing() my @edges = $node->outgoing(); Return all edges that start at this node. =head2 add_to_group() $node->add_to_group( $group ); Put the node into this group. =head2 group() my $group = $node->group(); Return the group this node belongs to, or undef. =head2 parent() my $parent = $node->parent(); Returns the parent object of the node, which is either the group the node belongs to, or the graph. =head2 origin() my $origin_node = $node->origin(); Returns the node this node is relativ to, or undef otherwise. =head2 relative_to() $node->relative_to($parent, $dx, $dy); Sets itself relativ to C<$parent> with the offset C<$dx,$dy>. =head2 shape() my $shape = $node->shape(); Returns the shape of the node as string, defaulting to 'rect'. =head2 angle() my $angle = $self->rotation(); Return the node's rotation, based on the C<rotate> attribute, and in case this is relative, on the node's flow. =head2 flow() my $flow = $node->flow(); Returns the outgoing flow for this node as absolute direction in degrees. The value is computed from the incoming flow (or the general flow as default) and the flow attribute of this node. =head2 _extra_params() my $extra_params = $node->_extra_params(); The return value of that method is added as extra params to the HTML tag for a node when as_html() is called. Returns the empty string by default, and can be overridden in subclasses. See also L<use_class()>. Overridden method should return a text with a leading space, or the empty string. Example: package Graph::Easy::MyNode; use base qw/Graph::Easy::Node/; sub _extra_params { my $self = shift; ' ' . 'onmouseover="alert(\'' . $self->name() . '\');"'; } 1; =head1 EXPORT None by default. =head1 SEE ALSO L<Graph::Easy>. =head1 AUTHOR Copyright (C) 2004 - 2007 by Tels L<http://bloodgate.com>. See the LICENSE file for more details. =cut ������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/lib/Graph/Easy/Layout.pm������������������������������������������������������������0000644�0000764�0000764�00000072700�12150074077�017633� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������############################################################################# # Layout directed graphs on a flat plane. Part of Graph::Easy. # # (c) by Tels 2004-2008. ############################################################################# package Graph::Easy::Layout; $VERSION = '0.29'; ############################################################################# ############################################################################# package Graph::Easy; use strict; require Graph::Easy::Node::Cell; use Graph::Easy::Edge::Cell qw/ EDGE_HOR EDGE_VER EDGE_CROSS EDGE_TYPE_MASK EDGE_MISC_MASK EDGE_NO_M_MASK EDGE_SHORT_CELL /; use constant { ACTION_NODE => 0, # place node somewhere ACTION_TRACE => 1, # trace path from src to dest ACTION_CHAIN => 2, # place node in chain (with parent) ACTION_EDGES => 3, # trace all edges (shortes connect. first) ACTION_SPLICE => 4, # splice in the group fillers }; require Graph::Easy::Layout::Chain; # chain management use Graph::Easy::Layout::Scout; # pathfinding use Graph::Easy::Layout::Repair; # group cells and splicing/repair use Graph::Easy::Layout::Path; # path management use Graph::Easy::Util qw(ord_values); ############################################################################# sub _assign_ranks { # Assign a rank to each node/group. # Afterwards, every node has a rank, these range from 1..infinite for # user supplied ranks, and -1..-infinite for automatically found ranks. # This lets us later distinguish between autoranks and userranks, while # still being able to sort nodes based on their (absolute) rank. my $self = shift; # a Heap to keep the todo-nodes (aka rank auto or explicit) my $todo = Graph::Easy::Heap->new(); # sort entries based on absolute value $todo->sort_sub( sub ($$) { abs($_[0]) <=> abs($_[1]) } ); # a list of all other nodes my @also; # XXX TODO: # gather elements todo: # graph: contained groups, plus non-grouped nodes # groups: contained groups, contained nodes # sort nodes on their ID to get some basic order my @N = $self->sorted_nodes('id'); push @N, $self->groups(); my $root = $self->root_node(); $todo->add([$root->{rank} = -1,$root]) if ref $root; # Gather all nodes that have outgoing connections, but no incoming: for my $n (@N) { # we already handled the root node above next if $root && $n == $root; # if no rank set, use 0 as default my $rank_att = $n->raw_attribute('rank'); $rank_att = undef if defined $rank_att && $rank_att eq 'auto'; # XXX TODO: this should not happen, the parser should assign an # automatic rank ID $rank_att = 0 if defined $rank_att && $rank_att eq 'same'; # user defined ranks range from 1..inf $rank_att++ if defined $rank_att; # assign undef or 0, 1 etc $n->{rank} = $rank_att; # user defined ranks are "1..inf", while auto ranks are -1..-inf $n->{rank} = -1 if !defined $n->{rank} && $n->predecessors() == 0; # push "rank: X;" nodes, or nodes without predecessors $todo->add([$n->{rank},$n]) if defined $n->{rank}; push @also, $n unless defined $n->{rank}; } # print STDERR "# Ranking:\n"; # for my $n (@{$todo->{_heap}}) # { # print STDERR "# $n->[1]->{name} $n->[0] $n->[1]->{rank}:\n"; # } # print STDERR "# Leftovers in \@also:\n"; # for my $n (@also) # { # print STDERR "# $n->{name}:\n"; # } # The above step will create a list of todo nodes that start a chain, but # it will miss circular chains like CDEC (e.g. only A appears in todo): # A -> B; C -> D -> E -> C; # We fix this as last step while ((@also != 0) || $todo->elements() != 0) { # while we still have nodes to follow while (my $elem = $todo->extract_top()) { my ($rank,$n) = @$elem; my $l = $n->{rank}; # If the rank comes from a user-supplied rank, make the next node # have an automatic rank (e.g. 4 => -4) $l = -$l if $l > 0; # -4 > -5 $l--; for my $o ($n->successors()) { if (!defined $o->{rank}) { # print STDERR "# set rank $l for $o->{name}\n"; $o->{rank} = $l; $todo->add([$l,$o]); } } } last unless @also; while (@also) { my $n = shift @also; # already done? so skip it next if defined $n->{rank}; $n->{rank} = -1; $todo->add([-1, $n]); # leave the others for later last; } } # while still something todo # print STDERR "# Final ranking:\n"; # for my $n (@N) # { # print STDERR "# $n->{name} $n->{rank}:\n"; # } $self; } sub _follow_chain { # follow the chain from the node my ($node) = @_; my $self = $node->{graph}; no warnings 'recursion'; my $indent = ' ' x (($node->{_chain}->{id} || 0) + 1); print STDERR "#$indent Tracking chain from $node->{name}\n" if $self->{debug}; # create a new chain and point it to the start node my $chain = Graph::Easy::Layout::Chain->new( start => $node, graph => $self ); $self->{chains}->{ $chain->{id} } = $chain; my $first_node = $node; my $done = 1; # how many nodes did we process? NODE: while (3 < 5) { # Count "unique" successsors, ignoring selfloops, multiedges and nodes # in the same chain. my $c = $node->{_chain}; local $node->{_c} = 1; # stop back-ward loops my %suc; for my $e (ord_values ( $node->{edges} )) { my $to = $e->{to}; # ignore self-loops next if $e->{from} == $e->{to}; # XXX TODO # skip links from/to groups next if $e->{to}->isa('Graph::Easy::Group') || $e->{from}->isa('Graph::Easy::Group'); # print STDERR "# bidi $e->{from}->{name} to $e->{to}->{name}\n" if $e->{bidirectional} && $to == $node; # if it is bidirectional, and points the "wrong" way, turn it around $to = $e->{from} if $e->{bidirectional} && $to == $node; # edge leads to this node instead from it? next if $to == $node; # print STDERR "# edge_flow for edge $e", $e->edge_flow() || 'undef' ,"\n"; # print STDERR "# flow for edge $e", $e->flow() ,"\n"; # If any of the leading out edges has a flow, stop the chain here # This prevents a chain on an edge w/o a flow to be longer and thus # come first instead of a flow-edge. But don't stop if there is only # one edge: if (defined $e->edge_flow()) { %suc = ( $to->{name} => $to ); # empy any possible chain info last; } next if exists $to->{_c}; # backloop into current branch? next if defined $to->{_chain} && # ignore if it points to the same $to->{_chain} == $c; # chain (backloop) # if the next node's grandparent is the same as ours, it depends on us next if $to->find_grandparent() == $node->find_grandparent(); # ignore multi-edges by dropping $suc{$to->{name}} = $to; # duplicates } last if keys %suc == 0; # the chain stopped here if (scalar keys %suc == 1) # have only one unique successor? { my ($key) = keys(%suc); my $s = $suc{ $key }; if (!defined $s->{_chain}) # chain already done? { $c->add_node( $s ); $node = $s; # next node print STDERR "#$indent Skipping ahead to $node->{name}\n" if $self->{debug}; $done++; # one more next NODE; # skip recursion } } # Select the longest chain from the list of successors # and join it with the current one: my $max = -1; my $next; # successor my $next_chain = undef; print STDERR "#$indent $node->{name} successors: \n" if $self->{debug}; my @rc; # for all successors #for my $s (sort { $a->{name} cmp $b->{name} || $a->{id} <=> $b->{id} } values %suc) for my $s (ord_values ( \%suc)) { print STDERR "# suc $s->{name} chain ", $s->{_chain} || 'undef',"\n" if $self->{debug}; $done += _follow_chain($s) # track chain if !defined $s->{_chain}; # if not already done next if $s->{_chain} == $c; # skip backlinks my $ch = $s->{_chain}; push @rc, [ $ch, $s ]; # point node to new next node ($next_chain, $max, $next) = ($ch, $ch->{len}, $s) if $ch->{len} > $max; } if (defined $next_chain && $self->{debug}) { print STDERR "# results of tracking successors:\n"; for my $ch (@rc) { my ($c,$s) = @$ch; my $len = $c->length($s); print STDERR "# chain $c->{id} starting at $c->{start}->{name} (len $c->{len}) ". " pointing to node $s->{name} (len from there: $len)\n"; } print STDERR "# Max chain length is $max (chain id $next_chain->{id})\n"; } if (defined $next_chain) { print STDERR "#$indent $node->{name} next: " . $next_chain->start()->{name} . "\n" if $self->{debug}; if ($self->{debug}) { print STDERR "# merging chains\n"; $c->dump(); $next_chain->dump(); } $c->merge($next_chain, $next) # merge the two chains unless $next == $self->{_root} # except if the next chain starts with # the root node (bug until v0.46) ;# || $next_chain->{start} == $self->{_root}; # or the first chain already starts # with the root node (bug until v0.47) delete $self->{chains}->{$next_chain->{id}} if $next_chain->{len} == 0; } last; } print STDERR "#$indent Chain $node->{_chain} ended at $node->{name}\n" if $self->{debug}; $done; # return nr of done nodes } sub _find_chains { # Track all node chains (A->B->C etc), trying to find the longest possible # node chain. Returns (one of) the root node(s) of the graph. my $self = shift; print STDERR "# Tracking chains\n" if $self->{debug}; # drop all old chain info $self->{_chains} = { }; $self->{_chain} = 0; # new chain ID # For all not-done-yet nodes, track the chain starting with that node. # compute predecessors for all nodes: O(1) my $p; my $has_origin = 0; foreach my $n (ord_values ( $self->{nodes} ), ord_values ( $self->{groups} )) # for my $n (ord_values ( $self->{nodes} )) { $n->{_chain} = undef; # reset chain info $has_origin = 0; $has_origin = 1 if defined $n->{origin} && $n->{origin} != $n; $p->{$n->{name}} = [ $n->has_predecessors(), $has_origin, abs($n->{rank}) ]; } my $done = 0; my $todo = scalar keys %{$self->{nodes}}; # the node where the layout should start, as name my $root_name = $self->{attr}->{root}; $self->{_root} = undef; # as ref to a Node object # Start at nodes with no predecessors (starting points) and then do the rest: for my $name ($root_name, sort { my $aa = $p->{$a}; my $bb = $p->{$b}; # sort first on rank $aa->[2] <=> $bb->[2] || # nodes that have an origin come last $aa->[1] <=> $bb->[1] || # nodes with no predecessorts are to be prefered $aa->[0] <=> $bb->[0] || # last resort, alphabetically sorted $a cmp $b } keys %$p) { next unless defined $name; # in case no root was set, first entry # will be undef and must be skipped my $n = $self->{nodes}->{$name}; # print STDERR "# tracing chain from $name (", join(", ", @{$p->{$name}}),")\n"; # store root node unless already found, is accessed in _follow_chain() $self->{_root} = $n unless defined $self->{_root}; last if $done == $todo; # already processed all nodes? # track the chain unless already done and count number of nodes done $done += _follow_chain($n) unless defined $n->{_chain}; } print STDERR "# Oops - done only $done nodes, but should have done $todo.\n" if $done != $todo && $self->{debug}; print STDERR "# Done all $todo nodes.\n" if $done == $todo && $self->{debug}; $self->{_root}; } ############################################################################# # debug sub _dump_stack { my ($self, @todo) = @_; print STDERR "# Action stack contains ", scalar @todo, " steps:\n"; for my $action (@todo) { my $action_type = $action->[0]; if ($action_type == ACTION_NODE) { my ($at,$node,$try,$edge) = @$action; my $e = ''; $e = " on edge $edge->{id}" if defined $edge; print STDERR "# place '$node->{name}' with try $try$e\n"; } elsif ($action_type == ACTION_CHAIN) { my ($at, $node, $try, $parent, $edge) = @$action; my $id = 'unknown'; $id = $edge->{id} if ref($edge); print STDERR "# chain '$node->{name}' from parent '$parent->{name}' with try $try (for edge id $id)'\n"; } elsif ($action_type == ACTION_TRACE) { my ($at,$edge) = @$action; my ($src,$dst) = ($edge->{from}, $edge->{to}); print STDERR "# trace '$src->{name}' to '$dst->{name}' via edge $edge->{id}\n"; } elsif ($action_type == ACTION_EDGES) { my $at = shift @$action; print STDERR "# tracing the following edges, shortest and with flow first:\n"; } elsif ($action_type == ACTION_SPLICE) { my ($at) = @$action; print STDERR "# splicing in group filler cells\n"; } } } sub _action { # generate an action for the action stack toplace a node my ($self, $action, $node, @params) = @_; # mark the node as already done delete $node->{_todo}; # mark all children of $node as processed, too, because they will be # placed at the same time: $node->_mark_as_placed() if keys %{$node->{children}} > 0; [ $action, $node, @params ]; } ############################################################################# # layout the graph # The general layout routine for the entire graph: sub layout { my $self = shift; # ( { type => 'force' } ) my $args = $_[0]; # ( type => 'force' ) $args = { @_ } if @_ > 1; my $type = 'adhoc'; $type = 'force' if $args->{type} && $args->{type} eq 'force'; # protect the layout with a timeout, unless run under the debugger: eval { local $SIG{ALRM} = sub { die "layout did not finish in time\n" }; alarm(abs( $args->{timeout} || $self->{timeout} || 5)) unless defined $DB::single; # no timeout under the debugger print STDERR "#\n# Starting $type-based layout.\n" if $self->{debug}; # Reset the sequence of the random generator, so that for the same # seed, the same layout will occur. Both for testing and repeatable # layouts based on max score. srand($self->{seed}); if ($type eq 'force') { require Graph::Easy::Layout::Force; $self->error("Force-directed layouts are not yet implemented."); $self->_layout_force(); } else { $self->_edges_into_groups(); $self->_layout(); } }; # eval {}; -- end of timeout protected code alarm(0); # disable alarm # cleanup $self->{chains} = undef; # drop chain info foreach my $n (ord_values ( $self->{nodes} ), ord_values ( $self->{groups} )) { # drop old chain info $n->{_next} = undef; delete $n->{_chain}; delete $n->{_c}; } delete $self->{_root}; die $@ if $@; # propagate errors } sub _drop_caches { # before the layout phase, we drop cached information from the last run my $self = shift; for my $n (ord_values ( $self->{nodes} )) { # XXX after we laid out the individual groups: # skip nodes that are not part of the current group #next if $n->{group} && !$self->{graph}; # empty the cache of computed values (flow, label, border etc) $n->{cache} = {}; $n->{x} = undef; $n->{y} = undef; # mark every node as not placed yet $n->{w} = undef; # force size recalculation $n->{_todo} = undef; # mark as todo } for my $g (ord_values ( $self->{groups} )) { $g->{x} = undef; $g->{y} = undef; # mark every group as not placed yet $g->{_todo} = undef; # mark as todo } } sub _layout { my $self = shift; ########################################################################### # do some assorted stuff beforehand print STDERR "# Doing layout for ", (defined $self->{name} ? 'group ' . $self->{name} : 'main graph'), "\n" if $self->{debug}; # XXX TODO: # for each primary group # my @groups = $self->groups_within(0); # # if (@groups > 0 && $self->{debug}) # { # print STDERR "# Found the following top-level groups:\n"; # for my $g (@groups) # { # print STDERR "# $g $g->{name}\n"; # } # } # # # layout each group on its own, recursively: # foreach my $g (@groups) # { # $g->_layout(); # } # finally assembly everything together $self->_drop_caches(); local $_; $_->_grow() for ord_values ( $self->{nodes} ); $self->_assign_ranks(); # find (longest possible) chains of nodes to "straighten" graph my $root = $self->_find_chains(); ########################################################################### # prepare our stack of things we need to do before we are finished # action stack, place root 1st if it is known my @todo = $self->_action( ACTION_NODE, $root, 0 ) if ref $root; if ($self->{debug}) { print STDERR "# Generated the following chains:\n"; for my $chain ( sort { $a->{len} <=> $b->{len} || $a->{start}->{name} cmp $b->{start}->{name} } values %{$self->{chains}}) { $chain->dump(' '); } } # mark all edges as unprocessed, so that we do not process them twice for my $edge (ord_values ( $self->{edges} )) { $edge->_clear_cells(); $edge->{_todo} = undef; # mark as todo } # XXX TODO: # put all chains on heap (based on their len) # take longest chain, resolve it and all "connected" chains, repeat until # heap is empty for my $chain (sort { # chain starting at root first (($b->{start} == $root) <=> ($a->{start} == $root)) || # longest chains first ($b->{len} <=> $a->{len}) || # chains on nodes that do have an origin come later (defined($a->{start}->{origin}) <=> defined ($b->{start}->{origin})) || # last resort, sort on name of the first node in chain ($a->{start}->{name} cmp $b->{start}->{name}) } values %{$self->{chains}}) { print STDERR "# laying out chain $chain->{id} (len $chain->{len})\n" if $self->{debug}; # layout the chain nodes, then resolve inter-chain links, then traverse # chains recursively push @todo, @{ $chain->layout() } unless $chain->{_done}; } print STDERR "# Done laying out all chains, doing left-overs:\n" if $self->{debug}; $self->_dump_stack(@todo) if $self->{debug}; # After laying out all chained nodes and their links, we need to resolve # left-over edges and links. We do this for each node, and then for each of # its edges, but do the edges shortest-first. for my $n (ord_values ( $self->{nodes} )) { push @todo, $self->_action( ACTION_NODE, $n, 0 ); # if exists $n->{_todo}; # gather to-do edges my @edges = (); for my $e (sort { $a->{to}->{name} cmp $b->{to}->{name} } values %{$n->{edges}}) # for my $e (ord_values ( $n->{edges} )) { # edge already done? next unless exists $e->{_todo}; # skip links from/to groups next if $e->{to}->isa('Graph::Easy::Group') || $e->{from}->isa('Graph::Easy::Group'); push @edges, $e; delete $e->{_todo}; } # XXX TODO: This does not work, since the nodes are not yet laid out # sort them on their shortest distances # @edges = sort { $b->_distance() <=> $a->_distance() } @edges; # put them on the action stack in that order for my $e (@edges) { push @todo, [ ACTION_TRACE, $e ]; # print STDERR "do $e->{from}->{name} to $e->{to}->{name} ($e->{id} " . $e->_distance().")\n"; # push @todo, [ ACTION_CHAIN, $e->{to}, 0, $n, $e ]; } } print STDERR "# Done laying out left-overs.\n" if $self->{debug}; # after laying out all inter-group nodes and their edges, we need to splice in the # group cells if (scalar $self->groups() > 0) { push @todo, [ ACTION_SPLICE ] if scalar $self->groups(); # now do all group-to-group and node-to-group and group-to-node links: for my $n (ord_values ( $self->{groups} )) { } } $self->_dump_stack(@todo) if $self->{debug}; ########################################################################### # prepare main backtracking-loop my $score = 0; # overall score $self->{cells} = { }; # cell array (0..x,0..y) my $cells = $self->{cells}; print STDERR "# Start\n" if $self->{debug}; $self->{padding_cells} = 0; # set to false (no filler cells yet) my @done = (); # stack with already done actions my $step = 0; my $tries = 16; # store for each rank the initial row/coluumn $self->{_rank_pos} = {}; # does rank_pos store rows or columns? $self->{_rank_coord} = 'y'; my $flow = $self->flow(); $self->{_rank_coord} = 'x' if $flow == 0 || $flow == 180; TRY: while (@todo > 0) # all actions on stack done? { $step ++; if ($self->{debug} && ($step % 1)==0) { my ($nodes,$e_nodes,$edges,$e_edges) = $self->_count_done_things(); print STDERR "# Done $nodes nodes and $edges edges.\n"; #$self->{debug} = 2 if $nodes > 243; return if ($nodes > 230); } # pop one action and mark it as done my $action = shift @todo; push @done, $action; # get the action type (ACTION_NODE etc) my $action_type = $action->[0]; my ($src, $dst, $mod, $edge); if ($action_type == ACTION_NODE) { my (undef, $node,$try,$edge) = @$action; print STDERR "# step $step: action place '$node->{name}' (try $try)\n" if $self->{debug}; $mod = 0 if defined $node->{x}; # $action is node to be placed, generic placement at "random" location $mod = $self->_find_node_place( $node, $try, undef, $edge) unless defined $node->{x}; } elsif ($action_type == ACTION_CHAIN) { my (undef, $node,$try,$parent, $edge) = @$action; print STDERR "# step $step: action chain '$node->{name}' from parent '$parent->{name}'\n" if $self->{debug}; $mod = 0 if defined $node->{x}; $mod = $self->_find_node_place( $node, $try, $parent, $edge ) unless defined $node->{x}; } elsif ($action_type == ACTION_TRACE) { # find a path to the target node ($action_type,$edge) = @$action; $src = $edge->{from}; $dst = $edge->{to}; print STDERR "# step $step: action trace '$src->{name}' => '$dst->{name}'\n" if $self->{debug}; if (!defined $dst->{x}) { # warn ("Target node $dst->{name} not yet placed"); $mod = $self->_find_node_place( $dst, 0, undef, $edge ); } if (!defined $src->{x}) { # warn ("Source node $src->{name} not yet placed"); $mod = $self->_find_node_place( $src, 0, undef, $edge ); } # find path (mod is score modifier, or undef if no path exists) $mod = $self->_trace_path( $src, $dst, $edge ); } elsif ($action_type == ACTION_SPLICE) { # fill in group info and return $self->_fill_group_cells($cells) unless $self->{error}; $mod = 0; } else { require Carp; Carp::confess ("Illegal action $action->[0] on TODO stack"); } if (!defined $mod) { # rewind stack if (($action_type == ACTION_NODE || $action_type == ACTION_CHAIN)) { print STDERR "# Step $step: Rewind stack for $action->[1]->{name}\n" if $self->{debug}; # undo node placement and free all cells $action->[1]->_unplace() if defined $action->[1]->{x}; $action->[2]++; # increment try for placing $tries--; last TRY if $tries == 0; } else { print STDERR "# Step $step: Rewind stack for path from $src->{name} to $dst->{name}\n" if $self->{debug}; # if we couldn't find a path, we need to rewind one more action (just # redoing the path would would fail again!) # unshift @todo, pop @done; # unshift @todo, pop @done; # $action = $todo[0]; # $action_type = $action->[0]; # $self->_dump_stack(@todo); # # if (($action_type == ACTION_NODE || $action_type == ACTION_CHAIN)) # { # # undo node placement # $action->[1]->_unplace(); # $action->[2]++; # increment try for placing # } $tries--; last TRY if $tries == 0; next TRY; } unshift @todo, $action; next TRY; } $score += $mod; print STDERR "# Step $step: Score is $score\n\n" if $self->{debug}; } $self->{score} = $score; # overall score # if ($tries == 0) { my ($nodes,$e_nodes,$edges,$e_edges) = $self->_count_done_things(); if ( ($nodes != $e_nodes) || ($edges != $e_edges) ) { $self->warn( "Layouter could only place $nodes nodes/$edges edges out of $e_nodes/$e_edges - giving up"); } else { $self->_optimize_layout(); } } # all things on the stack were done, or we encountered an error } sub _count_done_things { my $self = shift; # count placed nodes my $nodes = 0; my $i = 1; for my $n (ord_values ( $self->{nodes} )) { $nodes++ if defined $n->{x}; } my $edges = 0; $i = 1; # count fully routed edges for my $e (ord_values ( $self->{edges} )) { $edges++ if scalar @{$e->{cells}} > 0 && !exists $e->{_todo}; } my $e_nodes = scalar keys %{$self->{nodes}}; my $e_edges = scalar keys %{$self->{edges}}; return ($nodes,$e_nodes,$edges,$e_edges); } my $size_name = { EDGE_HOR() => [ 'cx', 'x' ], EDGE_VER() => [ 'cy', 'y' ] }; sub _optimize_layout { my $self = shift; # optimize the finished layout my $all_cells = $self->{cells}; ########################################################################### # for each edge, compact HOR and VER stretches of cells for my $e (ord_values ( $self->{edges} )) { my $cells = $e->{cells}; # there need to be at least two cells for us to be able to combine them next if @$cells < 2; print STDERR "# Compacting edge $e->{from}->{name} to $e->{to}->{name}\n" if $self->{debug}; my $f = $cells->[0]; my $i = 1; my ($px, $py); # coordinates of the placeholder cell while ($i < @$cells) { my $c = $cells->[$i++]; # print STDERR "# at $f->{type} $f->{x},$f->{y} (next: $c->{type} $c->{x},$c->{y})\n"; my $t1 = $f->{type} & EDGE_NO_M_MASK; my $t2 = $c->{type} & EDGE_NO_M_MASK; # > 0: delete that cell: 1 => reverse order, 2 => with hole my $delete = 0; # compare $first to $c if ($t1 == $t2 && ($t1 == EDGE_HOR || $t1 == EDGE_VER)) { # print STDERR "# $i: Combining them.\n"; # check that both pieces are continues (e.g. with a cross section, # the other edge has a hole in the cell array) # if the second cell has a misc (label, short) flag, carry it over $f->{type} += $c->{type} & EDGE_MISC_MASK; # which size/coordinate to modify my ($m,$co) = @{ $size_name->{$t1} }; # print STDERR "# Combining edge cells $f->{x},$f->{y} and $c->{x},$c->{y}\n"; # new width/height is the combined size $f->{$m} = ($f->{$m} || 1) + ($c->{$m} || 1); # print STDERR "# Result $f->{x},$f->{y} ",$f->{cx}||1," ", $f->{cy}||1,"\n"; # drop the reference from the $cells array for $c delete $all_cells->{ "$c->{x},$c->{y}" }; ($px, $py) = ($c->{x}, $c->{y}); if ($f->{$co} > $c->{$co}) { # remember coordinate of the moved cell for the placeholder ($px, $py) = ($f->{x}, $f->{y}); # move $f to the new place if it was modified delete $all_cells->{ "$f->{x},$f->{y}" }; # correct start coordinate for reversed order $f->{$co} -= ($c->{$m} || 1); $all_cells->{ "$f->{x},$f->{y}" } = $f; } $delete = 1; # delete $c } # remove that cell, but start combining at next # print STDERR "# found hole at $i\n" if $c->{type} == EDGE_HOLE; $delete = 2 if $c->{type} == EDGE_HOLE; if ($delete) { splice (@{$e->{cells}}, $i-1, 1); # remove from the edge if ($delete == 1) { my $xy = "$px,$py"; # replace with placeholder (important for HTML output) $all_cells->{$xy} = Graph::Easy::Edge::Cell::Empty->new ( x => $px, y => $py, ) unless $all_cells->{$xy}; $i--; $c = $f; # for the next statement } else { $c = $cells->[$i-1]; } } $f = $c; } # $i = 0; # while ($i < @$cells) # { # my $c = $cells->[$i]; # print STDERR "# $i: At $c->{type} $c->{x},$c->{y} ", $c->{cx}||1, " ", $c->{cy} || 1,"\n"; # $i++; # } } print STDERR "# Done compacting edges.\n" if $self->{debug}; } 1; __END__ =head1 NAME Graph::Easy::Layout - Layout the graph from Graph::Easy =head1 SYNOPSIS use Graph::Easy; my $graph = Graph::Easy->new(); my $bonn = Graph::Easy::Node->new( name => 'Bonn', ); my $berlin = Graph::Easy::Node->new( name => 'Berlin', ); $graph->add_edge ($bonn, $berlin); $graph->layout(); print $graph->as_ascii( ); # prints: # +------+ +--------+ # | Bonn | --> | Berlin | # +------+ +--------+ =head1 DESCRIPTION C<Graph::Easy::Layout> contains just the actual layout code for L<Graph::Easy|Graph::Easy>. =head1 METHODS C<Graph::Easy::Layout> injects the following methods into the C<Graph::Easy> namespace: =head2 layout() $graph->layout(); Layout the actual graph. =head2 _assign_ranks() $graph->_assign_ranks(); Used by C<layout()> to assign each node a rank, so they can be sorted and grouped on these. =head2 _optimize_layout Used by C<layout()> to optimize the layout as a last step. =head1 EXPORT Exports nothing. =head1 SEE ALSO L<Graph::Easy>. =head1 AUTHOR Copyright (C) 2004 - 2008 by Tels L<http://bloodgate.com> See the LICENSE file for information. =cut ����������������������������������������������������������������Graph-Easy-0.73/lib/Graph/Easy/Group/���������������������������������������������������������������0000755�0000764�0000764�00000000000�12150110221�017064� 5����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/lib/Graph/Easy/Group/Cell.pm��������������������������������������������������������0000644�0000764�0000764�00000021371�12147675341�020336� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������############################################################################# # A cell of a group during layout. Part of Graph::Easy. # ############################################################################# package Graph::Easy::Group::Cell; use Graph::Easy::Node; @ISA = qw/Graph::Easy::Node/; $VERSION = '0.14'; use strict; BEGIN { *get_attribute = \&attribute; } ############################################################################# # The different types for a group-cell: use constant { GROUP_INNER => 0, # completely sourounded by group cells GROUP_RIGHT => 1, # right border only GROUP_LEFT => 2, # left border only GROUP_TOP => 3, # top border only GROUP_BOTTOM => 4, # bottom border only GROUP_ALL => 5, # completely sourounded by non-group cells GROUP_BOTTOM_RIGHT => 6, # bottom and right border GROUP_BOTTOM_LEFT => 7, # bottom and left border GROUP_TOP_RIGHT => 8, # top and right border GROUP_TOP_LEFT => 9, # top and left order GROUP_MAX => 5, # max number }; my $border_styles = { # type top, bottom, left, right, class GROUP_INNER() => [ 0, 0, 0, 0, ['gi'] ], GROUP_RIGHT() => [ 0, 0, 0, 1, ['gr'] ], GROUP_LEFT() => [ 0, 0, 1, 0, ['gl'] ], GROUP_TOP() => [ 1, 0, 0, 0, ['gt'] ], GROUP_BOTTOM() => [ 0, 1, 0, 0, ['gb'] ], GROUP_ALL() => [ 0, 0, 0, 0, ['ga'] ], GROUP_BOTTOM_RIGHT() => [ 0, 1, 0, 1, ['gb','gr'] ], GROUP_BOTTOM_LEFT() => [ 0, 1, 1, 0, ['gb','gl'] ], GROUP_TOP_RIGHT() => [ 1, 0, 0, 1, ['gt','gr'] ], GROUP_TOP_LEFT() => [ 1, 0, 1, 0, ['gt','gl'] ], }; my $border_name = [ 'top', 'bottom', 'left', 'right' ]; sub _css { my ($c, $id, $group, $border) = @_; my $css = ''; for my $type (0 .. 5) { my $b = $border_styles->{$type}; # If border eq 'none', this would needlessly repeat the "border: none" # from the general group class. next if $border eq 'none'; my $cl = '.' . $b->[4]->[0]; # $cl .= "-$group" unless $group eq ''; $css .= "table.graph$id $cl {"; if ($type == GROUP_INNER) { $css .= " border: none;"; # shorter CSS } elsif ($type == GROUP_ALL) { $css .= " border-style: $border;"; # shorter CSS } else { for (my $i = 0; $i < 4; $i++) { $css .= ' border-' . $border_name->[$i] . "-style: $border;" if $b->[$i]; } } $css .= "}\n"; } $css; } ############################################################################# sub _init { # generic init, override in subclasses my ($self,$args) = @_; $self->{class} = 'group'; $self->{cell_class} = ' gi'; $self->{name} = ''; $self->{'x'} = 0; $self->{'y'} = 0; # XXX TODO check arguments foreach my $k (sort keys %$args) { $self->{$k} = $args->{$k}; } if (defined $self->{group}) { # register ourselves at this group $self->{group}->_add_cell ($self); # XXX CHECK also implement sub_class() $self->{class} = $self->{group}->{class}; $self->{class} = 'group' unless defined $self->{class}; } $self; } sub _set_type { # set the proper type of this cell based on the sourrounding cells my ($self, $cells) = @_; # +------+--------+-------+ # | LT TOP RU | # + + + + # | LEFT INNER Right | # + + + + # | LB BOTTOM RB | # +------+--------+-------+ my @coord = ( [ 0, -1, ' gt' ], [ +1, 0, ' gr' ], [ 0, +1, ' gb' ], [ -1, 0, ' gl' ], ); my ($sx,$sy) = ($self->{x},$self->{y}); my $class = ''; my $gr = $self->{group}; foreach my $co (@coord) { my ($x,$y,$c) = @$co; $x += $sx; $y += $sy; my $cell = $cells->{"$x,$y"}; # belongs to the same group? my $go = 0; $go = $cell->group() if UNIVERSAL::can($cell, 'group'); $class .= $c unless defined $go && $gr == $go; } $class = ' ga' if $class eq ' gt gr gb gl'; $self->{cell_class} = $class; $self; } sub _set_label { my $self = shift; $self->{has_label} = 1; $self->{name} = $self->{group}->label(); } sub shape { 'rect'; } sub attribute { my ($self, $name) = @_; # print STDERR "called attribute($name)\n"; # return $self->{group}->attribute($name); my $group = $self->{group}; return $group->{att}->{$name} if exists $group->{att}->{$name}; $group->{cache} = {} unless exists $group->{cache}; $group->{cache}->{att} = {} unless exists $group->{cache}->{att}; my $cache = $group->{cache}->{att}; return $cache->{$name} if exists $cache->{$name}; $cache->{$name} = $group->attribute($name); } use constant isa_cell => 1; ############################################################################# # conversion to ASCII or HTML sub as_ascii { my ($self, $x,$y) = @_; my $fb = $self->_framebuffer($self->{w}, $self->{h}); my $border_style = $self->attribute('borderstyle'); my $EM = 14; # use $self here and not $self->{group} to engage attribute cache: my $border_width = Graph::Easy::_border_width_in_pixels($self,$EM); # convert overly broad borders to the correct style $border_style = 'bold' if $border_width > 2; $border_style = 'broad' if $border_width > $EM * 0.2 && $border_width < $EM * 0.75; $border_style = 'wide' if $border_width >= $EM * 0.75; if ($border_style ne 'none') { ######################################################################### # draw our border into the framebuffer my $c = $self->{cell_class}; my $b_top = $border_style; my $b_left = $border_style; my $b_right = $border_style; my $b_bottom = $border_style; if ($c !~ 'ga') { $b_top = 'none' unless $c =~ /gt/; $b_left = 'none' unless $c =~ /gl/; $b_right = 'none' unless $c =~ /gr/; $b_bottom = 'none' unless $c =~ /gb/; } $self->_draw_border($fb, $b_right, $b_bottom, $b_left, $b_top, $x, $y); } if ($self->{has_label}) { # include our label my $align = $self->attribute('align'); # the default label cell as a top border, but no left/right border my $ys = 0.5; $ys = 0 if $border_style eq 'none'; my $h = $self->{h} - 1; $h ++ if $border_style eq 'none'; $self->_printfb_aligned ($fb, 0, $ys, $self->{w}, $h, $self->_aligned_label($align), 'middle'); } join ("\n", @$fb); } sub class { my $self = shift; $self->{class} . $self->{cell_class}; } ############################################################################# # for rendering this cell as ASCII/Boxart, we need to correct our width based # on whether we have a border or not. But this is only known after parsing is # complete. sub _correct_size { my ($self,$format) = @_; if (!defined $self->{w}) { my $border = $self->attribute('borderstyle'); $self->{w} = 0; $self->{h} = 0; # label needs space $self->{h} = 1 if $self->{has_label}; if ($border ne 'none') { # class "gt", "gb", "gr" or "gr" will be compressed away # (e.g. only edge cells will be existant) if ($self->{has_label} || ($self->{cell_class} =~ /g[rltb] /)) { $self->{w} = 2; $self->{h} = 2; } elsif ($self->{cell_class} =~ /^ g[rl]\z/) { $self->{w} = 2; } elsif ($self->{cell_class} =~ /^ g[bt]\z/) { $self->{h} = 2; } } } if ($self->{has_label}) { my ($w,$h) = $self->dimensions(); $self->{h} += $h; $self->{w} += $w; } } 1; __END__ =head1 NAME Graph::Easy::Group::Cell - A cell in a group =head1 SYNOPSIS use Graph::Easy; my $ssl = Graph::Easy::Edge->new( ); $ssl->set_attributes( label => 'encrypted connection', style => '-->', color => 'red', ); $graph = Graph::Easy->new(); $graph->add_edge('source', 'destination', $ssl); print $graph->as_ascii(); =head1 DESCRIPTION A C<Graph::Easy::Group::Cell> represents a cell of a group. Group cells can have a background and, if they are on the outside, a border. There should be no need to use this package directly. =head1 METHODS =head2 error() $last_error = $group->error(); $group->error($error); # set new messags $group->error(''); # clear error Returns the last error message, or '' for no error. =head2 as_ascii() my $ascii = $cell->as_ascii(); Returns the cell as a little ascii representation. =head2 as_html() my $html = $cell->as_html($tag,$id); Returns the cell as HTML code. =head2 label() my $label = $cell->label(); Returns the name (also known as 'label') of the cell. =head2 class() my $class = $cell->class(); Returns the classname(s) of this cell, like: group_cities gr gb for a cell with a bottom (gb) and right (gr) border in the class C<cities>. =head1 EXPORT None. =head1 SEE ALSO L<Graph::Easy>. =head1 AUTHOR Copyright (C) 2004 - 2007 by Tels L<http://bloodgate.com>. See the LICENSE file for more details. =cut �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/lib/Graph/Easy/Group/Anon.pm��������������������������������������������������������0000644�0000764�0000764�00000003475�11675050456�020356� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������############################################################################# # (c) by Tels 2004. Part of Graph::Easy. An anonymous group. # ############################################################################# package Graph::Easy::Group::Anon; use Graph::Easy::Group; @ISA = qw/Graph::Easy::Group/; $VERSION = '0.02'; use strict; sub _init { my $self = shift; $self->SUPER::_init(@_); $self->{name} = 'Group #' . $self->{id}; $self->{class} = 'group.anon'; $self->{att}->{label} = ''; $self; } sub _correct_size { my $self = shift; $self->{w} = 3; $self->{h} = 3; $self; } sub attributes_as_txt { my $self = shift; $self->SUPER::attributes_as_txt( { node => { label => undef, shape => undef, class => undef, } } ); } sub as_pure_txt { '( )'; } sub _as_part_txt { '( )'; } sub as_graphviz_txt { my $self = shift; my $name = $self->{name}; # quote special chars in name $name =~ s/([\[\]\(\)\{\}\#])/\\$1/g; '"' . $name . '"'; } sub text_styles_as_css { ''; } sub is_anon { # is an anon group 1; } 1; __END__ =head1 NAME Graph::Easy::Group::Anon - An anonymous group of nodes in Graph::Easy =head1 SYNOPSIS use Graph::Easy::Group::Anon; my $anon = Graph::Easy::Group::Anon->new(); =head1 DESCRIPTION A C<Graph::Easy::Group::Anon> represents an anonymous group of nodes, e.g. a group without a name. The syntax in the Graph::Easy textual description language looks like this: ( [ Bonn ] -> [ Berlin ] ) This module is loaded and used automatically by Graph::Easy, so there is no need to use it manually. =head1 EXPORT None by default. =head1 SEE ALSO L<Graph::Easy::Group>. =head1 AUTHOR Copyright (C) 2004 - 2006 by Tels L<http://bloodgate.com>. See the LICENSE file for more details. =cut ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/lib/Graph/Easy/Group.pm�������������������������������������������������������������0000644�0000764�0000764�00000047101�12150074222�017437� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������############################################################################# # A group of nodes. Part of Graph::Easy. # ############################################################################# package Graph::Easy::Group; use Graph::Easy::Group::Cell; use Graph::Easy; use Scalar::Util qw/weaken/; @ISA = qw/Graph::Easy::Node Graph::Easy/; $VERSION = '0.22'; use strict; use Graph::Easy::Util qw(ord_values); ############################################################################# sub _init { # generic init, override in subclasses my ($self,$args) = @_; $self->{name} = 'Group #'. $self->{id}; $self->{class} = 'group'; $self->{_cells} = {}; # the Group::Cell objects # $self->{cx} = 1; # $self->{cy} = 1; foreach my $k (sort keys %$args) { if ($k !~ /^(graph|name)\z/) { require Carp; Carp::confess ("Invalid argument '$k' passed to Graph::Easy::Group->new()"); } $self->{$k} = $args->{$k}; } $self->{nodes} = {}; $self->{groups} = {}; $self->{att} = {}; $self; } ############################################################################# # accessor methods sub nodes { my $self = shift; wantarray ? ( ord_values ( $self->{nodes} ) ) : scalar keys %{$self->{nodes}}; } sub edges { # edges leading from/to this group my $self = shift; wantarray ? ( ord_values ( $self->{edges} ) ) : scalar keys %{$self->{edges}}; } sub edges_within { # edges between nodes inside this group my $self = shift; wantarray ? ( ord_values ( $self->{edges_within} ) ) : scalar keys %{$self->{edges_within}}; } sub _groups_within { my ($self, $level, $max_level, $cur) = @_; no warnings 'recursion'; push @$cur, ord_values ( $self->{groups} ); return if $level >= $max_level; for my $g (ord_values ( $self->{groups} )) { $g->_groups_within($level+1,$max_level, $cur) if scalar keys %{$g->{groups}} > 0; } } ############################################################################# sub set_attribute { my ($self, $name, $val, $class) = @_; $self->SUPER::set_attribute($name, $val, $class); # if defined attribute "nodeclass", put our nodes into that class if ($name eq 'nodeclass') { my $class = $self->{att}->{nodeclass}; for my $node (ord_values ( $self->{nodes} ) ) { $node->sub_class($class); } } $self; } sub shape { my ($self) = @_; # $self->{att}->{shape} || $self->attribute('shape'); ''; } ############################################################################# # node handling sub add_node { # add a node to this group my ($self,$n) = @_; if (!ref($n) || !$n->isa("Graph::Easy::Node")) { if (!ref($self->{graph})) { return $self->error("Cannot add non node-object $n to group '$self->{name}'"); } $n = $self->{graph}->add_node($n); } $self->{nodes}->{ $n->{name} } = $n; # if defined attribute "nodeclass", put our nodes into that class $n->sub_class($self->{att}->{nodeclass}) if exists $self->{att}->{nodeclass}; # register ourselves with the member $n->{group} = $self; # set the proper attribute (for layout) $n->{att}->{group} = $self->{name}; # Register the nodes and the edge with our graph object # and weaken the references. Be carefull to not needlessly # override and weaken again an already existing reference, this # is an O(N) operation in most Perl versions, and thus very slow. # If the node does not belong to a graph yet or belongs to another # graph, add it to our own graph: weaken($n->{graph} = $self->{graph}) unless $n->{graph} && $self->{graph} && $n->{graph} == $self->{graph}; $n; } sub add_member { # add a node or group to this group my ($self,$n) = @_; if (!ref($n) || !$n->isa("Graph::Easy::Node")) { if (!ref($self->{graph})) { return $self->error("Cannot add non node-object $n to group '$self->{name}'"); } $n = $self->{graph}->add_node($n); } return $self->_add_edge($n) if $n->isa("Graph::Easy::Edge"); return $self->add_group($n) if $n->isa('Graph::Easy::Group'); $self->{nodes}->{ $n->{name} } = $n; # if defined attribute "nodeclass", put our nodes into that class my $cl = $self->attribute('nodeclass'); $n->sub_class($cl) if $cl ne ''; # register ourselves with the member $n->{group} = $self; # set the proper attribute (for layout) $n->{att}->{group} = $self->{name}; # Register the nodes and the edge with our graph object # and weaken the references. Be carefull to not needlessly # override and weaken again an already existing reference, this # is an O(N) operation in most Perl versions, and thus very slow. # If the node does not belong to a graph yet or belongs to another # graph, add it to our own graph: weaken($n->{graph} = $self->{graph}) unless $n->{graph} && $self->{graph} && $n->{graph} == $self->{graph}; $n; } sub del_member { # delete a node or group from this group my ($self,$n) = @_; # XXX TOOD: groups vs. nodes my $class = 'nodes'; my $key = 'name'; if ($n->isa('Graph::Easy::Group')) { # XXX TOOD: groups vs. nodes $class = 'groups'; $key = 'id'; } delete $self->{$class}->{ $n->{$key} }; delete $n->{group}; # unregister us if ($n->isa('Graph::Easy::Node')) { # find all edges that mention this node and drop them from the group my $edges = $self->{edges_within}; for my $e (ord_values ( $edges)) { delete $edges->{ $e->{id} } if $e->{from} == $n || $e->{to} == $n; } } $self; } sub del_node { # delete a node from this group my ($self,$n) = @_; delete $self->{nodes}->{ $n->{name} }; delete $n->{group}; # unregister us delete $n->{att}->{group}; # delete the group attribute # find all edges that mention this node and drop them from the group my $edges = $self->{edges_within}; for my $e (ord_values ( $edges)) { delete $edges->{ $e->{id} } if $e->{from} == $n || $e->{to} == $n; } $self; } sub add_nodes { my $self = shift; # make a copy in case of scalars my @arg = @_; foreach my $n (@arg) { if (!ref($n) && !ref($self->{graph})) { return $self->error("Cannot add non node-object $n to group '$self->{name}'"); } return $self->error("Cannot add group-object $n to group '$self->{name}'") if $n->isa('Graph::Easy::Group'); $n = $self->{graph}->add_node($n) unless ref($n); $self->{nodes}->{ $n->{name} } = $n; # set the proper attribute (for layout) $n->{att}->{group} = $self->{name}; # XXX TODO TEST! # # if defined attribute "nodeclass", put our nodes into that class # $n->sub_class($self->{att}->{nodeclass}) if exists $self->{att}->{nodeclass}; # register ourselves with the member $n->{group} = $self; # Register the nodes and the edge with our graph object # and weaken the references. Be carefull to not needlessly # override and weaken again an already existing reference, this # is an O(N) operation in most Perl versions, and thus very slow. # If the node does not belong to a graph yet or belongs to another # graph, add it to our own graph: weaken($n->{graph} = $self->{graph}) unless $n->{graph} && $self->{graph} && $n->{graph} == $self->{graph}; } @arg; } ############################################################################# sub _del_edge { # delete an edge from this group my ($self,$e) = @_; delete $self->{edges_within}->{ $e->{id} }; delete $e->{group}; # unregister us $self; } sub _add_edge { # add an edge to this group (e.g. when both from/to of this edge belong # to this group) my ($self,$e) = @_; if (!ref($e) || !$e->isa("Graph::Easy::Edge")) { return $self->error("Cannot add non edge-object $e to group '$self->{name}'"); } $self->{edges_within}->{ $e->{id} } = $e; # if defined attribute "edgeclass", put our edges into that class my $edge_class = $self->attribute('edgeclass'); $e->sub_class($edge_class) if $edge_class ne ''; # XXX TODO: inline $self->add_node($e->{from}); $self->add_node($e->{to}); # register us, but don't do weaken() if the ref was already set weaken($e->{group} = $self) unless defined $e->{group} && $e->{group} == $self; $e; } sub add_edge { # Add an edge to the graph of this group, then register it with this group. my ($self,$from,$to) = @_; my $g = $self->{graph}; return $self->error("Cannot add edge to group '$self->{name}' without graph") unless defined $g; my $edge = $g->add_edge($from,$to); $self->_add_edge($edge); } sub add_edge_once { # Add an edge to the graph of this group, then register it with this group. my ($self,$from,$to) = @_; my $g = $self->{graph}; return $self->error("Cannot non edge to group '$self->{name}' without graph") unless defined $g; my $edge = $g->add_edge_once($from,$to); # edge already exists => so fetch it $edge = $g->edge($from,$to) unless defined $edge; $self->_add_edge($edge); } ############################################################################# sub add_group { # add a group to us my ($self,$group) = @_; # group with that name already exists? my $name = $group; $group = $self->{groups}->{ $group } unless ref $group; # group with that name doesn't exist, so create new one $group = $self->{graph}->add_group($name) unless ref $group; # index under the group name for easier lookup $self->{groups}->{ $group->{name} } = $group; # make attribute->('group') work $group->{att}->{group} = $self->{name}; # register group with the graph and ourself $group->{graph} = $self->{graph}; $group->{group} = $self; { no warnings; # dont warn on already weak references weaken($group->{graph}); weaken($group->{group}); } $self->{graph}->{score} = undef; # invalidate last layout $group; } # cell management - used by the layouter sub _cells { # return all the cells this group currently occupies my $self = shift; $self->{_cells}; } sub _clear_cells { # remove all belonging cells my $self = shift; $self->{_cells} = {}; $self; } sub _add_cell { # add a cell to the list of cells this group covers my ($self,$cell) = @_; $cell->_update_boundaries(); $self->{_cells}->{"$cell->{x},$cell->{y}"} = $cell; $cell; } sub _del_cell { # delete a cell from the list of cells this group covers my ($self,$cell) = @_; delete $self->{_cells}->{"$cell->{x},$cell->{y}"}; delete $cell->{group}; $self; } sub _find_label_cell { # go through all cells of this group and find one where to attach the label my $self = shift; my $g = $self->{graph}; my $align = $self->attribute('align'); my $loc = $self->attribute('labelpos'); # depending on whether the label should be on top or bottom: my $match = qr/^\s*gt\s*\z/; $match = qr/^\s*gb\s*\z/ if $loc eq 'bottom'; my $lc; # the label cell for my $c (ord_values ( $self->{_cells} )) { # find a cell where to put the label next unless $c->{cell_class} =~ $match; if (defined $lc) { if ($align eq 'left') { # find top-most, left-most cell next if $lc->{x} < $c->{x} || $lc->{y} < $c->{y}; } elsif ($align eq 'center') { # just find any top-most cell next if $lc->{y} < $c->{y}; } elsif ($align eq 'right') { # find top-most, right-most cell next if $lc->{x} > $c->{x} || $lc->{y} < $c->{y}; } } $lc = $c; } # find the cell mostly near the center in the found top-row if (ref($lc) && $align eq 'center') { my ($left, $right); # find left/right most coordinates for my $c (ord_values ( $self->{_cells} )) { next if $c->{y} != $lc->{y}; $left = $c->{x} if !defined $left || $left > $c->{x}; $right = $c->{x} if !defined $right || $right < $c->{x}; } my $center = int(($right - $left) / 2 + $left); my $min_dist; # find the cell mostly near the center in the found top-row for my $c (ord_values ( $self->{_cells} )) { next if $c->{y} != $lc->{y}; # squared to get rid of sign my $dist = ($center - $c->{x}); $dist *= $dist; next if defined $min_dist && $dist > $min_dist; $min_dist = $dist; $lc = $c; } } print STDERR "# Setting label for group '$self->{name}' at $lc->{x},$lc->{y}\n" if $self->{debug}; $lc->_set_label() if ref($lc); } sub layout { my $self = shift; $self->_croak('Cannot call layout() on a Graph::Easy::Group directly.'); } sub _layout { my $self = shift; ########################################################################### # set local {debug} for groups local $self->{debug} = $self->{graph}->{debug}; $self->SUPER::_layout(); } sub _set_cell_types { my ($self, $cells) = @_; # Set the right cell class for all of our cells: for my $cell (ord_values ( $self->{_cells} )) { $cell->_set_type($cells); } $self; } 1; __END__ =head1 NAME Graph::Easy::Group - A group of nodes (aka subgraph) in Graph::Easy =head1 SYNOPSIS use Graph::Easy; my $bonn = Graph::Easy::Node->new('Bonn'); $bonn->set_attribute('border', 'solid 1px black'); my $berlin = Graph::Easy::Node->new( name => 'Berlin' ); my $cities = Graph::Easy::Group->new( name => 'Cities', ); $cities->set_attribute('border', 'dashed 1px blue'); $cities->add_nodes ($bonn); # $bonn will be ONCE in the group $cities->add_nodes ($bonn, $berlin); =head1 DESCRIPTION A C<Graph::Easy::Group> represents a group of nodes in an C<Graph::Easy> object. These nodes are grouped together on output. =head1 METHODS =head2 new() my $group = Graph::Easy::Group->new( $options ); Create a new, empty group. C<$options> are the possible options, see L<Graph::Easy::Node> for a list. =head2 error() $last_error = $group->error(); $group->error($error); # set new messags $group->error(''); # clear error Returns the last error message, or '' for no error. =head2 as_ascii() my $ascii = $group->as_ascii(); Return the group as a little box drawn in ASCII art as a string. =head2 name() my $name = $group->name(); Return the name of the group. =head2 id() my $id = $group->id(); Returns the group's unique ID number. =head2 set_attribute() $group->set_attribute('border-style', 'none'); Sets the specified attribute of this (and only this!) group to the specified value. =head2 add_member() $group->add_member($node); $group->add_member($group); Add the specified object to this group and returns this member. If the passed argument is a scalar, will treat it as a node name. Note that each object can only be a member of one group at a time. =head2 add_node() $group->add_node($node); Add the specified node to this group and returns this node. Note that each object can only be a member of one group at a time. =head2 add_edge(), add_edge_once() $group->add_edge($edge); # Graph::Easy::Edge $group->add_edge($from, $to); # Graph::Easy::Node or # Graph::Easy::Group $group->add_edge('From', 'To'); # Scalars If passed an Graph::Easy::Edge object, moves the nodes involved in this edge to the group. if passed two nodes, adds these nodes to the graph (unless they already exist) and adds an edge between these two nodes. See L<add_edge_once()> to avoid creating multiple edges. This method works only on groups that are part of a graph. Note that each object can only be a member of one group at a time, and edges are automatically a member of a group if and only if both the target and the destination node are a member of the same group. =head2 add_group() my $inner = $group->add_group('Group name'); my $nested = $group->add_group($group); Add a group as subgroup to this group and returns this group. =head2 del_member() $group->del_member($node); $group->del_member($group); Delete the specified object from this group. =head2 del_node() $group->del_node($node); Delete the specified node from this group. =head2 del_edge() $group->del_edge($edge); Delete the specified edge from this group. =head2 add_nodes() $group->add_nodes($node, $node2, ... ); Add all the specified nodes to this group and returns them as a list. =head2 nodes() my @nodes = $group->nodes(); Returns a list of all node objects that belong to this group. =head2 edges() my @edges = $group->edges(); Returns a list of all edge objects that lead to or from this group. Note: This does B<not> return edges between nodes that are inside the group, for this see L<edges_within()>. =head2 edges_within() my @edges_within = $group->edges_within(); Returns a list of all edge objects that are I<inside> this group, in arbitrary order. Edges are automatically considered I<inside> a group if their starting and ending node both are in the same group. Note: This does B<not> return edges between this group and other groups, nor edges between this group and nodes outside this group, for this see L<edges()>. =head2 groups() my @groups = $group->groups(); Returns the contained groups of this group as L<Graph::Easy::Group> objects, in arbitrary order. =head2 groups_within() # equivalent to $group->groups(): my @groups = $group->groups_within(); # all my @toplevel_groups = $group->groups_within(0); # level 0 only Return the groups that are inside this group, up to the specified level, in arbitrary order. The default level is -1, indicating no bounds and thus all contained groups are returned. A level of 0 means only the direct children, and hence only the toplevel groups will be returned. A level 1 means the toplevel groups and their toplevel children, and so on. =head2 as_txt() my $txt = $group->as_txt(); Returns the group as Graph::Easy textual description. =head2 _find_label_cell() $group->_find_label_cell(); Called by the layouter once for each group. Goes through all cells of this group and finds one where to attach the label to. Internal usage only. =head2 get_attributes() my $att = $object->get_attributes(); Return all effective attributes on this object (graph/node/group/edge) as an anonymous hash ref. This respects inheritance and default values. See also L<raw_attributes()>. =head2 raw_attributes() my $att = $object->get_attributes(); Return all set attributes on this object (graph/node/group/edge) as an anonymous hash ref. This respects inheritance, but does not include default values for unset attributes. See also L<get_attributes()>. =head2 attribute related methods You can call all the various attribute related methods like C<set_attribute()>, C<get_attribute()>, etc. on a group, too. For example: $group->set_attribute('label', 'by train'); my $attr = $group->get_attributes(); You can find more documentation in L<Graph::Easy>. =head2 layout() This routine should not be called on groups, it only works on the graph itself. =head2 shape() my $shape = $group->shape(); Returns the shape of the group as string. =head2 has_as_successor() if ($group->has_as_successor($other)) { ... } Returns true if C<$other> (a node or group) is a successor of this group, e.g. if there is an edge leading from this group to C<$other>. =head2 has_as_predecessor() if ($group->has_as_predecessor($other)) { ... } Returns true if the group has C<$other> (a group or node) as predecessor, that is if there is an edge leading from C<$other> to this group. =head2 root_node() my $root = $group->root_node(); Return the root node as L<Graph::Easy::Node> object, if it was set with the 'root' attribute. =head1 EXPORT None by default. =head1 SEE ALSO L<Graph::Easy>, L<Graph::Easy::Node>, L<Graph::Easy::Manual>. =head1 AUTHOR Copyright (C) 2004 - 2008 by Tels L<http://bloodgate.com> See the LICENSE file for more details. =cut ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/lib/Graph/Easy/Edge.pm��������������������������������������������������������������0000644�0000764�0000764�00000041307�12147675457�017240� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������############################################################################# # An edge connecting two nodes in Graph::Easy. # ############################################################################# package Graph::Easy::Edge; use Graph::Easy::Node; @ISA = qw/Graph::Easy::Node/; # an edge is just a special node $VERSION = '0.31'; use strict; use constant isa_cell => 1; ############################################################################# sub _init { # generic init, override in subclasses my ($self,$args) = @_; $self->{class} = 'edge'; # leave this unitialized until we need it # $self->{cells} = [ ]; foreach my $k (sort keys %$args) { if ($k !~ /^(label|name|style)\z/) { require Carp; Carp::confess ("Invalid argument '$k' passed to Graph::Easy::Node->new()"); } my $n = $k; $n = 'label' if $k eq 'name'; $self->{att}->{$n} = $args->{$k}; } $self; } ############################################################################# # accessor methods sub bidirectional { my $self = shift; if (@_ > 0) { my $old = $self->{bidirectional} || 0; $self->{bidirectional} = $_[0] ? 1 : 0; # invalidate layout? $self->{graph}->{score} = undef if $old != $self->{bidirectional} && ref($self->{graph}); } $self->{bidirectional}; } sub undirected { my $self = shift; if (@_ > 0) { my $old = $self->{undirected} || 0; $self->{undirected} = $_[0] ? 1 : 0; # invalidate layout? $self->{graph}->{score} = undef if $old != $self->{undirected} && ref($self->{graph}); } $self->{undirected}; } sub has_ports { my $self = shift; my $s_port = $self->{att}->{start} || $self->attribute('start'); return 1 if $s_port ne ''; my $e_port = $self->{att}->{end} || $self->attribute('end'); return 1 if $e_port ne ''; 0; } sub start_port { # return the side and portnumber if the edge has a shared source port # undef for none my $self = shift; my $s = $self->{att}->{start} || $self->attribute('start'); return undef if !defined $s || $s !~ /,/; # "south, 0" => ok, "south" => no return (split /\s*,\s*/, $s) if wantarray; $s =~ s/\s+//g; # remove spaces to normalize "south, 0" to "south,0" $s; } sub end_port { # return the side and portnumber if the edge has a shared source port # undef for none my $self = shift; my $s = $self->{att}->{end} || $self->attribute('end'); return undef if !defined $s || $s !~ /,/; # "south, 0" => ok, "south" => no return split /\s*,\s*/, $s if wantarray; $s =~ s/\s+//g; # remove spaces to normalize "south, 0" to "south,0" $s; } sub style { my $self = shift; $self->{att}->{style} || $self->attribute('style'); } sub name { # returns actually the label my $self = shift; $self->{att}->{label} || ''; } ############################################################################# # cell management - used by the cell-based layouter sub _cells { # return all the cells this edge currently occupies my $self = shift; $self->{cells} = [] unless defined $self->{cells}; @{$self->{cells}}; } sub _clear_cells { # remove all belonging cells my $self = shift; $self->{cells} = []; $self; } sub _unplace { # Take an edge, and remove all the cells it covers from the cells area my ($self, $cells) = @_; print STDERR "# clearing path from $self->{from}->{name} to $self->{to}->{name}\n" if $self->{debug}; for my $key (@{$self->{cells}}) { # XXX TODO: handle crossed edges differently (from CROSS => HOR or VER) # free in our cells area delete $cells->{$key}; } $self->clear_cells(); $self; } sub _distance { # estimate the distance from SRC to DST node my ($self) = @_; my $src = $self->{from}; my $dst = $self->{to}; # one of them not yet placed? return 100000 unless defined $src->{x} && defined $dst->{x}; my $cells = $self->{graph}->{cells}; # get all the starting positions # distance = 1: slots, generate starting types, the direction is shifted # by 90° counter-clockwise my @start = $src->_near_places($cells, 1, undef, undef, $src->_shift(-90) ); # potential stop positions my @stop = $dst->_near_places($cells, 1); # distance = 1: slots my ($s_p,@ss_p) = $self->port('start'); my ($e_p,@ee_p) = $self->port('end'); # the edge has a port description, limiting the start places @start = $src->_allowed_places( \@start, $src->_allow( $s_p, @ss_p ), 3) if defined $s_p; # the edge has a port description, limiting the stop places @stop = $dst->_allowed_places( \@stop, $dst->_allow( $e_p, @ee_p ), 3) if defined $e_p; my $stop = scalar @stop; return 0 unless @stop > 0 && @start > 0; # no free slots on one node? my $lowest; my $i = 0; while ($i < scalar @start) { my $sx = $start[$i]; my $sy = $start[$i+1]; $i += 2; # for each start point, calculate the distance to each stop point, then use # the smallest as value for (my $u = 0; $u < $stop; $u += 2) { my $dist = Graph::Easy::_astar_distance($sx,$sy, $stop[$u], $stop[$u+1]); $lowest = $dist if !defined $lowest || $dist < $lowest; } } $lowest; } sub _add_cell { # add a cell to the list of cells this edge covers. If $after is a ref # to a cell, then the new cell will be inserted right after this cell. # if after is defined, but not a ref, the new cell will be inserted # at the specified position. my ($self, $cell, $after, $before) = @_; $self->{cells} = [] unless defined $self->{cells}; my $cells = $self->{cells}; # if both are defined, but belong to different edges, just ignore $before: $before = undef if ref($before) && $before->{edge} != $self; $after = undef if ref($after) && $after->{edge} != $self; if (!defined $after && ref($before)) { $after = $before; $before = undef; } if (defined $after) { # insert the new cell right after $after my $ofs = $after; if (ref($after) && !ref($before)) { # insert after $after $ofs = 1; for my $cell (@$cells) { last if $cell == $after; $ofs++; } } elsif (ref($after) && ref($before)) { # insert between after and before (or before/after for "reversed edges) $ofs = 0; my $found = 0; while ($ofs < scalar @$cells - 1) # 0,1,2,3 => 0 .. 2 { my $c1 = $cells->[$ofs]; my $c2 = $cells->[$ofs+1]; $ofs++; $found++, last if (($c1 == $after && $c2 == $before) || ($c1 == $before && $c2 == $after)); } if (!$found) { # XXX TODO: last effort # insert after $after $ofs = 1; for my $cell (@$cells) { last if $cell == $after; $ofs++; } $found++; } $self->_croak("Could not find $after and $before") unless $found; } splice (@$cells, $ofs, 0, $cell); } else { # insert new cell at the end push @$cells, $cell; } $cell->_update_boundaries(); $self; } ############################################################################# sub from { my $self = shift; $self->{from}; } sub to { my $self = shift; $self->{to}; } sub nodes { my $self = shift; ($self->{from}, $self->{to}); } sub start_at { # move the edge's start point from the current node to the given node my ($self, $node) = @_; # if not a node yet, or not part of this graph, make into one proper node $node = $self->{graph}->add_node($node); $self->_croak("start_at() needs a node object, but got $node") unless ref($node) && $node->isa('Graph::Easy::Node'); # A => A => nothing to do return $node if $self->{from} == $node; # delete self at A delete $self->{from}->{edges}->{ $self->{id} }; # set "from" to B $self->{from} = $node; # add to B $self->{from}->{edges}->{ $self->{id} } = $self; # invalidate layout $self->{graph}->{score} = undef if ref($self->{graph}); # return new start point $node; } sub end_at { # move the edge's end point from the current node to the given node my ($self, $node) = @_; # if not a node yet, or not part of this graph, make into one proper node $node = $self->{graph}->add_node($node); $self->_croak("start_at() needs a node object, but got $node") unless ref($node) && $node->isa('Graph::Easy::Node'); # A => A => nothing to do return $node if $self->{to} == $node; # delete self at A delete $self->{to}->{edges}->{ $self->{id} }; # set "to" to B $self->{to} = $node; # add to node B $self->{to}->{edges}->{ $self->{id} } = $self; # invalidate layout $self->{graph}->{score} = undef if ref($self->{graph}); # return new end point $node; } sub edge_flow { # return the flow at this edge or '' if the edge itself doesn't have a flow my $self = shift; # our flow comes from ourselves my $flow = $self->{att}->{flow}; $flow = $self->raw_attribute('flow') unless defined $flow; $flow; } sub flow { # return the flow at this edge (including inheriting flow from node) my ($self) = @_; # print STDERR "# flow from $self->{from}->{name} to $self->{to}->{name}\n"; # our flow comes from ourselves my $flow = $self->{att}->{flow}; # or maybe our class $flow = $self->raw_attribute('flow') unless defined $flow; # if the edge doesn't have a flow, maybe the node has a default out flow $flow = $self->{from}->{att}->{flow} if !defined $flow; # if that didn't work out either, use the parents flows $flow = $self->parent()->attribute('flow') if !defined $flow; # or finally, the default "east": $flow = 90 if !defined $flow; # absolute flow does not depend on the in-flow, so can return early return $flow if $flow =~ /^(0|90|180|270)\z/; # in-flow comes from our "from" node my $in = $self->{from}->flow(); # print STDERR "# in: $self->{from}->{name} = $in\n"; my $out = $self->{graph}->_flow_as_direction($in,$flow); $out; } sub port { my ($self, $which) = @_; $self->_croak("'$which' must be one of 'start' or 'end' in port()") unless $which =~ /^(start|end)/; # our flow comes from ourselves my $sp = $self->attribute($which); return (undef,undef) unless defined $sp && $sp ne ''; my ($side, $port) = split /\s*,\s*/, $sp; # if absolut direction, return as is my $s = Graph::Easy->_direction_as_side($side); if (defined $s) { my @rc = ($s); push @rc, $port if defined $port; return @rc; } # in_flow comes from our "from" node my $in = 90; $in = $self->{from}->flow() if ref($self->{from}); # turn left in "south" etc: $s = Graph::Easy->_flow_as_side($in,$side); my @rc = ($s); push @rc, $port if defined $port; @rc; } sub flip { # swap from and to for this edge my ($self) = @_; ($self->{from}, $self->{to}) = ($self->{to}, $self->{from}); # invalidate layout $self->{graph}->{score} = undef if ref($self->{graph}); $self; } sub as_ascii { my ($self, $x,$y) = @_; # invisible nodes, or very small ones return '' if $self->{w} == 0 || $self->{h} == 0; my $fb = $self->_framebuffer($self->{w}, $self->{h}); ########################################################################### # "draw" the label into the framebuffer (e.g. the edge and the text) $self->_draw_label($fb, $x, $y, ''); join ("\n", @$fb); } sub as_txt { require Graph::Easy::As_ascii; _as_txt(@_); } 1; __END__ =head1 NAME Graph::Easy::Edge - An edge (a path connecting one ore more nodes) =head1 SYNOPSIS use Graph::Easy; my $ssl = Graph::Easy::Edge->new( label => 'encrypted connection', style => 'solid', ); $ssl->set_attribute('color', 'red'); my $src = Graph::Easy::Node->new('source'); my $dst = Graph::Easy::Node->new('destination'); $graph = Graph::Easy->new(); $graph->add_edge($src, $dst, $ssl); print $graph->as_ascii(); =head1 DESCRIPTION A C<Graph::Easy::Edge> represents an edge between two (or more) nodes in a simple graph. Each edge has a direction (from source to destination, or back and forth), plus a style (line width and style), colors etc. It can also have a label, e.g. a text associated with it. During the layout phase, each edge also contains a list of path-elements (also called cells), which make up the path from source to destination. =head1 METHODS =head2 error() $last_error = $edge->error(); $cvt->error($error); # set new messags $cvt->error(''); # clear error Returns the last error message, or '' for no error. =head2 as_ascii() my $ascii = $edge->as_ascii(); Returns the edge as a little ascii representation. =head2 as_txt() my $txt = $edge->as_txt(); Returns the edge as a little Graph::Easy textual representation. =head2 label() my $label = $edge->label(); Returns the label (also known as 'name') of the edge. =head2 name() my $label = $edge->name(); To make the interface more consistent, the C<name()> method of an edge can also be called, and it will returned either the edge label, or the empty string if the edge doesn't have a label. =head2 style() my $style = $edge->style(); Returns the style of the edge, like 'solid', 'dotted', 'double', etc. =head2 nodes() my @nodes = $edge->nodes(); Returns the source and target node that this edges connects as objects. =head2 bidirectional() $edge->bidirectional(1); if ($edge->bidirectional()) { } Returns true if the edge is bidirectional, aka has arrow heads on both ends. An optional parameter will set the bidirectional status of the edge. =head2 undirected() $edge->undirected(1); if ($edge->undirected()) { } Returns true if the edge is undirected, aka has now arrow at all. An optional parameter will set the undirected status of the edge. =head2 has_ports() if ($edge->has_ports()) { ... } Return true if the edge has restriction on the starting or ending port, e.g. either the C<start> or C<end> attribute is set on this edge. =head2 start_port() my $port = $edge->start_port(); Return undef if the edge does not have a fixed start port, otherwise returns the port as "side, number", for example "south, 0". =head2 end_port() my $port = $edge->end_port(); Return undef if the edge does not have a fixed end port, otherwise returns the port as "side, number", for example "south, 0". =head2 from() my $from = $edge->from(); Returns the node that this edge starts at. See also C<to()>. =head2 to() my $to = $edge->to(); Returns the node that this edge leads to. See also C<from()>. =head2 start_at() $edge->start_at($other); my $other = $edge->start_at('some node'); Set the edge's start point to the given node. If given a node name, will add that node to the graph first. Returns the new edge start point node. =head2 end_at() $edge->end_at($other); my $other = $edge->end_at('some other node'); Set the edge's end point to the given node. If given a node name, will add that node to the graph first. Returns the new edge end point node. =head2 flip() $edge->flip(); Swaps the C<start> and C<end> nodes on this edge, e.g. reverses the direction of the edge. X<transpose> =head2 flow() my $flow = $edge->flow(); Returns the flow for this edge, honoring inheritance. An edge without a specific flow set will inherit the flow from the node it comes from. =head2 edge_flow() my $flow = $edge->edge_flow(); Returns the flow for this edge, or undef if it has none set on either the object itself or its class. =head2 port() my ($side, $number) = $edge->port('start'); my ($side, $number) = $edge->port('end'); Return the side and port number where this edge starts or ends. Returns undef for $side if the edge has no port restriction. The returned side will be one absolute direction of C<east>, C<west>, C<north> or C<south>, depending on the port restriction and flow at that edge. =head2 get_attributes() my $att = $object->get_attributes(); Return all effective attributes on this object (graph/node/group/edge) as an anonymous hash ref. This respects inheritance and default values. See also L<raw_attributes()>. =head2 raw_attributes() my $att = $object->get_attributes(); Return all set attributes on this object (graph/node/group/edge) as an anonymous hash ref. This respects inheritance, but does not include default values for unset attributes. See also L<get_attributes()>. =head2 attribute related methods You can call all the various attribute related methods like C<set_attribute()>, C<get_attribute()>, etc. on an edge, too. For example: $edge->set_attribute('label', 'by train'); my $attr = $edge->get_attributes(); my $raw_attr = $edge->raw_attributes(); You can find more documentation in L<Graph::Easy>. =head1 EXPORT None by default. =head1 SEE ALSO L<Graph::Easy>. =head1 AUTHOR Copyright (C) 2004 - 2008 by Tels L<http://bloodgate.com>. See the LICENSE file for more details. =cut �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/lib/Graph/Easy/Node/����������������������������������������������������������������0000755�0000764�0000764�00000000000�12150110221�016655� 5����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/lib/Graph/Easy/Node/Cell.pm���������������������������������������������������������0000644�0000764�0000764�00000004200�12147675472�020124� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������############################################################################# # (c) by Tels 2004 - 2005. An empty filler cell. Part of Graph::Easy. # ############################################################################# package Graph::Easy::Node::Cell; use Graph::Easy::Node; @ISA = qw/Graph::Easy::Node/; $VERSION = '0.10'; use strict; ############################################################################# sub _init { # generic init, override in subclasses my ($self,$args) = @_; $self->{class} = ''; $self->{name} = ''; $self->{'x'} = 0; $self->{'y'} = 0; # default: belongs to no node $self->{node} = undef; foreach my $k (sort keys %$args) { if ($k !~ /^(node|graph|x|y)\z/) { require Carp; Carp::confess ("Invalid argument '$k' passed to Graph::Easy::Node::Cell->new()"); } $self->{$k} = $args->{$k}; } $self; } sub _correct_size { my $self = shift; $self->{w} = 0; $self->{h} = 0; $self; } sub node { # return the node this cell belongs to my $self = shift; $self->{node}; } sub as_ascii { ''; } sub as_html { ''; } sub group { my $self = shift; $self->{node}->group(); } 1; __END__ =head1 NAME Graph::Easy::Node::Cell - An empty filler cell =head1 SYNOPSIS use Graph::Easy; use Graph::Easy::Edge; my $graph = Graph::Easy->new(); my $node = $graph->add_node('A'); my $path = Graph::Easy::Node::Cell->new( graph => $graph, node => $node, ); ... print $graph->as_ascii(); =head1 DESCRIPTION A C<Graph::Easy::Node::Cell> is used to reserve a cell in the grid for nodes that occupy more than one cell. You should not need to use this class directly. =head1 METHODS =head2 error() $last_error = $cell->error(); $cvt->error($error); # set new messags $cvt->error(''); # clear error Returns the last error message, or '' for no error. =head2 node() my $node = $cell->node(); Returns the node this filler cell belongs to. =head1 SEE ALSO L<Graph::Easy>. =head1 AUTHOR Copyright (C) 2004 - 2005 by Tels L<http://bloodgate.com>. See the LICENSE file for more details. =cut ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/lib/Graph/Easy/Node/Empty.pm��������������������������������������������������������0000644�0000764�0000764�00000002306�11675050456�020342� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������############################################################################# # An empty, borderless cell. Part of Graph::Easy. # ############################################################################# package Graph::Easy::Node::Empty; use Graph::Easy::Node; @ISA = qw/Graph::Easy::Node/; $VERSION = '0.06'; use strict; ############################################################################# sub _init { # generic init, override in subclasses my ($self,$args) = @_; $self->SUPER::_init($args); $self->{class} = 'node.empty'; $self; } sub _correct_size { my $self = shift; $self->{w} = 3; $self->{h} = 3; $self; } 1; __END__ =head1 NAME Graph::Easy::Node::Empty - An empty, borderless cell in a node cluster =head1 SYNOPSIS my $cell = Graph::Easy::Node::Empty->new(); =head1 DESCRIPTION A C<Graph::Easy::Node::Empty> represents a borderless, empty cell in a node cluster. It is mainly used to have an object to render collapsed borders in ASCII output. You should not need to use this class directly. =head1 SEE ALSO L<Graph::Easy::Node>. =head1 AUTHOR Copyright (C) 2004 - 2007 by Tels L<http://bloodgate.com>. See the LICENSE file for more details. =cut ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/lib/Graph/Easy/Node/Anon.pm���������������������������������������������������������0000644�0000764�0000764�00000003213�11675050456�020135� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������############################################################################# # (c) by Tels 2004. Part of Graph::Easy. An anonymous (invisible) node. # ############################################################################# package Graph::Easy::Node::Anon; use Graph::Easy::Node; @ISA = qw/Graph::Easy::Node/; $VERSION = '0.11'; use strict; sub _init { my $self = shift; $self->SUPER::_init(@_); $self->{name} = '#' . $self->{id}; $self->{class} = 'node.anon'; $self->{att}->{label} = ' '; $self; } sub _correct_size { my $self = shift; $self->{w} = 3; $self->{h} = 3; $self; } sub attributes_as_txt { my $self = shift; $self->SUPER::attributes_as_txt( { node => { label => undef, shape => undef, class => undef, } } ); } sub as_pure_txt { '[ ]'; } sub _as_part_txt { '[ ]'; } sub as_txt { my $self = shift; '[ ]' . $self->attributes_as_txt(); } sub text_styles_as_css { ''; } sub is_anon { # is an anon node 1; } 1; __END__ =head1 NAME Graph::Easy::Node::Anon - An anonymous, invisible node in Graph::Easy =head1 SYNOPSIS use Graph::Easy::Node::Anon; my $anon = Graph::Easy::Node::Anon->new(); =head1 DESCRIPTION A C<Graph::Easy::Node::Anon> represents an anonymous, invisible node. These can be used to let edges start and end "nowhere". The syntax in the Graph::Easy textual description language looks like this: [ ] -> [ Bonn ] -> [ ] =head1 EXPORT None by default. =head1 SEE ALSO L<Graph::Easy::Node>. =head1 AUTHOR Copyright (C) 2004 - 2006 by Tels L<http://bloodgate.com>. See the LICENSE file for more details. =cut �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/lib/Graph/Easy/As_vcg.pm������������������������������������������������������������0000644�0000764�0000764�00000034654�12150075413�017561� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������############################################################################# # Output the graph as VCG or GDL text. # ############################################################################# package Graph::Easy::As_vcg; $VERSION = '0.05'; ############################################################################# ############################################################################# package Graph::Easy; use strict; my $vcg_remap = { node => { align => \&_vcg_remap_align, autolabel => undef, autolink => undef, autotitle => undef, background => undef, basename => undef, class => undef, colorscheme => undef, columns => undef, flow => undef, fontsize => undef, format => undef, group => undef, id => undef, link => undef, linkbase => undef, offset => undef, origin => undef, pointstyle => undef, rank => 'level', rotate => undef, rows => undef, shape => \&_vcg_remap_shape, size => undef, textstyle => undef, textwrap => undef, title => undef, }, edge => { color => 'color', # this entry overrides 'all'! align => undef, arrowshape => undef, arrowstyle => undef, autojoin => undef, autolabel => undef, autolink => undef, autosplit => undef, autotitle => undef, border => undef, bordercolor => undef, borderstyle => undef, borderwidth => undef, colorscheme => undef, end => undef, fontsize => undef, format => undef, id => undef, labelcolor => 'textcolor', link => undef, linkbase => undef, minlen => undef, start => undef, # XXX TODO: remap unknown styles style => 'linestyle', textstyle => undef, textwrap => undef, title => undef, }, graph => { align => \&_vcg_remap_align, flow => \&_vcg_remap_flow, label => 'title', type => undef, }, group => { }, all => { background => undef, color => 'textcolor', comment => undef, fill => 'color', font => 'fontname', }, always => { }, # this routine will handle all custom "x-dot-..." attributes x => \&_remap_custom_vcg_attributes, }; sub _remap_custom_vcg_attributes { my ($self, $name, $value) = @_; # drop anything that is not starting with "x-vcg-..." return (undef,undef) unless $name =~ /^x-vcg-/; $name =~ s/^x-vcg-//; # "x-vcg-foo" => "foo" ($name,$value); } my $vcg_shapes = { rect => 'box', diamond => 'rhomb', triangle => 'triangle', invtriangle => 'triangle', ellipse => 'ellipse', circle => 'circle', hexagon => 'hexagon', trapezium => 'trapeze', invtrapezium => 'uptrapeze', invparallelogram => 'lparallelogram', parallelogram => 'rparallelogram', }; sub _vcg_remap_shape { my ($self, $name, $shape) = @_; return ('invisible','yes') if $shape eq 'invisible'; ('shape', $vcg_shapes->{$shape} || 'box'); } sub _vcg_remap_align { my ($self, $name, $style) = @_; # center => center, left => left_justify, right => right_justify $style .= '_justify' unless $style eq 'center'; ('textmode', $style); } my $vcg_flow = { 'south' => 'top_to_bottom', 'north' => 'bottom_to_top', 'down' => 'top_to_bottom', 'up' => 'bottom_to_top', 'east' => 'left_to_right', 'west' => 'right_to_left', 'right' => 'left_to_right', 'left' => 'right_to_left', }; sub _vcg_remap_flow { my ($self, $name, $style) = @_; ('orientation', $vcg_flow->{$style} || 'top_to_bottom'); } sub _class_attributes_as_vcg { # convert a hash with attribute => value mappings to a string my ($self, $a, $class) = @_; my $att = ''; $class = '' if $class eq 'graph'; $class .= '.' if $class ne ''; # create the attributes as text: for my $atr (sort keys %$a) { my $v = $a->{$atr}; $v =~ s/"/\\"/g; # '2"' => '2\"' $v = '"' . $v . '"' unless $v =~ /^[0-9]+\z/; # 1, "1a" $att .= " $class$atr: $v\n"; } $att =~ s/,\s$//; # remove last "," $att = "\n$att" unless $att eq ''; $att; } ############################################################################# sub _generate_vcg_edge { # Given an edge, generate the VCG code for it my ($self, $e, $indent) = @_; # skip links from/to groups, these will be done later return '' if $e->{from}->isa('Graph::Easy::Group') || $e->{to}->isa('Graph::Easy::Group'); my $edge_att = $e->attributes_as_vcg(); $e->{_p} = undef; # mark as processed " edge:$edge_att\n"; # return edge text } use Graph::Easy::Util qw(ord_values); sub _as_vcg { my ($self) = @_; # convert the graph to a textual representation # does not need a layout() beforehand! # gather all edge classes to build the classname attribute from them: $self->{_vcg_edge_classes} = {}; for my $e (ord_values ( $self->{edges} )) { my $class = $e->sub_class(); $self->{_vcg_edge_classes}->{$class} = undef if defined $class && $class ne ''; } # sort gathered class names and map them to integers my $class_names = ''; if (keys %{$self->{_vcg_edge_classes}} > 0) { my $i = 1; $class_names = "\n"; for my $ec (sort keys %{$self->{_vcg_edge_classes}}) { $self->{_vcg_edge_classes}->{$ec} = $i; # remember mapping $class_names .= " classname $i: \"$ec\"\n"; $i++; } } # generate the class attributes first my $label = $self->label(); my $t = ''; $t = "\n title: \"$label\"" if $label ne ''; my $txt = "graph: {$t\n\n" . " // Generated by Graph::Easy $Graph::Easy::VERSION" . " at " . scalar localtime() . "\n" . $class_names; my $groups = $self->groups(); # to keep track of invisible helper nodes $self->{_vcg_invis} = {}; # name for invisible helper nodes $self->{_vcg_invis_id} = 'joint0'; my $atts = $self->{att}; # insert the class attributes for my $class (qw/edge graph node/) { next if $class =~ /\./; # skip subclasses my $out = $self->_remap_attributes( $class, $atts->{$class}, $vcg_remap, 'noquote'); $txt .= $self->_class_attributes_as_vcg($out, $class); } $txt .= "\n" if $txt ne ''; # insert newline ########################################################################### # output groups as subgraphs # insert the edges into the proper group $self->_edges_into_groups() if $groups > 0; # output the groups (aka subclusters) my $indent = ' '; for my $group (sort { $a->{name} cmp $b->{name} } values %{$self->{groups}}) { # quote special chars in group name my $name = $group->{name}; $name =~ s/([\[\]\(\)\{\}\#"])/\\$1/g; # # output group attributes first # $txt .= " subgraph \"cluster$group->{id}\" {\n${indent}label=\"$name\";\n"; # Make a copy of the attributes, including our class attributes: my $copy = {}; my $attribs = $group->get_attributes(); for my $a (keys %$attribs) { $copy->{$a} = $attribs->{$a}; } # # set some defaults # $copy->{'borderstyle'} = 'solid' unless defined $copy->{'borderstyle'}; my $out = {}; # my $out = $self->_remap_attributes( $group->class(), $copy, $vcg_remap, 'noquote'); # Set some defaults: $out->{fillcolor} = '#a0d0ff' unless defined $out->{fillcolor}; # $out->{labeljust} = 'l' unless defined $out->{labeljust}; my $att = ''; # we need to output style first ("filled" and "color" need come later) for my $atr (reverse sort keys %$out) { my $v = $out->{$atr}; $v = '"' . $v . '"'; $att .= " $atr: $v\n"; } $txt .= $att . "\n" if $att ne ''; # # output nodes (w/ or w/o attributes) in that group # for my $n ($group->sorted_nodes()) # { # my $att = $n->attributes_as_vcg(); # $n->{_p} = undef; # mark as processed # $txt .= $indent . $n->as_graphviz_txt() . $att . "\n"; # } # # output node connections in this group # for my $e (ord_values ( $group->{edges} )) # { # next if exists $e->{_p}; # $txt .= $self->_generate_edge($e, $indent); # } $txt .= " }\n"; } my $root = $self->attribute('root'); $root = '' unless defined $root; my $count = 0; # output nodes with attributes first, sorted by their name for my $n (sort { $a->{name} cmp $b->{name} } values %{$self->{nodes}}) { next if exists $n->{_p}; my $att = $n->attributes_as_vcg($root); if ($att ne '') { $n->{_p} = undef; # mark as processed $count++; $txt .= " node:" . $att . "\n"; } } $txt .= "\n" if $count > 0; # insert a newline my @nodes = $self->sorted_nodes(); foreach my $n (@nodes) { my @out = $n->successors(); my $first = $n->as_vcg_txt(); if ((@out == 0) && ( (scalar $n->predecessors() || 0) == 0)) { # single node without any connections (unless already output) $txt .= " node: { title: " . $first . " }\n" unless exists $n->{_p}; } # for all outgoing connections foreach my $other (reverse @out) { # in case there is more than one edge going from N to O my @edges = $n->edges_to($other); foreach my $e (@edges) { next if exists $e->{_p}; $txt .= $self->_generate_vcg_edge($e, ' '); } } } # insert now edges between groups (clusters/subgraphs) # foreach my $e (ord_values ( $self->{edges} )) # { # $txt .= $self->_generate_group_edge($e, ' ') # if $e->{from}->isa('Graph::Easy::Group') || # $e->{to}->isa('Graph::Easy::Group'); # } # clean up for my $n ( ord_values ( $self->{nodes} ), ord_values ( $self->{edges} )) { delete $n->{_p}; } delete $self->{_vcg_invis}; # invisible helper nodes for joints delete $self->{_vcg_invis_id}; # invisible helper node name delete $self->{_vcg_edge_classes}; $txt . "\n}\n"; # close the graph } package Graph::Easy::Node; sub attributes_as_vcg { # return the attributes of this node as text description my ($self, $root) = @_; $root = '' unless defined $root; my $att = ''; my $class = $self->class(); return '' unless ref $self->{graph}; my $g = $self->{graph}; # get all attributes, excluding the class attributes my $a = $self->raw_attributes(); # add the attributes that are listed under "always": my $attr = $self->{att}; my $base_class = $class; $base_class =~ s/\..*//; my $list = $vcg_remap->{always}->{$class} || $vcg_remap->{always}->{$base_class}; for my $name (@$list) { # for speed, try to look it up directly # look if we have a code ref, if yes, simple set the value to undef # and let the coderef handle it later: if ( ref($vcg_remap->{$base_class}->{$name}) || ref($vcg_remap->{all}->{$name}) ) { $a->{$name} = $attr->{$name}; } else { $a->{$name} = $attr->{$name}; $a->{$name} = $self->attribute($name) unless defined $a->{$name} && $a->{$name} ne 'inherit'; } } $a = $g->_remap_attributes( $self, $a, $vcg_remap, 'noquote'); if ($self->isa('Graph::Easy::Edge')) { $a->{sourcename} = $self->{from}->{name}; $a->{targetname} = $self->{to}->{name}; my $class = $self->sub_class(); $a->{class} = $self->{graph}->{_vcg_edge_classes}->{ $class } if defined $class && $class ne ''; } else { # title: "Bonn" $a->{title} = $self->{name}; } # do not needlessly output labels: delete $a->{label} if !$self->isa('Graph::Easy::Edge') && # not an edge exists $a->{label} && $a->{label} eq $self->{name}; # bidirectional and undirected edges if ($self->{bidirectional}) { delete $a->{dir}; my ($n,$s) = Graph::Easy::_graphviz_remap_arrow_style( $self,'', $self->attribute('arrowstyle')); $a->{arrowhead} = $s; $a->{arrowtail} = $s; } if ($self->{undirected}) { delete $a->{dir}; $a->{arrowhead} = 'none'; $a->{arrowtail} = 'none'; } # borderstyle: double: if (!$self->isa('Graph::Easy::Edge')) { my $style = $self->attribute('borderstyle'); $a->{peripheries} = 2 if $style =~ /^double/; } # For nodes with shape plaintext, set the fillcolor to the background of # the graph/group my $shape = $a->{shape} || 'rect'; if ($class =~ /node/ && $shape eq 'plaintext') { my $p = $self->parent(); $a->{fillcolor} = $p->attribute('fill'); $a->{fillcolor} = 'white' if $a->{fillcolor} eq 'inherit'; } $shape = $self->attribute('shape') unless $self->isa_cell(); # for point-shaped nodes, include the point as label and set width/height if ($shape eq 'point') { require Graph::Easy::As_ascii; # for _u8 and point-style my $style = $self->_point_style( $self->attribute('pointstyle') ); $a->{label} = $style; # for point-shaped invisible nodes, set height/width = 0 $a->{width} = 0, $a->{height} = 0 if $style eq ''; } if ($shape eq 'invisible') { $a->{label} = ' '; } $a->{rank} = '0' if $root ne '' && $root eq $self->{name}; # create the attributes as text: for my $atr (sort keys %$a) { my $v = $a->{$atr}; $v =~ s/"/\\"/g; # '2"' => '2\"' $v = '"' . $v . '"' unless $v =~ /^[0-9]+\z/; # 1, "1a" $att .= "$atr: $v "; } $att =~ s/,\s$//; # remove last "," # generate attribute text if nec. $att = ' { ' . $att . '}' if $att ne ''; $att; } sub as_vcg_txt { # return the node itself (w/o attributes) as VCG representation my $self = shift; my $name = $self->{name}; # escape special chars in name (including doublequote!) $name =~ s/([\[\]\(\)\{\}"])/\\$1/g; # quote: '"' . $name . '"'; } 1; __END__ =head1 NAME Graph::Easy::As_vcg - Generate VCG/GDL text from Graph::Easy object =head1 SYNOPSIS use Graph::Easy; my $graph = Graph::Easy->new(); my $bonn = Graph::Easy::Node->new( name => 'Bonn', ); my $berlin = Graph::Easy::Node->new( name => 'Berlin', ); $graph->add_edge ($bonn, $berlin); print $graph->as_vcg(); This prints something like this: graph: { node: { title: "Bonn" } node: { title: "Berlin" } edge: { sourcename: "Bonn" targetname: "Berlin" } } =head1 DESCRIPTION C<Graph::Easy::As_vcg> contains just the code for converting a L<Graph::Easy|Graph::Easy> object to either a VCG or GDL textual description. Note that the generated format is compatible to C<GDL> aka I<Graph Description Language>. =head1 EXPORT Exports nothing. =head1 SEE ALSO L<Graph::Easy>, L<http://rw4.cs.uni-sb.de/~sander/html/gsvcg1.html>. =head1 AUTHOR Copyright (C) 2004-2008 by Tels L<http://bloodgate.com> See the LICENSE file for information. =cut ������������������������������������������������������������������������������������Graph-Easy-0.73/lib/Graph/Easy/As_txt.pm������������������������������������������������������������0000644�0000764�0000764�00000027036�12147674443�017633� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������############################################################################# # Output an Graph::Easy object as textual description # package Graph::Easy::As_txt; $VERSION = '0.15'; ############################################################################# ############################################################################# package Graph::Easy; use strict; sub _as_txt { my ($self) = @_; # Convert the graph to a textual representation - does not need layout(). $self->_assign_ranks(); # generate the class attributes first my $txt = ''; my $att = $self->{att}; for my $class (sort keys %$att) { my $out = $self->_remap_attributes( $class, $att->{$class}, {}, 'noquote', 'encode' ); my $att = ''; for my $atr (sort keys %$out) { # border is handled special below next if $atr =~ /^border/; $att .= " $atr: $out->{$atr};\n"; } # edges do not have a border if ($class !~ /^edge/) { my $border = $self->border_attribute($class) || ''; # 'solid 1px #000000' =~ /^solid/; # 'solid 1px #000000' =~ /^solid 1px #000000/; $border = '' if $self->default_attribute($class,'border') =~ /^$border/; $att .= " border: $border;\n" if $border ne ''; } if ($att ne '') { # the following makes short, single definitions to fit on one line if ($att !~ /\n.*\n/ && length($att) < 40) { $att =~ s/\n/ /; $att =~ s/^ / /; } else { $att = "\n$att"; } $txt .= "$class {$att}\n"; } } $txt .= "\n" if $txt ne ''; # insert newline my @nodes = $self->sorted_nodes('name','id'); my $count = 0; # output nodes with attributes first, sorted by their name foreach my $n (@nodes) { $n->{_p} = undef; # mark as not yet processed my $att = $n->attributes_as_txt(); if ($att ne '') { $n->{_p} = 1; # mark as processed $count++; $txt .= $n->as_pure_txt() . $att . "\n"; } } $txt .= "\n" if $count > 0; # insert a newline # output groups first, with their nodes foreach my $gn (sort keys %{$self->{groups}}) { my $group = $self->{groups}->{$gn}; $txt .= $group->as_txt(); # marks nodes as processed if nec. $count++; } # XXX TODO: # Output all nodes with rank=0 first, and also follow their successors # What is left will then be done next, with rank=1 etc. # This output order let's us output node chains in compact form as: # [A]->[B]->[C]->[D] # [B]->[E] # instead of having: # [A]->[B] # [B]->[E] # [B]->[C] etc @nodes = $self->sorted_nodes('rank','name'); foreach my $n (@nodes) { my @out = $n->sorted_successors(); my $first = $n->as_pure_txt(); # [ A | B ] if ( defined $n->{autosplit} || ((@out == 0) && ( (scalar $n->predecessors() || 0) == 0))) { # single node without any connections (unless already output) next if exists $n->{autosplit} && !defined $n->{autosplit}; $txt .= $first . "\n" unless defined $n->{_p}; } $first = $n->_as_part_txt(); # [ A.0 ] # for all outgoing connections foreach my $other (@out) { # in case there exists more than one edge from $n --> $other my @edges = $n->edges_to($other); for my $edge (sort { $a->{id} <=> $b->{id} } @edges) { $txt .= $first . $edge->as_txt() . $other->_as_part_txt() . "\n"; } } } foreach my $n (@nodes) { delete $n->{_p}; # clean up } $txt; } ############################################################################# package Graph::Easy::Group; use strict; sub as_txt { my $self = shift; my $n = ''; if (!$self->isa('Graph::Easy::Group::Anon')) { $n = $self->{name}; # quote special chars in name $n =~ s/([\[\]\(\)\{\}\#])/\\$1/g; $n = ' ' . $n; } my $txt = "($n"; $n = $self->{nodes}; $txt .= (keys %$n > 0 ? "\n" : ' '); for my $name ( sort keys %$n ) { $n->{$name}->{_p} = 1; # mark as processed $txt .= ' ' . $n->{$name}->as_pure_txt() . "\n"; } $txt .= ")" . $self->attributes_as_txt() . "\n\n"; # insert all the edges of the group # $txt; } ############################################################################# package Graph::Easy::Node; use strict; sub attributes_as_txt { # return the attributes of this node as text description my ($self, $remap) = @_; # nodes that were autosplit if (exists $self->{autosplit}) { # other nodes are invisible in as_txt: return '' unless defined $self->{autosplit}; # the first one might have had a label set } my $att = ''; my $class = $self->class(); my $g = $self->{graph}; # XXX TODO: remove atttributes that are simple the default attributes my $attributes = $self->{att}; if (exists $self->{autosplit}) { # for the first node in a row of autosplit nodes, we need to create # the correct attributes, e.g. "silver|red|" instead of just silver: my $basename = $self->{autosplit_basename}; $attributes = { }; my $parts = $self->{autosplit_parts}; # gather all possible attribute names, otherwise an attribute set # on only one part (like via "color: |red;" would not show up: my $names = {}; for my $child ($self, @$parts) { for my $k (sort keys %{$child->{att}}) { $names->{$k} = undef; } } for my $k (sort keys %$names) { next if $k eq 'basename'; my $val = $self->{att}->{$k}; $val = '' unless defined $val; my $first = $val; my $not_equal = 0; $val .= '|'; for my $child (@$parts) { # only consider our own autosplit parts (check should not be nec.) # next if !exists $child->{autosplit_basename} || # $child->{autosplit_basename} ne $basename; my $v = $child->{att}->{$k}; $v = '' if !defined $v; $not_equal ++ if $v ne $first; $val .= $v . '|'; } # all parts equal, so do "red|red|red" => "red" $val = $first if $not_equal == 0; $val =~ s/\|+\z/\|/; # "silver|||" => "silver|" $val =~ s/\|\z// if $val =~ /\|.*\|/; # "silver|" => "silver|" # but "red|blue|" => "red|blue" $attributes->{$k} = $val unless $val eq '|'; # skip '|' } $attributes->{basename} = $self->{att}->{basename} if defined $self->{att}->{basename}; } my $new = $g->_remap_attributes( $self, $attributes, $remap, 'noquote', 'encode' ); # For nodes, we do not output their group attribute, since they simple appear # at the right place in the txt: delete $new->{group}; # for groups inside groups, insert their group attribute $new->{group} = $self->{group}->{name} if $self->isa('Graph::Easy::Group') && exists $self->{group}; if (defined $self->{origin}) { $new->{origin} = $self->{origin}->{name}; $new->{offset} = join(',', $self->offset()); } # shorten output for multi-celled nodes # for "rows: 2;" still output "rows: 2;", because it is shorter if (exists $new->{columns}) { $new->{size} = ($new->{columns}||1) . ',' . ($new->{rows}||1); delete $new->{rows}; delete $new->{columns}; # don't output the default size delete $new->{size} if $new->{size} eq '1,1'; } for my $atr (sort keys %$new) { next if $atr =~ /^border/; # handled special $att .= "$atr: $new->{$atr}; "; } if (!$self->isa_cell()) { my $border; if (!exists $self->{autosplit}) { $border = $self->border_attribute(); } else { $border = Graph::Easy::_border_attribute( $attributes->{borderstyle}||'', $attributes->{borderwidth}||'', $attributes->{bordercolor}||''); } # XXX TODO: should do this for all attributes, not only for border # XXX TODO: this seems wrong anyway # don't include default border $border = '' if ref $g && $g->attribute($class,'border') eq $border; $att .= "border: $border; " if $border ne ''; } # if we have a subclass, we probably need to include it my $c = ''; $c = $1 if $class =~ /\.(\w+)/; # but we do not need to include it if our group has a nodeclass attribute $c = '' if ref($self->{group}) && $self->{group}->attribute('nodeclass') eq $c; # include our subclass as attribute $att .= "class: $c; " if $c ne '' && $c ne 'anon'; # generate attribute text if nec. $att = ' { ' . $att . '}' if $att ne ''; $att; } sub _as_part_txt { # for edges, we need the name of the part of the first part, not the entire # autosplit text my $self = shift; my $name = $self->{name}; # quote special chars in name $name =~ s/([\[\]\|\{\}\#])/\\$1/g; '[ ' . $name . ' ]'; } sub as_pure_txt { my $self = shift; if (exists $self->{autosplit} && defined $self->{autosplit}) { my $name = $self->{autosplit}; # quote special chars in name (but not |) $name =~ s/([\[\]\{\}\#])/\\$1/g; return '[ '. $name .' ]' } my $name = $self->{name}; # quote special chars in name $name =~ s/([\[\]\|\{\}\#])/\\$1/g; '[ ' . $name . ' ]'; } sub as_txt { my $self = shift; if (exists $self->{autosplit}) { return '' unless defined $self->{autosplit}; my $name = $self->{autosplit}; # quote special chars in name (but not |) $name =~ s/([\[\]\{\}\#])/\\$1/g; return '[ ' . $name . ' ]' } my $name = $self->{name}; # quote special chars in name $name =~ s/([\[\]\|\{\}\#])/\\$1/g; '[ ' . $name . ' ]' . $self->attributes_as_txt(); } ############################################################################# package Graph::Easy::Edge; my $styles = { solid => '--', dotted => '..', double => '==', 'double-dash' => '= ', dashed => '- ', 'dot-dash' => '.-', 'dot-dot-dash' => '..-', wave => '~~', }; sub _as_txt { my $self = shift; # '- Name ' or '' my $n = $self->{att}->{label}; $n = '' unless defined $n; my $left = ' '; $left = ' <' if $self->{bidirectional}; my $right = '> '; $right = ' ' if $self->{undirected}; my $s = $self->style() || 'solid'; my $style = '--'; # suppress border on edges my $suppress = { all => { label => undef } }; if ($s =~ /^(bold|bold-dash|broad|wide|invisible)\z/) { # output "--> { style: XXX; }" $style = '--'; } else { # output "-->" or "..>" etc $suppress->{all}->{style} = undef; $style = $styles->{ $s }; if (!defined $style) { require Carp; Carp::confess ("Unknown edge style '$s'\n"); } } $n = $style . " $n " if $n ne ''; # make " - " into " - - " $style = $style . $style if $self->{undirected} && substr($style,1,1) eq ' '; # ' - Name -->' or ' --> ' or ' -- ' my $a = $self->attributes_as_txt($suppress) . ' '; $a =~ s/^\s//; $left . $n . $style . $right . $a; } 1; __END__ =head1 NAME Graph::Easy::As_txt - Generate textual description from graph object =head1 SYNOPSIS use Graph::Easy; my $graph = Graph::Easy->new(); my $bonn = Graph::Easy::Node->new( name => 'Bonn', ); my $berlin = Graph::Easy::Node->new( name => 'Berlin', ); $graph->add_edge ($bonn, $berlin); print $graph->as_txt(); # prints something like: # [ Bonn ] -> [ Berlin ] =head1 DESCRIPTION C<Graph::Easy::As_txt> contains just the code for converting a L<Graph::Easy|Graph::Easy> object to a human-readable textual description. =head1 EXPORT Exports nothing. =head1 SEE ALSO L<Graph::Easy>. =head1 AUTHOR Copyright (C) 2004 - 2007 by Tels L<http://bloodgate.com> See the LICENSE file for information. =cut ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/lib/Graph/Easy.pm�������������������������������������������������������������������0000644�0000764�0000764�00000312156�12150110173�016344� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������############################################################################ # Manage, and layout graphs on a flat plane. # ############################################################################# package Graph::Easy; use 5.008002; use Graph::Easy::Base; use Graph::Easy::Attributes; use Graph::Easy::Edge; use Graph::Easy::Group; use Graph::Easy::Group::Anon; use Graph::Easy::Layout; use Graph::Easy::Node; use Graph::Easy::Node::Anon; use Graph::Easy::Node::Empty; use Scalar::Util qw/weaken/; $VERSION = '0.73'; @ISA = qw/Graph::Easy::Base/; use strict; my $att_aliases; use Graph::Easy::Util qw(ord_values); BEGIN { # a few aliases for backwards compatibility *get_attribute = \&attribute; *as_html_page = \&as_html_file; *as_graphviz_file = \&as_graphviz; *as_ascii_file = \&as_ascii; *as_boxart_file = \&as_boxart; *as_txt_file = \&as_txt; *as_vcg_file = \&as_vcg; *as_gdl_file = \&as_gdl; *as_graphml_file = \&as_graphml; # a few aliases for code re-use *_aligned_label = \&Graph::Easy::Node::_aligned_label; *quoted_comment = \&Graph::Easy::Node::quoted_comment; *_un_escape = \&Graph::Easy::Node::_un_escape; *_convert_pod = \&Graph::Easy::Node::_convert_pod; *_label_as_html = \&Graph::Easy::Node::_label_as_html; *_wrapped_label = \&Graph::Easy::Node::_wrapped_label; *get_color_attribute = \&color_attribute; *get_custom_attributes = \&Graph::Easy::Node::get_custom_attributes; *custom_attributes = \&Graph::Easy::Node::get_custom_attributes; $att_aliases = Graph::Easy::_att_aliases(); # backwards compatibility *is_simple_graph = \&is_simple; # compatibility to Graph *vertices = \&nodes; } ############################################################################# sub new { # override new() as to not set the {id} my $class = shift; # called like "new->('[A]->[B]')": if (@_ == 1 && !ref($_[0])) { require Graph::Easy::Parser; my $parser = Graph::Easy::Parser->new(); my $self = eval { $parser->from_text($_[0]); }; if (!defined $self) { $self = Graph::Easy->new( fatal_errors => 0 ); $self->error( 'Error: ' . $parser->error() || 'Unknown error while parsing initial text' ); $self->catch_errors( 0 ); } return $self; } my $self = bless {}, $class; my $args = $_[0]; $args = { @_ } if ref($args) ne 'HASH'; $self->_init($args); } sub DESTROY { my $self = shift; # Be carefull to not delete ->{graph}, these will be cleaned out by # Perl automatically in O(1) time, manual delete is O(N) instead. delete $self->{chains}; # clean out pointers in child-objects so that they can safely be reused for my $n (ord_values ( $self->{nodes} )) { if (ref($n)) { delete $n->{edges}; delete $n->{group}; } } for my $e (ord_values ( $self->{edges} )) { if (ref($e)) { delete $e->{cells}; delete $e->{to}; delete $e->{from}; } } for my $g (ord_values ( $self->{groups} )) { if (ref($g)) { delete $g->{nodes}; delete $g->{edges}; } } } # Attribute overlay for HTML output: my $html_att = { node => { borderstyle => 'solid', borderwidth => '1px', bordercolor => '#000000', align => 'center', padding => '0.2em', 'padding-left' => '0.3em', 'padding-right' => '0.3em', margin => '0.1em', fill => 'white', }, 'node.anon' => { 'borderstyle' => 'none', # ' inherit' to protect the value from being replaced by the one from "node" 'background' => ' inherit', }, graph => { margin => '0.5em', padding => '0.5em', 'empty-cells' => 'show', }, edge => { border => 'none', padding => '0.2em', margin => '0.1em', 'font' => 'monospaced, courier-new, courier, sans-serif', 'vertical-align' => 'bottom', }, group => { 'borderstyle' => 'dashed', 'borderwidth' => '1', 'fontsize' => '0.8em', fill => '#a0d0ff', padding => '0.2em', # XXX TODO: # in HTML, align left is default, so we could omit this: align => 'left', }, 'group.anon' => { 'borderstyle' => 'none', background => 'white', }, }; sub _init { my ($self,$args) = @_; $self->{debug} = 0; $self->{timeout} = 5; # in seconds $self->{strict} = 1; # check attributes strict? $self->{class} = 'graph'; $self->{id} = ''; $self->{groups} = {}; # node objects, indexed by their unique name $self->{nodes} = {}; # edge objects, indexed by unique ID $self->{edges} = {}; $self->{output_format} = 'html'; $self->{_astar_bias} = 0.001; # default classes to use in add_foo() methods $self->{use_class} = { edge => 'Graph::Easy::Edge', group => 'Graph::Easy::Group', node => 'Graph::Easy::Node', }; # Graph::Easy will die, Graph::Easy::Parser::Graphviz will warn $self->{_warn_on_unknown_attributes} = 0; $self->{fatal_errors} = 1; # The attributes of the graph itself, _and_ the class/subclass attributes. # These can share a hash, because: # * {att}->{graph} contains both the graph attributes and the class, since # these are synonymous, it is not possible to have more than one graph. # * 'node', 'group', 'edge' are not valid attributes for a graph, so # setting "graph { node: 1; }" is not possible and can thus not overwrite # the entries from att->{node}. # * likewise for "node.subclass", attribute names never have a "." in them $self->{att} = {}; foreach my $k (sort keys %$args) { if ($k !~ /^(timeout|debug|strict|fatal_errors|undirected)\z/) { $self->error ("Unknown option '$k'"); } if ($k eq 'undirected' && $args->{$k}) { $self->set_attribute('type', 'undirected'); next; } $self->{$k} = $args->{$k}; } binmode(STDERR,'utf8') or die ("Cannot do binmode(STDERR,'utf8'") if $self->{debug}; $self->{score} = undef; $self->randomize(); $self; } ############################################################################# # accessors sub timeout { my $self = shift; $self->{timeout} = $_[0] if @_; $self->{timeout}; } sub debug { my $self = shift; $self->{debug} = $_[0] if @_; $self->{debug}; } sub strict { my $self = shift; $self->{strict} = $_[0] if @_; $self->{strict}; } sub type { # return the type of the graph, "undirected" or "directed" my $self = shift; $self->{att}->{type} || 'directed'; } sub is_simple { # return true if the graph does not have multiedges my $self = shift; my %count; for my $e (ord_values ( $self->{edges} )) { my $id = "$e->{to}->{id},$e->{from}->{id}"; return 0 if exists $count{$id}; $count{$id} = undef; } 1; # found none } sub is_directed { # return true if the graph is directed my $self = shift; $self->attribute('type') eq 'directed' ? 1 : 0; } sub is_undirected { # return true if the graph is undirected my $self = shift; $self->attribute('type') eq 'undirected' ? 1 : 0; } sub id { my $self = shift; $self->{id} = shift if defined $_[0]; $self->{id}; } sub score { my $self = shift; $self->{score}; } sub randomize { my $self = shift; srand(); $self->{seed} = rand(2 ** 31); $self->{seed}; } sub root_node { # Return the root node my $self = shift; my $root = $self->{att}->{root}; $root = $self->{nodes}->{$root} if defined $root; $root; } sub source_nodes { # return nodes with only outgoing edges my $self = shift; my @roots; for my $node (ord_values ( $self->{nodes} )) { push @roots, $node if (keys %{$node->{edges}} != 0) && !$node->has_predecessors(); } @roots; } sub predecessorless_nodes { # return nodes with no incoming (but maybe outgoing) edges my $self = shift; my @roots; for my $node (ord_values ( $self->{nodes} )) { push @roots, $node if (keys %{$node->{edges}} == 0) || !$node->has_predecessors(); } @roots; } sub label { my $self = shift; my $label = $self->{att}->{graph}->{label}; $label = '' unless defined $label; $label = $self->_un_escape($label) if !$_[0] && $label =~ /\\[EGHNT]/; $label; } sub link { # return the link, build from linkbase and link (or autolink) my $self = shift; my $link = $self->attribute('link'); my $autolink = ''; $autolink = $self->attribute('autolink') if $link eq ''; if ($link eq '' && $autolink ne '') { $link = $self->{name} if $autolink eq 'name'; # defined to avoid overriding "name" with the non-existant label attribute $link = $self->{att}->{label} if $autolink eq 'label' && defined $self->{att}->{label}; $link = $self->{name} if $autolink eq 'label' && !defined $self->{att}->{label}; } $link = '' unless defined $link; # prepend base only if link is relative if ($link ne '' && $link !~ /^([\w]{3,4}:\/\/|\/)/) { $link = $self->attribute('linkbase') . $link; } $link = $self->_un_escape($link) if !$_[0] && $link =~ /\\[EGHNT]/; $link; } sub parent { # return parent object, for graphs that is undef undef; } sub seed { my $self = shift; $self->{seed} = $_[0] if @_ > 0; $self->{seed}; } sub nodes { # return all nodes as objects, in scalar context their count my ($self) = @_; my $n = $self->{nodes}; return scalar keys %$n unless wantarray; # shortcut return ord_values ( $n ); } sub anon_nodes { # return all anon nodes as objects my ($self) = @_; my $n = $self->{nodes}; if (!wantarray) { my $count = 0; for my $node (ord_values ($n)) { $count++ if $node->is_anon(); } return $count; } my @anon = (); for my $node (ord_values ( $n)) { push @anon, $node if $node->is_anon(); } @anon; } sub edges { # Return all the edges this graph contains as objects my ($self) = @_; my $e = $self->{edges}; return scalar keys %$e unless wantarray; # shortcut ord_values ($e); } sub edges_within { # return all the edges as objects my ($self) = @_; my $e = $self->{edges}; return scalar keys %$e unless wantarray; # shortcut ord_values ($e); } sub sorted_nodes { # return all nodes as objects, sorted by $f1 or $f1 and $f2 my ($self, $f1, $f2) = @_; return scalar keys %{$self->{nodes}} unless wantarray; # shortcut $f1 = 'id' unless defined $f1; # sorting on a non-unique field alone will result in unpredictable # sorting order due to hashing $f2 = 'name' if !defined $f2 && $f1 !~ /^(name|id)$/; my $sort; $sort = sub { $a->{$f1} <=> $b->{$f1} } if $f1; $sort = sub { abs($a->{$f1}) <=> abs($b->{$f1}) } if $f1 && $f1 eq 'rank'; $sort = sub { $a->{$f1} cmp $b->{$f1} } if $f1 && $f1 =~ /^(name|title|label)$/; $sort = sub { $a->{$f1} <=> $b->{$f1} || $a->{$f2} <=> $b->{$f2} } if $f2; $sort = sub { abs($a->{$f1}) <=> abs($b->{$f1}) || $a->{$f2} <=> $b->{$f2} } if $f2 && $f1 eq 'rank'; $sort = sub { $a->{$f1} <=> $b->{$f1} || abs($a->{$f2}) <=> abs($b->{$f2}) } if $f2 && $f2 eq 'rank'; $sort = sub { $a->{$f1} <=> $b->{$f1} || $a->{$f2} cmp $b->{$f2} } if $f2 && $f2 =~ /^(name|title|label)$/; $sort = sub { abs($a->{$f1}) <=> abs($b->{$f1}) || $a->{$f2} cmp $b->{$f2} } if $f1 && $f1 eq 'rank' && $f2 && $f2 =~ /^(name|title|label)$/; # 'name', 'id' $sort = sub { $a->{$f1} cmp $b->{$f1} || $a->{$f2} <=> $b->{$f2} } if $f2 && $f2 eq 'id' && $f1 ne 'rank'; # the 'return' here should not be removed return sort $sort values %{$self->{nodes}}; } sub add_edge_once { # add an edge, unless it already exists. In that case it returns undef my ($self, $x, $y, $edge) = @_; # got an edge object? Don't add it twice! return undef if ref($edge); # turn plaintext scalars into objects my $x1 = $self->{nodes}->{$x} unless ref $x; my $y1 = $self->{nodes}->{$y} unless ref $y; # nodes do exist => maybe the edge also exists if (ref($x1) && ref($y1)) { my @ids = $x1->edges_to($y1); return undef if @ids; # found already one edge? } $self->add_edge($x,$y,$edge); } sub edge { # return an edge between two nodes as object my ($self, $x, $y) = @_; # turn plaintext scalars into objects $x = $self->{nodes}->{$x} unless ref $x; $y = $self->{nodes}->{$y} unless ref $y; # node does not exist => edge does not exist return undef unless ref($x) && ref($y); my @ids = $x->edges_to($y); wantarray ? @ids : $ids[0]; } sub flip_edges { # turn all edges going from $x to $y around my ($self, $x, $y) = @_; # turn plaintext scalars into objects $x = $self->{nodes}->{$x} unless ref $x; $y = $self->{nodes}->{$y} unless ref $y; # node does not exist => edge does not exist # if $x == $y, return early (no need to turn selfloops) return $self unless ref($x) && ref($y) && ($x != $y); for my $e (ord_values ( $x->{edges} )) { $e->flip() if $e->{from} == $x && $e->{to} == $y; } $self; } sub node { # return node by name my ($self,$name) = @_; $name = '' unless defined $name; $self->{nodes}->{$name}; } sub rename_node { # change the name of a node my ($self, $node, $new_name) = @_; $node = $self->{nodes}->{$node} unless ref($node); if (!ref($node)) { $node = $self->add_node($new_name); } else { if (!ref($node->{graph})) { # add node to ourself $node->{name} = $new_name; $self->add_node($node); } else { if ($node->{graph} != $self) { $node->{graph}->del_node($node); $node->{name} = $new_name; $self->add_node($node); } else { delete $self->{nodes}->{$node->{name}}; $node->{name} = $new_name; $self->{nodes}->{$node->{name}} = $node; } } } if ($node->is_anon()) { # turn anon nodes into a normal node (since it got a new name): bless $node, $self->{use_class}->{node} || 'Graph::Easy::Node'; delete $node->{att}->{label} if $node->{att}->{label} eq ' '; $node->{class} = 'group'; } $node; } sub rename_group { # change the name of a group my ($self, $group, $new_name) = @_; if (!ref($group)) { $group = $self->add_group($new_name); } else { if (!ref($group->{graph})) { # add node to ourself $group->{name} = $new_name; $self->add_group($group); } else { if ($group->{graph} != $self) { $group->{graph}->del_group($group); $group->{name} = $new_name; $self->add_group($group); } else { delete $self->{groups}->{$group->{name}}; $group->{name} = $new_name; $self->{groups}->{$group->{name}} = $group; } } } if ($group->is_anon()) { # turn anon groups into a normal group (since it got a new name): bless $group, $self->{use_class}->{group} || 'Graph::Easy::Group'; delete $group->{att}->{label} if $group->{att}->{label} eq ''; $group->{class} = 'group'; } $group; } ############################################################################# # attribute handling sub _check_class { # Check the given class ("graph", "node.foo" etc.) or class selector # (".foo") for being valid, and return a list of base classes this applies # to. Handles also a list of class selectors like ".foo, .bar, node.foo". my ($self, $selector) = @_; my @parts = split /\s*,\s*/, $selector; my @classes = (); for my $class (@parts) { # allowed classes, subclasses (except "graph."), selectors (excpet ".") return unless $class =~ /^(\.\w|node|group|edge|graph\z)/; # "node." is invalid, too return if $class =~ /\.\z/; # run a loop over all classes: "node.foo" => ("node"), ".foo" => ("node","edge","group") $class =~ /^(\w*)/; my $base_class = $1; if ($base_class eq '') { push @classes, ('edge'.$class, 'group'.$class, 'node'.$class); } else { push @classes, $class; } } # end for all parts @classes; } sub set_attribute { my ($self, $class_selector, $name, $val) = @_; # allow calling in the style of $graph->set_attribute($name,$val); if (@_ == 3) { $val = $name; $name = $class_selector; $class_selector = 'graph'; } # font-size => fontsize $name = $att_aliases->{$name} if exists $att_aliases->{$name}; $name = 'undef' unless defined $name; $val = 'undef' unless defined $val; my @classes = $self->_check_class($class_selector); return $self->error ("Illegal class '$class_selector' when trying to set attribute '$name' to '$val'") if @classes == 0; for my $class (@classes) { $val = $self->unquote_attribute($class,$name,$val); if ($self->{strict}) { my ($rc, $newname, $v) = $self->validate_attribute($name,$val,$class); return if defined $rc; # error? $val = $v; } $self->{score} = undef; # invalidate layout to force a new layout delete $self->{cache}; # setting a class or flow must invalidate the cache # handle special attribute 'gid' like in "graph { gid: 123; }" if ($class eq 'graph') { if ($name =~ /^g?id\z/) { $self->{id} = $val; } # handle special attribute 'output' like in "graph { output: ascii; }" if ($name eq 'output') { $self->{output_format} = $val; } } my $att = $self->{att}; # create hash if it doesn't exist yet $att->{$class} = {} unless ref $att->{$class}; if ($name eq 'border') { my $c = $att->{$class}; ($c->{borderstyle}, $c->{borderwidth}, $c->{bordercolor}) = $self->split_border_attributes( $val ); return $val; } $att->{$class}->{$name} = $val; } # end for all selected classes $val; } sub set_attributes { my ($self, $class_selector, $att) = @_; # if called as $graph->set_attributes( { color => blue } ), assume # class eq 'graph' if (defined $class_selector && !defined $att) { $att = $class_selector; $class_selector = 'graph'; } my @classes = $self->_check_class($class_selector); return $self->error ("Illegal class '$class_selector' when trying to set attributes") if @classes == 0; foreach my $a (sort keys %$att) { for my $class (@classes) { $self->set_attribute($class, $a, $att->{$a}); } } $self; } sub del_attribute { # delete the attribute with the name in the selected class(es) my ($self, $class_selector, $name) = @_; if (@_ == 2) { $name = $class_selector; $class_selector = 'graph'; } # font-size => fontsize $name = $att_aliases->{$name} if exists $att_aliases->{$name}; my @classes = $self->_check_class($class_selector); return $self->error ("Illegal class '$class_selector' when trying to delete attribute '$name'") if @classes == 0; for my $class (@classes) { my $a = $self->{att}->{$class}; delete $a->{$name}; if ($name eq 'size') { delete $a->{rows}; delete $a->{columns}; } if ($name eq 'border') { delete $a->{borderstyle}; delete $a->{borderwidth}; delete $a->{bordercolor}; } } $self; } ############################################################################# # for determining the absolute graph flow my $p_flow = { 'east' => 90, 'west' => 270, 'north' => 0, 'south' => 180, 'up' => 0, 'down' => 180, 'back' => 270, 'left' => 270, 'right' => 90, 'front' => 90, 'forward' => 90, }; sub flow { # return out flow as number my ($self) = @_; my $flow = $self->{att}->{graph}->{flow}; return 90 unless defined $flow; my $f = $p_flow->{$flow}; $f = $flow unless defined $f; $f; } ############################################################################# ############################################################################# # Output (as_ascii, as_html) routines; as_txt() is in As_txt.pm, as_graphml # is in As_graphml.pm sub output_format { # set the output format my $self = shift; $self->{output_format} = shift if $_[0]; $self->{output_format}; } sub output { # general output routine, to output the graph as the format that was # specified in the graph source itself my $self = shift; no strict 'refs'; my $method = 'as_' . $self->{output_format}; $self->_croak("Cannot find a method to generate '$self->{output_format}'") unless $self->can($method); $self->$method(); } sub _class_styles { # Create the style sheet with the class lists. This is used by both # css() and as_svg(). $skip is a qr// object that returns true for # attribute names to be skipped (e.g. excluded), and $map is a # HASH that contains mapping for attribute names for the output. # "$base" is the basename for classes (either "table.graph$id" if # not defined, or whatever you pass in, like "" for svg). # $indent is a left-indenting spacer like " ". # $overlay contains a HASH with attribute-value pairs to set as defaults. my ($self, $skip, $map, $base, $indent, $overlay) = @_; my $a = $self->{att}; $indent = '' unless defined $indent; my $indent2 = $indent x 2; $indent2 = ' ' if $indent2 eq ''; my $class_list = { edge => {}, node => {}, group => {} }; if (defined $overlay) { $a = {}; # make a copy from $self->{att} to $a: for my $class (sort keys %{$self->{att}}) { my $ac = $self->{att}->{$class}; $a->{$class} = {}; my $acc = $a->{$class}; for my $k (sort keys %$ac) { $acc->{$k} = $ac->{$k}; } } # add the extra keys for my $class (sort keys %$overlay) { my $oc = $overlay->{$class}; # create the hash if it doesn't exist yet $a->{$class} = {} unless ref $a->{$class}; my $acc = $a->{$class}; for my $k (sort keys %$oc) { $acc->{$k} = $oc->{$k} unless exists $acc->{$k}; } $class_list->{$class} = {}; } } my $id = $self->{id}; my @primaries = sort keys %$class_list; foreach my $primary (@primaries) { my $cl = $class_list->{$primary}; # shortcut foreach my $class (sort keys %$a) { if ($class =~ /^$primary\.(.*)/) { $cl->{$1} = undef; # note w/o doubles } } } $base = "table.graph$id " unless defined $base; my $groups = $self->groups(); # do we have groups? my $css = ''; foreach my $class (sort keys %$a) { next if (not %{$a->{$class}}); # skip empty ones my $c = $class; $c =~ s/\./_/g; # node.city => node_city next if $class eq 'group' and $groups == 0; my $css_txt = ''; my $cls = ''; if ($class eq 'graph' && $base eq '') { $css_txt .= "${indent}.$class \{\n"; # for SVG } elsif ($class eq 'graph') { $css_txt .= "$indent$base\{\n"; } else { if ($c !~ /\./) # one of our primary ones { # generate also class list # like: "cities,node_rivers" $cls = join (",$base.${c}_", sort keys %{ $class_list->{$c} }); $cls = ",$base.${c}_$cls" if $cls ne ''; # like: ",node_cities,node_rivers" } $css_txt .= "$indent$base.$c$cls {\n"; } my $done = 0; foreach my $att (sort keys %{$a->{$class}}) { # should be skipped? next if $att =~ $skip || $att eq 'border'; # do not specify attributes for the entire graph (only for the label) # $base ne '' skips this rule for SVG output next if $class eq 'graph' && $base ne '' && $att =~ /^(color|font|fontsize|align|fill)\z/; $done++; # how many did we really? my $val = $a->{$class}->{$att}; next if !defined $val; # for groups, set to none, it will be later overriden for the different # cells (like "ga") with a border only on the appropriate side: $val = 'none' if $att eq 'borderstyle' && $class eq 'group'; # fix border-widths to be in pixel $val .= 'px' if $att eq 'borderwidth' && $val !~ /(px|em|%)\z/; # for color attributes, convert to hex my $entry = $self->_attribute_entry($class, $att); if (defined $entry) { my $type = $entry->[ ATTR_TYPE_SLOT ] || ATTR_STRING; if ($type == ATTR_COLOR) { # create as RGB color $val = $self->get_color_attribute($class,$att) || $val; } } # change attribute name/value? if (exists $map->{$att}) { $att = $map->{$att} unless ref $map->{$att}; # change attribute name? ($att,$val) = &{$map->{$att}}($self,$att,$val,$class) if ref $map->{$att}; } # value is "inherit"? if ($class ne 'graph' && $att && $val && $val eq 'inherit') { # get the value from one class "up" # node.foo => node, node => graph my $base_class = $class; $base_class = 'graph' unless $base_class =~ /\./; $base_class =~ s/\..*//; $val = $a->{$base_class}->{$att}; if ($base_class ne 'graph' && (!defined $val || $val eq 'inherit')) { # node.foo => node, inherit => graph $val = $a->{graph}->{$att}; $att = undef if !defined $val; } } $css_txt .= "$indent2$att: $val;\n" if defined $att && defined $val; } $css_txt .= "$indent}\n"; $css .= $css_txt if $done > 0; # skip if no attributes at all } $css; } sub _skip { # return a regexp that specifies which attributes to suppress in CSS my ($self) = shift; # skip these for CSS qr/^(basename|columns|colorscheme|comment|class|flow|format|group|rows|root|size|offset|origin|linkbase|(auto)?(label|link|title)|auto(join|split)|(node|edge)class|shape|arrowstyle|label(color|pos)|point(style|shape)|textstyle|style)\z/; } ############################################################################# # These routines are used by as_html for the generation of CSS sub _remap_text_wrap { my ($self,$name,$style) = @_; return (undef,undef) if $style ne 'auto'; # make text wrap again ('white-space','normal'); } sub _remap_fill { my ($self,$name,$color,$class) = @_; return ('background',$color) unless $class =~ /edge/; # for edges, the fill is ignored (undef,undef); } ############################################################################# sub css { my $self = shift; my $a = $self->{att}; my $id = $self->{id}; # for each primary class (node/group/edge) we need to find all subclasses, # and list them in the CSS, too. Otherwise "node_city" would not inherit # the attributes from "node". my $css = $self->_class_styles( $self->_skip(), { fill => \&_remap_fill, textwrap => \&_remap_text_wrap, align => 'text-align', font => 'font-family', fontsize => 'font-size', bordercolor => 'border-color', borderstyle => 'border-style', borderwidth => 'border-width', }, undef, undef, $html_att, ); my @groups = $self->groups(); # Set attributes for all TDs that start with "group": $css .= <<CSS table.graph##id## td[class|="group"] { padding: 0.2em; } CSS if @groups > 0; $css .= <<CSS table.graph##id## td { padding: 2px; background: inherit; white-space: nowrap; } table.graph##id## span.l { float: left; } table.graph##id## span.r { float: right; } CSS ; # append CSS for edge cells (and their parts like va (vertical arrow # (left/right), vertical empty), etc) # eb - empty bottom or arrow pointing down/up # el - (vertical) empty left space of ver edge # or empty vertical space on hor edge starts # lh - edge label horizontal # le - edge label, but empty (no label) # lv - edge label vertical # sh - shifted arrow horizontal (shift right) # sa - shifted arrow horizontal (shift left for corners) # shl - shifted arrow horizontal (shift left) # sv - shifted arrow vertical (pointing down) # su - shifted arrow vertical (pointing up) $css .= <<CSS table.graph##id## .va { vertical-align: middle; line-height: 1em; width: 0.4em; } table.graph##id## .el { width: 0.1em; max-width: 0.1em; min-width: 0.1em; } table.graph##id## .lh, table.graph##id## .lv { font-size: 0.8em; padding-left: 0.4em; } table.graph##id## .sv, table.graph##id## .sh, table.graph##id## .shl, table.graph##id## .sa, table.graph##id## .su { max-height: 1em; line-height: 1em; position: relative; top: 0.55em; left: -0.3em; overflow: visible; } table.graph##id## .sv, table.graph##id## .su { max-height: 0.5em; line-height: 0.5em; } table.graph##id## .shl { left: 0.3em; } table.graph##id## .sv { left: -0.5em; top: -0.4em; } table.graph##id## .su { left: -0.5em; top: 0.4em; } table.graph##id## .sa { left: -0.3em; top: 0; } table.graph##id## .eb { max-height: 0; line-height: 0; height: 0; } CSS # if we have edges if keys %{$self->{edges}} > 0; # if we have nodes with rounded shapes: my $rounded = 0; for my $n (ord_values ( $self->{nodes} )) { $rounded ++ and last if $n->shape() =~ /circle|ellipse|rounded/; } $css .= <<CSS table.graph##id## span.c { position: relative; top: 1.5em; } table.graph##id## div.c { -moz-border-radius: 100%; border-radius: 100%; } table.graph##id## div.r { -moz-border-radius: 1em; border-radius: 1em; } CSS if $rounded > 0; # append CSS for group cells (only if we actually have groups) if (@groups > 0) { foreach my $group (@groups) { my $class = $group->class(); my $border = $group->attribute('borderstyle'); $class =~ s/.*\.//; # leave only subclass $css .= Graph::Easy::Group::Cell->_css($self->{id}, $class, $border); } } # replace the id with either '' or '123', depending on our ID $css =~ s/##id##/$id/g; $css; } sub html_page_header { # return the HTML header for as_html_file() my ($self, $css) = @_; my $html = <<HTML <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=##charset##"> <title>##title####CSS## HTML ; $html =~ s/\n\z//; $html =~ s/##charset##/utf-8/g; my $t = $self->title(); $html =~ s/##title##/$t/g; # insert CSS if requested $css = $self->css() unless defined $css; $html =~ s/##CSS##/\n

<graph>-Plugin for Mediawiki

##NAME##

This page was automatically created at ##time## by examples/syntax.pl running Graph::Easy v##version##.

On each of the following testcases you will see the original text representation of the graph, a text representation created automatically from the parsed input, as well the automatically generated HTML+CSS code.

Notes:

Table of Contents:

##TOC##
##HTML##
Graph-Easy-0.73/examples/as_svg0000755000076400007640000000206411675050456016340 0ustar shlomifshlomif#!/usr/bin/perl -w ############################################################################# # This example is a bit outdated, please use the new bin/graph-easy script - # which is after "make install" available on any command line in your system. # Convert an input file containing a Graph::Easy description to # standalone SVG file # Example usage: # examples/as_svg t/in/2nodes.txt >test.svg # echo "[ A ] -> [ B ]" | examples/as_svg BEGIN { $|++; } use strict; use lib 'lib'; use Graph::Easy::Parser; my $file = shift; my $debug = shift; my $parser = Graph::Easy::Parser->new( debug => $debug ); if (!defined $file) { $file = \*STDIN; binmode STDIN, ':utf8' or die ("binmode STDIN, ':utf8' failed: $!"); } binmode STDERR, ':utf8' or die ("binmode STDERR, ':utf8' failed: $!"); my $graph = $parser->from_file( $file ); die ($parser->error()) unless defined $graph; $graph->timeout(360); $graph->layout(); warn ($graph->error()) if $graph->error(); binmode STDOUT, ':utf8' or die ("binmode STDOUT, ':utf8' failed: $!"); print $graph->as_svg_file(); Graph-Easy-0.73/examples/wikicrawl.pl0000644000076400007640000002020111675050456017452 0ustar shlomifshlomif#!/usr/bin/perl use strict; use Graph::Easy; use LWP; use HTML::TokeParser; use utf8; use Getopt::Long; use Encode; use Data::Dumper; my $VERSION = 0.03; # things that shouldn't be looked at my %bad = map { $_ => 1 } qw/ Wikipedia Image Talk Help Template Portal Special User Category Wikipedia Bild Diskussion Hilfe Vorlage Portal Spezial Benutzer Kategorie Wikipédia Image Discuter Modèle Mod%C3%A9le Aide Utilisateur Catégorie Cat%C3%A9gorie /; # do not crawl these: my $skip = qr/\((disambiguation|Begriffsklärung|Homonymie)\)/i; # to figure out redirections my $redir = qr/(Weitergeleitet von|Redirected from|Redirig. depuis).*?title="(.*?)"/i; # the default settings are defined in get_options() # option handling my $help_requested = 0; $help_requested = 1 if @ARGV == 0; my $opt = get_options(); # error? $help_requested = 1 if !ref($opt); # no error and --help was specified $help_requested = 2 if ref($opt) && $opt->{help} ne ''; my $copyright = "wikicrawl v$VERSION (c) by Tels 2008. " ."Released under the GPL 2.0 or later.\n\n" ."After a very cool idea by 'integral' on forum.xkcd.com. Thanx! :)\n\n"; if (ref($opt) && $opt->{version} != 0) { print $copyright; print "Running under Perl v$].\n\n"; exit 2; } if ($help_requested > 0) { print STDERR $copyright; require Pod::Usage; if ($help_requested > 1 && $Pod::Usage::VERSION < 1.35) { # The way old Pod::Usage executes "perldoc" might fail: system('perldoc', $0); exit 2; } Pod::Usage::pod2usage( { -exitval => 2, -verbose => $help_requested } ); } my $verbose = $opt->{verbose}; output ($copyright); my $graph = Graph::Easy->new(); # set some default attributes on the graph $graph->set_attribute('node','shape',$opt->{nodeshape}); $graph->set_attribute('node','font-size','80%'); $graph->set_attribute('edge','arrowstyle','filled'); $graph->set_attribute('graph','label',"Wikipedia map for $opt->{root}"); $graph->set_attribute('graph','font-size', '200%'); $graph->set_attribute('graph','comment', "Created with wikicrawl.pl v$VERSION"); output ("Using the following settings:\n"); print Data::Dumper->Dump([$opt], ['opt']); # don't crawl stuff twice my %visitedLinks; # re-use the UserAgent object my $ua = LWP::UserAgent->new(); #$ua->agent("WikiCrawl/$VERSION - " . $ua->_agent . " - vGraph::Easy $Graph::Easy::VERSION"); # count how many we have done my $nodes = 0; # enable UTF-8 output binmode STDERR, ':utf8'; binmode STDOUT, ':utf8'; # push the first node on the stack my @todo = [$opt->{root},0]; # and work on it (this will take one off and then push more nodes on it) while (@todo && crawl()) { }; my $file = "wikicrawl-$opt->{lang}.txt"; output ("Generating $file:\n"); open(my $DATA, ">", "$file") or die("Could not write to '$file': $!"); binmode ($DATA,':utf8'); print $DATA $graph->as_txt(); close $DATA; output ("All done.\n"); my $png = $file; $png =~ s/.txt/.png/; output ("Generating $png:\n"); `perl -Ilib bin/graph-easy --png --renderer=$opt->{renderer} $file`; output ("All done.\n"); ######################################################################################## # main crawl routine sub crawl { no warnings 'recursion'; # all done? return if @todo == 0; my ($name,$depth) = ($todo[0]->[0],$todo[0]->[1]); shift @todo; my $page = "http://$opt->{lang}.wikipedia.org/wiki/$name"; # limit depth return if $depth + 1 > $opt->{maxdepth}; # already did as many nodes? return if $opt->{maxnodes} > 0 && $nodes > $opt->{maxnodes}; # skip this page return 1 if exists $visitedLinks{$page}; # crawl page my $res = $ua->request(HTTP::Request->new(GET => $page)); return 1 unless $res->is_success(); # remove the " - Wikipedia" (en) or " – Wikipedia" (de) from the title my $title = decode('utf8',$res->title); # convert to UTF-8 $title =~ s/ [–-] Wikip[ée]dia.*//; return 1 if $title =~ $skip; # no disambiguation pages # tels: not sure when/why these happen: print STDERR "# $title ",$res->title()," $page\n" if $title eq ''; output ("Crawling node #$nodes '$title' at depth $depth\n"); $nodes++; # set flag $visitedLinks{$page} = undef; my $content = $res->content; # parse anchors my $parser = HTML::TokeParser->new(\$content) or die("Could not parse page."); # handle redirects: $content = decode('utf-8', $content); $content =~ $redir; my $old = $2; if ($old) { output (" Redirected to '$title' from '$old'\n"); # find the node named "$old" (at the same time adding it if it didn't exist yet) my $source = $graph->add_node($old); # and mention the redirect in the label $source->set_attribute('label', "$old\\n($title)"); # now force edges to come from that node $title = $old; } # iterate over all links for(my $i = 0; (my $token = $parser->get_tag("a")) && ($i < $opt->{maxspread} || $opt->{maxspread} == 0);) { my $url = $token->[1]{href}; my $alt = $token->[1]{title}; next unless defined $url; # we do not crawl these: next if $url !~ m/^\/wiki\//; # no pages outside of wikipedia next if $alt =~ $skip; # no disambiguation pages next if $alt =~ m/\[/; # no brackets my @chunks = split ":", substr(decode('utf-8',$url), 6); # extract special pages, if any next if exists $bad{$chunks[0]}; # no bad pages $i++; if ($title ne $alt) { output (" Adding link from '$title' to '$alt'\n", 1); my ($from,$to,$edge) = $graph->add_edge_once($title,$alt); if (defined $to) { my $old_depth = $to->raw_attribute('rank'); if (!$old_depth) { my $color = sprintf("%i", (360 / $opt->{maxdepth}) * ($depth)); $to->set_attribute('fill', 'hsl(' .$color.',1,0.7)'); # store rank $to->set_attribute('rank', $depth+1); } } } my $u = $url; $u =~ s/^\/wiki\///; push @todo, [$u,$depth+1]; } # continue return 1; } sub get_options { my $opt = {}; $opt->{help} = ''; $opt->{version} = 0; # max depth to crawl $opt->{maxdepth} = 4; # max number of links per node $opt->{maxspread} = 5; # stop after so many nodes, -1 to disable $opt->{maxnodes} = -1; # language $opt->{lang} = 'en'; # root node $opt->{root} = 'Xkcd'; $opt->{renderer} = 'neato'; $opt->{nodeshape} = 'rect'; my @o = ( "language=s" => \$opt->{lang}, "root=s" => \$opt->{root}, "maxdepth=i" => \$opt->{maxdepth}, "maxspread=i" => \$opt->{maxspread}, "maxnodes=i" => \$opt->{maxnodes}, "version" => \$opt->{version}, "help|?" => \$opt->{help}, "verbose" => \$opt->{verbose}, "nodeshape" => \$opt->{nodeshape}, ); return unless Getopt::Long::GetOptions (@o); $opt; } sub output { my ($txt, $level) = @_; $level |= 0; print STDERR $txt if $opt->{verbose} || $level == 0; } =pod =head1 NAME wikicrawl - crawl Wikipedia to generate graph from the found article links =head1 SYNOPSIS Crawl wikipedia and create a L text describing the inter-article links that were found during the crawl. At least one argument must be given to start: perl examples/wikicrawl.pl --lang=fr =head1 ARGUMENTS Here are the options: =over 12 =item --help Print the full documentation, not just this short overview. =item --version Write version info and exit. =item --language Select the language of Wikipedia that we should crawl. Currently supported are 'de', 'en' and 'fr'. Default is 'en'. =item --root Set the root node where the crawl should start. Default is of course 'Xkcd'. =item --maxdepth The maximum depth the crawl should go. Please select small values under 10. Default is 4. =item --maxspread The maximum number of links we follow per article. Please select small values under 10. Default is 5. =item --maxnodes The maximum number of nodes we crawl. Set to -1 (default) to disable. =back =head1 SEE ALSO L and L. =head1 LICENSE This library is free software; you can redistribute it and/or modify it under the terms of the GPL. See the LICENSE file of Graph::Easy for a copy of the GPL. X =head1 AUTHOR Copyright (C) 2008 by integral L Copyright (C) 2008 by Tels L =cut Graph-Easy-0.73/examples/syntax.pl0000644000076400007640000001133611675050456017015 0ustar shlomifshlomif#!/usr/bin/perl -w ############################################################################# # This example is a bit outdated, please use the new bin/grapheasy script - # which is after "make install" available in your system as simple as # "grapheasy" on any command line prompt. ############################################################################# # This script tries to generate graphs from all the files in t/syntax/ # and outputs the result as an HTML page. # Use it like: # examples/syntax.pl >test.html # and then open test.html in your favourite browser. BEGIN { chdir 'examples' if -d 'examples'; use lib '../lib'; } use strict; use warnings; use Graph::Easy::Parser; my $parser = Graph::Easy::Parser->new( debug => 0); my ($name, $template, $sep, @dirs) = @ARGV; $name = 'Graph::Easy Test page' unless $name; $template = 'syntax.tpl' unless $template; my @toc = (); open FILE, $template or die ("Cannot read 'syntax.tpl': $!"); local $/ = undef; my $html = ; close FILE; my $output = ''; my $ID = '0'; # generate the parts and push their names into @toc gen_graphs($parser, @dirs); my $toc = '
    '; for my $t (@toc) { $toc .= "
  • $t->[1]\n"; } $toc .= "
\n"; # insert the TOC $html =~ s/##TOC##/ $toc /; $html =~ s/##NAME##/ $name /; $html =~ s/##HTML##/ $output /; $html =~ s/##time##/ scalar localtime() /eg; $html =~ s/##version##/$Graph::Easy::VERSION/eg; print $html; # all done; 1; ############################################################################# sub gen_graphs { # for all files in a dir, generate a graph from it my $parser = shift; @dirs = qw/syntax stress/ unless @dirs; foreach my $dir (@dirs) { _for_all_files($parser, $dir); } } sub _for_all_files { my ($parser, $dir) = @_; opendir DIR, "../t/$dir" or die ("Cannot read dir '../t/$dir': $!"); my @files = readdir DIR; closedir DIR; foreach my $file (sort @files) { my $f = "../t/$dir/" . $file; next unless -f $f; # not a file? print STDERR "# at file $f\n"; open FILE, "$f" or die ("Cannot read '$f': $!"); local $/ = undef; my $input = ; close FILE; my $graph = $parser->from_text( $input ); if (!defined $graph) { my $error = $parser->error(); $output .= "

$dir/$file

" . "Top -^\n". "
\n". "Error: Could not parse input from $file: $error". "
Input was:\n" . "
$input
\n". "
\n"; next; } $graph->timeout(100); $graph->layout(); if ($graph->error()) { my $error = $graph->error(); $output .= "

$dir/$file

" . "Top -^\n". "
\n". "Error: $error". "
Input was:\n" . "
$input
\n". "
\n"; next; } $output .= out ($input, $graph, 'html', $dir, $file); } } sub out { my ($txt,$graph,$method,$dir, $file) = @_; $method = 'as_' . $method; # set unique ID for CSS $graph->id($ID++); my $t = $graph->nodes() . ' Nodes, ' . $graph->edges . ' Edges'; my $n = $dir."_$file"; $dir = ucfirst($dir); # get comment $txt =~ /^\s*#\s*(.*)/; my $comment = ucfirst($1 || ''); my $link; $link = $1 if $txt =~ /\n#\s*(http.*)/; my $name = $comment || $t; push @toc, [ $n, $name ]; my $out = "\n"; if (!$sep) { $out .= "

$dir: $name

\n" . "Top -^\n". "
\n"; $out .= "Error: " . $graph->error() if $graph->error(); my $input = "
\n" . "

Input

\n" . "
$txt
\n
" . "
\n" . "

As Text

\n" . "
" . $graph->as_txt() . "
\n
"; $out .= $input . "
\n" . "

As HTML:

\n" . $graph->$method() . "\n
\n"; $out .= "
 
\n\n"; } else { $out .= "

$name

\n"; $out .= "Top -^\n"; $out .= "Source\n" if $link; $out .= "Error: " . $graph->error() if $graph->error(); $out .= $graph->$method() . "\n" . "
\n\n"; # write out the input/text } $out; } Graph-Easy-0.73/examples/as_ascii0000755000076400007640000000207611675050456016634 0ustar shlomifshlomif#!/usr/bin/perl -w ############################################################################# # This example is a bit outdated, please use the new bin/graph-easy script - # which is after "make install" available on any command line in your system. # Convert an input file containing a Graph::Easy description to # ASCII art. # Example usage: # examples/as_ascii t/in/2nodes.txt # echo "[ A ] -> [ B ]" | examples/as_ascii BEGIN { $|++; } use lib 'lib'; use Graph::Easy::Parser; my $file = shift; my $id = shift || ''; my $debug = shift; my $parser = Graph::Easy::Parser->new( debug => $debug ); if (!defined $file) { $file = \*STDIN; binmode STDIN, ':utf8' or die ("binmode STDIN, ':utf8' failed: $!"); } binmode STDERR, ':utf8' or die ("binmode STDERR, ':utf8' failed: $!"); my $graph = $parser->from_file( $file ); die ($parser->error()) unless defined $graph; $graph->id($id); $graph->timeout(360); $graph->layout(); warn($graph->error()) if $graph->error(); binmode STDOUT, ':utf8' or die ("binmode STDOUT, ':utf8' failed: $!"); print $graph->as_ascii(); Graph-Easy-0.73/examples/as_graphviz0000755000076400007640000000201511675050456017367 0ustar shlomifshlomif#!/usr/bin/perl -w ############################################################################# # This example is a bit outdated, please use the new bin/graph-easy script - # which is after "make install" available on any command line in your system. # Convert an input file containing a Graph::Easy description to # graphviz output that can be feed to dot etc. # Example usage: # examples/as_graphviz t/in/2nodes.txt | dot -Tpng >test.png # echo "[ A ] -> [ B ]" | examples/as_graphviz | dot -Tpng >test.png BEGIN { $|++; } use strict; use lib 'lib'; use Graph::Easy::Parser; my $file = shift; my $parser = Graph::Easy::Parser->new( debug => 0 ); if (!defined $file) { $file = \*STDIN; binmode STDIN, ':utf8' or die ("binmode STDIN, ':utf8' failed: $!"); } binmode STDERR, ':utf8' or die ("binmode STDERR, ':utf8' failed: $!"); my $graph = $parser->from_file( $file ); die ($parser->error()) unless defined $graph; binmode STDOUT, ':utf8' or die ("binmode STDOUT, ':utf8' failed: $!"); print $graph->as_graphviz(); Graph-Easy-0.73/examples/history.txt0000644000076400007640000000030011675050456017361 0ustar shlomifshlomif [ Bonn ] -> [ Berlin ] [ Berlin ] -> [ Frankfurt ] [ Frankfurt ] -> [ Dresden ] [ Berlin ] -> [ Potsdam ] [ Potsdam ] -> [ Cottbus ] { border-color: red; } [ Cottbus ] -> [ Frankfurt ] Graph-Easy-0.73/examples/parse0000755000076400007640000000141711675050456016171 0ustar shlomifshlomif#!/usr/bin/perl -w BEGIN { $|++; } use strict; use lib 'lib'; use Graph::Easy::Parser; print "# Graph::Easy v$Graph::Easy::VERSION\n"; my $file = shift; $file = \*STDIN unless defined $file; my $id = shift || ''; my $debug = shift || 0; my $parser = Graph::Easy::Parser->new( debug => $debug ); my $graph = $parser->from_file( $file ); print "# input: '$file'\n"; die ($parser->error()) unless defined $graph; print "# Graph has ", scalar $graph->nodes(), " nodes and ", scalar $graph->edges()," edges.\n"; $graph->id($id); $graph->timeout(240); $graph->layout(); warn ($graph->error()) if $graph->error(); print $graph->as_txt(); print $graph->as_ascii(), "\n"; print "\n", $graph->as_html(); Graph-Easy-0.73/examples/complex.txt0000644000076400007640000000143211675050456017336 0ustar shlomifshlomifgraph { border: 1px solid black; fill: oldlace; background: goldenrod; label: My sample graph; } edge { label-color: green; color: blue; } [ One ] { fill: seagreen; color: white; } -- label --> [ Two ] { shape: triangle; } [ One ] => { arrow-style: closed; } [ Three ] [ Five ] { fill: maroon; color: yellow; } <=> [ Three ] [ One ] .. Test\n label ..> [ Four ] [ Three ] { border-style: dashed; } .. Test\n label ..> { arrow-style: closed; } [ Six ] { label: Sixty\n Six\nand\nsix; } [ Five ] - Test label - > { label-color: darkslategrey; color: red; } [ Seven ] [ Seven ] -- [ Eight ] [ Five ] --> [ Eight ] [ Five ] --> [ Seven ] [ Two ] -> [ Four ] [ Three ] <-- Test label --> { arrow-style: closed; } [ Six ] [ Eight ] .. [ None ] { shape: none; fill: red; color: brown; } Graph-Easy-0.73/examples/ascii.pl0000644000076400007640000000125511675050456016556 0ustar shlomifshlomif#!/usr/bin/perl -w ############################################################################# # This example is a bit outdated, please use the new bin/grapheasy script - # which is after "make install" available in your system as simple as # "grapheasy" on any command line prompt. ############################################################################# # This script uses examples/common.pl to generate some example graphs and # displays them in ASCII. use strict; use warnings; BEGIN { chdir 'examples' if -d 'examples'; } require "common.pl"; sub out { my ($graph,$method) = @_; $method = 'as_' . $method; print $graph->$method(), "\n"; } gen_graphs (); Graph-Easy-0.73/examples/as_html0000755000076400007640000000214011675050456016500 0ustar shlomifshlomif#!/usr/bin/perl -w ############################################################################# # This example is a bit outdated, please use the new bin/graph-easy script - # which is after "make install" available on any command line in your system. # Convert an input file containing a Graph::Easy description to # an HTML page. # Example usage: # examples/as_html t/in/2nodes.txt >test.html # echo "[ A ] -> [ B ]" | examples/as_ascii BEGIN { $|++; } use strict; use lib 'lib'; use Graph::Easy::Parser; my $file = shift; my $id = shift || ''; my $debug = shift || 0; my $parser = Graph::Easy::Parser->new( debug => $debug ); if (!defined $file) { $file = \*STDIN; binmode STDIN, ':utf8' or die ("binmode STDIN, ':utf8' failed: $!"); } binmode STDERR, ':utf8' or die ("binmode STDERR, ':utf8' failed: $!"); my $graph = $parser->from_file( $file ); die ($parser->error()) unless defined $graph; $graph->id($id); $graph->timeout(360); $graph->layout(); warn ($graph->error()) if $graph->error(); binmode STDOUT, ':utf8' or die ("binmode STDOUT, ':utf8' failed: $!"); print $graph->as_html_page(); Graph-Easy-0.73/examples/as_boxart_html0000755000076400007640000000211011675050456020054 0ustar shlomifshlomif#!/usr/bin/perl -w ############################################################################# # This example is a bit outdated, please use the new bin/graph-easy script - # which is after "make install" available on any command line in your system. # Convert an input file containing a Graph::Easy description to # ASCII art using "box drawing" Unicode characters. # Example usage: # examples/as_boxart t/in/2nodes.txt # echo "[ A ] -> [ B ]" | examples/as_boxart BEGIN { $|++; } use lib 'lib'; use Graph::Easy::Parser; my $file = shift; my $id = shift || ''; my $debug = shift; my $parser = Graph::Easy::Parser->new( debug => $debug ); if (!defined $file) { $file = \*STDIN; binmode STDIN, ':utf8' or die ("binmode STDIN, ':utf8' failed: $!"); } my $graph = $parser->from_file( $file ); die ($parser->error()) unless defined $graph; $graph->id($id); $graph->timeout(360); $graph->layout(); warn($graph->error()) if $graph->error(); binmode STDOUT, ':utf8' or die ("binmode STDOUT, ':utf8' failed: $!"); #print $graph->as_boxart(); print $graph->as_boxart_html_file(); Graph-Easy-0.73/examples/base.css0000644000076400007640000000565111675050456016561 0ustar shlomifshlomifh1 { border: 1px solid black; padding: 0.2em; background: #fff0f0; margin-bottom: 0; margin-top: 0; padding-left: 0.5em; } h2 { border: 1px solid gray; border-bottom: none; padding: 0.2em; padding-left: 0.5em; background: #e0e0f0; margin-top: 0.8em; margin-bottom: 0; } div.h3 { border-bottom: 1px solid gray; padding: 0.2em; padding-left: 0.1em; margin-top: 0; margin-bottom: 0; font-weight: bold; font-size: 1.2em; } h2.green { background: #e0f0e0; } h2.coral { background: #e0f0f0; } h2.purple { background: #f0e0f0; } h2.orange { background: #fff0d0; } h2.brown { background: #e0b090; } h2.lime { background: #e0f090; } h2.honey { background: #f0f0a0; } h2.mint { background: #c0ffe0; } div.footer { background: #f0f0f0; border: 1px solid gray; padding: 0.6em; padding-left: 1.6em; font-size: small; margin-top: 1em; margin-bottom: 1em; font-size: 0.8em; } p.hr { padding-top: 0.3em; border: none; border-top: 1px solid gray; } div.right { margin-left: 8.2em; } div.text { border: 1px solid gray; padding: 0.5em; padding-left: 1.5em; background: #e8e8e8; font-size: 0.9em; } .clear { clear: both; } a.top { font-size: 0.8em; float: right; position: relative; top: -2.5em; right: 0.5em; color: black; font-weight: bold; text-decoration: none; padding: 0.2em; } a.top:hover { color: white; background: black; padding: 0.2em; } .menubck, .menuext, .menucur, .menuadd, .menuind, .menuinc, .menucin { display: block; border: 1px solid gray; padding: 0.1em; padding-left: 0.5em; margin: 0; margin-bottom: 0.4em; min-width: 7em; font-size: 0.75em; text-decoration: none; background: #e0e0ff; color: black; } .menuind, .menuinc, .menucin { min-width: 6em; margin-left: 1em; background: #e0e0ff; } .menu { background: white; padding: 0em; margin: 0; border: none; width: 7em; margin-right: 0.2em; position: fixed; } .menucur, .menucin { border-color: #404040; } .menucin { background: #a0a0ff; } .menucur { background: #a0a0ff; } .menuadd { background: #f0a0a0; } .menuind { background: #d0d0ff; } .menubck { background: #f0b0b0; } :hover { color: #ffffff; background: #000000; } .menubck:hover, .menuadd:hover { background: #a03030; } .menucur:hover { background: #000080; } .menuind:hover, .menucin:hover { background: #3030a0; } img.i { border: none; } img { border: 1px solid gray; margin-top: 0.7em; margin-bottom: 0.7em; } p, li { max-width: 50em; } p { padding-bottom: 0; margin-bottom: 0.4em; margin-top: 0.4em; } ul { list-style: square; } li { font-size: 0.9em; } tr.odd td { background: #ffdead; } code { background: #ffffff; color: black; padding: 2px; } pre { background: #d0d0d0; color: black; padding: 0.8em; margin-left: 1em; margin-bottom: 2.5em; border: 1px solid black; max-width: 40em; } Graph-Easy-0.73/examples/fun.tpl0000644000076400007640000000261011675050456016436 0ustar shlomifshlomif <graph>-Plugin for Mediawiki - Syntax

<graph>-Plugin for Mediawiki

Table of Contents:

##TOC##

To see the input text for each graph, follow the Source link in each section.

Fun with Graphs

##HTML##
Graph-Easy-0.73/examples/html.pl0000644000076400007640000000543311675050456016434 0ustar shlomifshlomif#!/usr/bin/perl -w ############################################################################# # This example is a bit outdated, please use the new bin/grapheasy script - # which is after "make install" available in your system as simple as # "grapheasy" on any command line prompt. ############################################################################# # This script uses examples/common.pl to generate some example graphs and # prints them as HTML page. Use it like: # ewxamples/html.pl >test.html # and then open test.html in your favourite browser. use strict; use warnings; BEGIN { chdir 'examples' if -d 'examples'; } require "common.pl"; my $graph = Graph::Easy->new(); my @toc = (); my $html = $graph->html_page_header(); $html .= < h1 { border-bottom: 1px solid black; padding-bottom: 0.2em; } h2 { border-bottom: 1px solid grey; padding-bottom: 0.2em; margin-bottom: 0em; } div { margin-left: 2em; } .graph { margin-left: 2em; }

Graph-Simple Test page

This page was automatically created at ##time## by examples/html.pl running Graph::Easy v##version##.

On each of the following testcases you will see a text representation of the graph on the left side, and on the right side the automatically generated HTML+CSS code.

Notes:

  • The text representation does not yet carry node attributes, like colors or border style.
  • The HTML does not yet have "pretty" edges. This will be fixed later.
  • The limitations in Graph::Easy apply.

Testcases:

##TOC## HTML ; # generate the parts and push their names into @toc gen_graphs($graph, 'html'); $html .= $graph->html_page_footer(); my $toc = '
    '; for my $t (@toc) { my $n = $t; $n =~ s/\s/_/; $toc .= "
  • " . $t . "\n"; } $toc .= "
\n"; # insert the TOC $html =~ s/##TOC##/ $toc /; $html =~ s/##time##/ scalar localtime() /e; $html =~ s/##version##/$Graph::Easy::VERSION/e; print $html; # all done; 1; ############################################################################# sub out { my ($graph,$method) = @_; $method = 'as_' . $method; my $t = $graph->nodes() . ' Nodes, ' . $graph->edges . ' Edges'; my $n = $t; $n =~ s/\s/_/; $html .= "

$t

\n" . "
\n" . "

As Text

\n" . "
" . $graph->as_txt() . "
" . "
\n" . "

As HTML:

\n" . $graph->$method() . "
\n" . "
 
\n\n"; push @toc, $t; } Graph-Easy-0.73/examples/as_boxart0000755000076400007640000000204211675050456017034 0ustar shlomifshlomif#!/usr/bin/perl -w ############################################################################# # This example is a bit outdated, please use the new bin/graph-easy script - # which is after "make install" available on any command line in your system. # Convert an input file containing a Graph::Easy description to # ASCII art using "box drawing" Unicode characters. # Example usage: # examples/as_boxart t/in/2nodes.txt # echo "[ A ] -> [ B ]" | examples/as_boxart BEGIN { $|++; } use lib 'lib'; use Graph::Easy::Parser; my $file = shift; my $id = shift || ''; my $debug = shift; my $parser = Graph::Easy::Parser->new( debug => $debug ); if (!defined $file) { $file = \*STDIN; binmode STDIN, ':utf8' or die ("binmode STDIN, ':utf8' failed: $!"); } my $graph = $parser->from_file( $file ); die ($parser->error()) unless defined $graph; $graph->id($id); $graph->timeout(360); $graph->layout(); warn($graph->error()) if $graph->error(); binmode STDOUT, ':utf8' or die ("binmode STDOUT, ':utf8' failed: $!"); print $graph->as_boxart(); Graph-Easy-0.73/examples/common.pl0000644000076400007640000000334011675050456016753 0ustar shlomifshlomif#!/usr/bin/perl -w ############################################################################# # This script is used by both examples/ascii.pl and examples/html.pl to # generate some sample graphs and then outputting them in the desired format. use strict; use warnings; BEGIN { use lib '../lib'; } use Graph::Easy; sub gen_graphs { my $graph = shift || Graph::Easy->new(); my $method = shift || 'ascii'; ########################################################################### my $node = $graph->add_node( 'Bonn' ); my $node2 = $graph->add_node( 'Berlin' ); $graph->add_edge( $node, $node2 ); out ($graph, $method); ########################################################################### $graph->{debug} = 0; my $node3 = $graph->add_node( 'Frankfurt' ); $node3->set_attribute('border-style', 'dotted'); my $edge3 = Graph::Easy::Edge->new( style => 'double' ); $graph->add_edge( $node2, $node3, $edge3 ); out ($graph, $method); ########################################################################### $graph->add_edge( $node3, 'Dresden' ); out ($graph, $method); ########################################################################### $graph->add_edge( $node2, 'Potsdam' ); out ($graph, $method); ########################################################################### my $node6 = $graph->add_node( 'Cottbus',); $node6->set_attribute('border', '1px red dashed'); my $edge5 = $graph->add_edge( 'Potsdam', $node6 ); out ($graph, $method); ########################################################################### $graph->add_edge( $node6, $node3 ); out ($graph, $method); $graph->add_edge( $node6, $node3 ); out ($graph, $method); } 1; Graph-Easy-0.73/LICENSE0000644000076400007640000004236711675050456014331 0ustar shlomifshlomif=pod =head1 LICENSES =head2 Colorschemes This product includes color specifications and designs developed by Cynthia Brewer (http://colorbrewer.org/). The following license applies to them: Apache-Style Software License for ColorBrewer Color Schemes v1.1 Copyright (c) 2002 Cynthia Brewer, Mark Harrower, and The Pennsylvania State University. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions as source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: This product includes color specifications and designs developed by Cynthia Brewer (http://colorbrewer.org/). Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear. 3. The name "ColorBrewer" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact Cynthia Brewer at cbrewer at psu dot edu. 4. Products derived from this software may not be called "ColorBrewer", nor may "ColorBrewer" appear in their name, without prior written permission of Cynthia Brewer. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CYNTHIA BREWER, MARK HARROWER, OR THE PENNSYLVANIA STATE UNIVERSITY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =head2 All other things included in this package To the rest of the code, documentation, scripts etc. the following license applies: GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS =cut Graph-Easy-0.73/MANIFEST0000644000076400007640000004105412150107417014432 0ustar shlomifshlomifbench/bench.pl bench/serie.pl bench/stress.pl bench/test.dot bench/test.txt bin/graph-easy CHANGES examples/as_ascii examples/as_boxart examples/as_boxart_html examples/as_graphviz examples/as_html examples/as_svg examples/as_txt examples/ascii.pl examples/base.css examples/common.pl examples/complex.txt examples/fun.tpl examples/history.txt examples/html.pl examples/parse examples/syntax.pl examples/syntax.tpl examples/wikicrawl.pl foo inc/Module/Install.pm inc/Module/Install/Base.pm inc/Module/Install/Can.pm inc/Module/Install/Fetch.pm inc/Module/Install/Makefile.pm inc/Module/Install/Metadata.pm inc/Module/Install/Scripts.pm inc/Module/Install/Win32.pm inc/Module/Install/WriteAll.pm INSTALL lib/Graph/Easy.pm lib/Graph/Easy/As_ascii.pm lib/Graph/Easy/As_graphml.pm lib/Graph/Easy/As_graphviz.pm lib/Graph/Easy/As_txt.pm lib/Graph/Easy/As_vcg.pm lib/Graph/Easy/Attributes.pm lib/Graph/Easy/Base.pm lib/Graph/Easy/Edge.pm lib/Graph/Easy/Edge/Cell.pm lib/Graph/Easy/Group.pm lib/Graph/Easy/Group/Anon.pm lib/Graph/Easy/Group/Cell.pm lib/Graph/Easy/Layout.pm lib/Graph/Easy/Layout/Chain.pm lib/Graph/Easy/Layout/Force.pm lib/Graph/Easy/Layout/Grid.pm lib/Graph/Easy/Layout/Path.pm lib/Graph/Easy/Layout/Repair.pm lib/Graph/Easy/Layout/Scout.pm lib/Graph/Easy/Node.pm lib/Graph/Easy/Node/Anon.pm lib/Graph/Easy/Node/Cell.pm lib/Graph/Easy/Node/Empty.pm lib/Graph/Easy/Parser.pm lib/Graph/Easy/Parser/Graphviz.pm lib/Graph/Easy/Parser/VCG.pm lib/Graph/Easy/Util.pm LICENSE Makefile.PL MANIFEST This list of files MANIFEST.SKIP META.yml README SIGNATURE t/anon.t t/anon_group.t t/as_txt.t t/as_vcg.t t/ascii.t t/astar.t t/attributes.t t/base.t t/boxart.t t/cell.t t/chain.t t/class.t t/cluster.t t/copy.t t/custom.t t/delete.t t/dot/4_loose.dot t/drop.t t/easypm.t t/edge.t t/edge_cell.t t/fb.t t/fun/0000.txt t/fun/0010.txt t/fun/0011.txt t/fun/0020.txt t/fun/0030.txt t/fun/0131.txt t/fun/0200.txt t/fun/biofuel.txt t/fun/geek_dating.txt t/fun/overview.txt t/gdl.t t/graph-maker.t t/graph.t t/graphml.t t/graphml_yed.t t/graphviz.t t/group.t t/group/0010.txt t/group/0131.txt t/group/0230.txt t/gv.t t/heap.t t/html.t t/in/0_empty_group.txt t/in/0_empty_groups.txt t/in/10_repair.txt t/in/10borders.txt t/in/18_multiples.txt t/in/1_bidi_loop.txt t/in/1_empty_group.txt t/in/1_selfloop.txt t/in/1_selfloop_2.txt t/in/1_selfloop_label.txt t/in/1_undirected_loop.txt t/in/1node.txt t/in/25_autosplit_empty.txt t/in/2_autolabel.txt t/in/2_autosplit_empty.txt t/in/2_autosplit_escaped.txt t/in/2_bidi_astar.txt t/in/2_bidi_endpoint.txt t/in/2_class.txt t/in/2_classes.txt t/in/2_cluster.txt t/in/2_cluster_2.txt t/in/2_cluster_3.txt t/in/2_dot.txt t/in/2_dot_dot_dash.txt t/in/2_edges.txt t/in/2_flow.txt t/in/2_graph_label.txt t/in/2_group.txt t/in/2_group_labelpos.txt t/in/2_group_multicell.txt t/in/2_group_no_border.txt t/in/2_invisible_left.txt t/in/2_invisible_right.txt t/in/2_label.txt t/in/2_label_align.txt t/in/2_list_attr.txt t/in/2_long_labels.txt t/in/2_newlines.txt t/in/2_selfloop.txt t/in/2_selfloop_flow_down.txt t/in/2_split_bug.txt t/in/2_wrap.txt t/in/2_zeros.txt t/in/2nodes.txt t/in/3_autosplit_hang.txt t/in/3_bend_bug.txt t/in/3_cache_bug.txt t/in/3_cluster.txt t/in/3_colors.txt t/in/3_corrupt.txt t/in/3_edge_labels_from_class.txt t/in/3_edge_repair.txt t/in/3_edge_start.txt t/in/3_empty_group.txt t/in/3_flow.txt t/in/3_group_align_center.txt t/in/3_inherit.txt t/in/3_invisible_both.txt t/in/3_joining.txt t/in/3_joint.txt t/in/3_joint_short.txt t/in/3_list_attr.txt t/in/3_lists.txt t/in/3_nodes_5_edges.txt t/in/3_selfloop.txt t/in/3_selfloop_flip.txt t/in/3_selfloop_flow_down.txt t/in/3_selfloop_flow_left.txt t/in/3_selfloop_flow_up.txt t/in/3_split_attribute.txt t/in/3nodes.txt t/in/4_2x2nodes.txt t/in/4_att.txt t/in/4_autosplit_class.txt t/in/4_autosplit_empty.txt t/in/4_autosplit_offset.txt t/in/4_autosplit_shape.txt t/in/4_bend_bug.txt t/in/4_bug_basename.txt t/in/4_bug_joint_2.txt t/in/4_collapse.txt t/in/4_comma.txt t/in/4_cross.txt t/in/4_cross_inv.txt t/in/4_cross_split.txt t/in/4_cross_split_hor.txt t/in/4_edge_cross.txt t/in/4_edge_labels.txt t/in/4_edge_types.txt t/in/4_endless_loop.txt t/in/4_endless_loop_2.txt t/in/4_flow.txt t/in/4_flow_chain.txt t/in/4_invisible.txt t/in/4_joint.txt t/in/4_joint_bug_flags.txt t/in/4_list_attr.txt t/in/4_lists.txt t/in/4_minlen.txt t/in/4_near.txt t/in/4_node_edge.txt t/in/4_nodes_5_edges.txt t/in/4_nodes_6_edges.txt t/in/4_nodes_edge.txt t/in/4groups.txt t/in/4groups_class.txt t/in/4nodes.txt t/in/5_a-star_bug.txt t/in/5_arrow_styles.txt t/in/5_flow.txt t/in/5_group_repair.txt t/in/5_group_split.txt t/in/5_joint.txt t/in/5_joint_bug2.txt t/in/5_joint_label.txt t/in/5_long_edge_labels.txt t/in/5_multicell.txt t/in/5_offsets.txt t/in/5_offsets_2.txt t/in/5_rounded.txt t/in/5_tree_joint.txt t/in/6_autosplit_class.txt t/in/6_chain_10_edges.txt t/in/6_chained.txt t/in/6_empty_row.txt t/in/6_fanout.txt t/in/6_group_align.txt t/in/6_joint.txt t/in/6_multicell.txt t/in/6_multicell_offset.txt t/in/6_nested_groups.txt t/in/6_ranks.txt t/in/6_split_join_loop.txt t/in/7_cluster.txt t/in/7_star.txt t/in/7_tree.txt t/in/8_align.txt t/in/8_basename.txt t/in/8_chain.txt t/in/8_endless_loop.txt t/in/8_flow.txt t/in/8_invisible.txt t/in/8_labels.txt t/in/8_optimize_bend.txt t/in/8_points.txt t/in/9_chain.txt t/in/9_cross.txt t/in/9_flow_south.txt t/in/dot/0_empty.dot t/in/dot/10_numbers.dot t/in/dot/16_split.dot t/in/dot/2_bool.dot t/in/dot/2_comment_inside_attr.dot t/in/dot/2_graph_label_bottom.dot t/in/dot/2_group_labelloc.dot t/in/dot/2_ignore.dot t/in/dot/2_linewidth.dot t/in/dot/2_no_spaces.dot t/in/dot/2_nospace.dot t/in/dot/2_ports.dot t/in/dot/2_setlinewidth.dot t/in/dot/2_square_bracket_in_attr.dot t/in/dot/2_strict.dot t/in/dot/3_colors.dot t/in/dot/3_empty_record.dot t/in/dot/3_empty_record_LR.dot t/in/dot/3_graph_label_long.dot t/in/dot/3_ids.dot t/in/dot/3_invis.dot t/in/dot/3_node_label.dot t/in/dot/3_output_lone.dot t/in/dot/4_cluster_labeljust.dot t/in/dot/4_compass.dot t/in/dot/4_html_like.dot t/in/dot/4_record.dot t/in/dot/4_strings.dot t/in/dot/4_uppercase.dot t/in/dot/5_scope_atr.dot t/in/dot/5_scopes.dot t/in/dot/5_scopes_chain.dot t/in/dot/5_scopes_uni.dot t/in/dot/6_2_cluster.dot t/in/dot/6_comments.dot t/in/dot/6_group_align.dot t/in/dot/7_record.dot t/in/dot/9_back.dot t/in/dot/9_edge_styles.dot t/in/dot/9_stacking.dot t/in/dot/9_tree.dot t/in/gdl/1_color_code.gdl t/in/gdl/2_bottom_to_top.gdl t/in/gdl/2_left_to_right.gdl t/in/gdl/2_right_to_left.gdl t/in/gdl/2_top_to_bottom.gdl t/in/README t/layers.t t/layout.t t/layout_r.t t/layouter.t t/layouter/edge_label.txt t/layouter/layouter.txt t/layouter/layouter_chain.txt t/layouter/layouter_loop.txt t/layouter/multiples.txt t/layouter/state.txt t/messages.t t/nesting.t t/node.t t/node_mc.t t/out/0_empty_group.txt t/out/0_empty_groups.txt t/out/10_repair.txt t/out/10borders.txt t/out/18_multiples.txt t/out/1_bidi_loop.txt t/out/1_empty_group.txt t/out/1_selfloop.txt t/out/1_selfloop_2.txt t/out/1_selfloop_label.txt t/out/1_undirected_loop.txt t/out/1node.txt t/out/25_autosplit_empty.txt t/out/2_autolabel.txt t/out/2_autosplit_empty.txt t/out/2_autosplit_escaped.txt t/out/2_bidi_astar.txt t/out/2_bidi_endpoint.txt t/out/2_class.txt t/out/2_classes.txt t/out/2_cluster.txt t/out/2_cluster_2.txt t/out/2_cluster_3.txt t/out/2_dot.txt t/out/2_dot_dot_dash.txt t/out/2_edges.txt t/out/2_flow.txt t/out/2_graph_label.txt t/out/2_group.txt t/out/2_group_labelpos.txt t/out/2_group_multicell.txt t/out/2_group_no_border.txt t/out/2_invisible_left.txt t/out/2_invisible_right.txt t/out/2_label.txt t/out/2_label_align.txt t/out/2_list_attr.txt t/out/2_long_labels.txt t/out/2_newlines.txt t/out/2_nodes_inv.txt t/out/2_selfloop.txt t/out/2_selfloop_flow_down.txt t/out/2_split_bug.txt t/out/2_wrap.txt t/out/2_zeros.txt t/out/2nodes.txt t/out/3_autosplit_hang.txt t/out/3_bend_bug.txt t/out/3_cache_bug.txt t/out/3_cluster.txt t/out/3_colors.txt t/out/3_corrupt.txt t/out/3_edge_labels_from_class.txt t/out/3_edge_repair.txt t/out/3_edge_start.txt t/out/3_empty_group.txt t/out/3_flow.txt t/out/3_group_align_center.txt t/out/3_inherit.txt t/out/3_invisible_both.txt t/out/3_joining.txt t/out/3_joint.txt t/out/3_joint_short.txt t/out/3_list_attr.txt t/out/3_lists.txt t/out/3_nodes_5_edges.txt t/out/3_selfloop.txt t/out/3_selfloop_flip.txt t/out/3_selfloop_flow_down.txt t/out/3_selfloop_flow_left.txt t/out/3_selfloop_flow_up.txt t/out/3_split_attribute.txt t/out/3nodes.txt t/out/4_2x2nodes.txt t/out/4_att.txt t/out/4_autosplit_class.txt t/out/4_autosplit_empty.txt t/out/4_autosplit_offset.txt t/out/4_autosplit_shape.txt t/out/4_bend_bug.txt t/out/4_bug_basename.txt t/out/4_bug_joint_2.txt t/out/4_collapse.txt t/out/4_comma.txt t/out/4_cross.txt t/out/4_cross_inv.txt t/out/4_cross_split.txt t/out/4_cross_split_hor.txt t/out/4_edge_cross.txt t/out/4_edge_labels.txt t/out/4_edge_types.txt t/out/4_endless_loop.txt t/out/4_endless_loop_2.txt t/out/4_flow.txt t/out/4_flow_chain.txt t/out/4_invisible.txt t/out/4_joint.txt t/out/4_joint_bug_flags.txt t/out/4_list_attr.txt t/out/4_lists.txt t/out/4_minlen.txt t/out/4_near.txt t/out/4_node_edge.txt t/out/4_nodes_5_edges.txt t/out/4_nodes_6_edges.txt t/out/4_nodes_edge.txt t/out/4groups.txt t/out/4groups_class.txt t/out/4nodes.txt t/out/5_a-star_bug.txt t/out/5_arrow_styles.txt t/out/5_flow.txt t/out/5_group_repair.txt t/out/5_group_split.txt t/out/5_joint.txt t/out/5_joint_bug2.txt t/out/5_joint_label.txt t/out/5_long_edge_labels.txt t/out/5_multicell.txt t/out/5_offsets.txt t/out/5_offsets_2.txt t/out/5_rounded.txt t/out/5_tree_joint.txt t/out/6_autosplit_class.txt t/out/6_chain_10_edges.txt t/out/6_chained.txt t/out/6_empty_row.txt t/out/6_fanout.txt t/out/6_group_align.txt t/out/6_joint.txt t/out/6_multicell.txt t/out/6_multicell_offset.txt t/out/6_nested_groups.txt t/out/6_ranks.txt t/out/6_split_join_loop.txt t/out/7_cluster.txt t/out/7_star.txt t/out/7_tree.txt t/out/8_align.txt t/out/8_basename.txt t/out/8_chain.txt t/out/8_endless_loop.txt t/out/8_flow.txt t/out/8_invisible.txt t/out/8_labels.txt t/out/8_optimize_bend.txt t/out/8_points.txt t/out/9_chain.txt t/out/9_cross.txt t/out/9_flow_south.txt t/out/dot/0_empty.txt t/out/dot/10_numbers.txt t/out/dot/16_split.txt t/out/dot/2_bool.txt t/out/dot/2_comment_inside_attr.txt t/out/dot/2_graph_label_bottom.txt t/out/dot/2_group_labelloc.txt t/out/dot/2_ignore.txt t/out/dot/2_linewidth.txt t/out/dot/2_no_spaces.txt t/out/dot/2_nospace.txt t/out/dot/2_ports.txt t/out/dot/2_setlinewidth.txt t/out/dot/2_square_bracket_in_attr.txt t/out/dot/2_strict.txt t/out/dot/3_colors.txt t/out/dot/3_empty_record.txt t/out/dot/3_empty_record_LR.txt t/out/dot/3_graph_label_long.txt t/out/dot/3_ids.txt t/out/dot/3_invis.txt t/out/dot/3_node_label.txt t/out/dot/3_output_lone.txt t/out/dot/4_cluster_labeljust.txt t/out/dot/4_compass.txt t/out/dot/4_html_like.txt t/out/dot/4_loose.txt t/out/dot/4_record.txt t/out/dot/4_strings.txt t/out/dot/4_uppercase.txt t/out/dot/5_scope_atr.txt t/out/dot/5_scopes.txt t/out/dot/5_scopes_chain.txt t/out/dot/5_scopes_uni.txt t/out/dot/6_2_cluster.txt t/out/dot/6_comments.txt t/out/dot/6_group_align.txt t/out/dot/7_record.txt t/out/dot/9_back.txt t/out/dot/9_edge_styles.txt t/out/dot/9_stacking.txt t/out/dot/9_tree.txt t/out/drop_result.txt t/out/gdl/1_color_code.txt t/out/gdl/2_bottom_to_top.txt t/out/gdl/2_left_to_right.txt t/out/gdl/2_right_to_left.txt t/out/gdl/2_top_to_bottom.txt t/parse_att.t t/parse_edge.t t/parser.t t/parser_dot.t t/parser_dot_html.t t/path.t t/pod.t t/pod_cov.t t/re_layout.t t/split.t t/stress/0001.txt t/stress/0002.txt t/stress/0003.txt t/stress/0004.txt t/stress/0005.txt t/stress/0006.txt t/stress/0010.txt t/stress/0011.txt t/stress/0012.txt t/stress/0020.txt t/stress/anon.txt t/stress/drop.txt t/syntax/0000.txt t/syntax/0001.txt t/syntax/0002.txt t/syntax/0003.txt t/syntax/0010.txt t/syntax/0011.txt t/syntax/0020.txt t/syntax/0021.txt t/syntax/0030.txt t/syntax/0040.txt t/syntax/0050.txt t/syntax/0060.txt t/syntax/0061.txt t/syntax/0062.txt t/syntax/0063.txt t/syntax/0070.txt t/syntax/0080.txt t/syntax/0090.txt t/syntax/0100.txt t/syntax/0102.txt t/syntax/0110.txt t/syntax/0120.txt t/syntax/0130.txt t/syntax/0131.txt t/syntax/0140.txt t/syntax/0150.txt t/syntax/0160.txt t/syntax/0170.txt t/syntax/0171.txt t/syntax/0180.txt t/syntax/0190.txt t/syntax/0200.txt t/syntax/0210.txt t/syntax/0220.txt t/syntax/0230.txt t/syntax/0240.txt t/syntax/0250.txt t/syntax/0251.txt t/syntax/0252.txt t/syntax/0254.txt t/txt/0_empty_group.txt t/txt/0_empty_groups.txt t/txt/10_repair.txt t/txt/10borders.txt t/txt/18_multiples.txt t/txt/1_empty_group.txt t/txt/1_undirected_loop.txt t/txt/25_autosplit_empty.txt t/txt/2_autolabel.txt t/txt/2_autosplit_empty.txt t/txt/2_autosplit_escaped.txt t/txt/2_class.txt t/txt/2_classes.txt t/txt/2_cluster.txt t/txt/2_cluster_2.txt t/txt/2_cluster_3.txt t/txt/2_dot.txt t/txt/2_dot_dot_dash.txt t/txt/2_edges.txt t/txt/2_flow.txt t/txt/2_graph_label.txt t/txt/2_group.txt t/txt/2_group_labelpos.txt t/txt/2_group_multicell.txt t/txt/2_group_no_border.txt t/txt/2_invisible_left.txt t/txt/2_invisible_right.txt t/txt/2_label.txt t/txt/2_label_align.txt t/txt/2_list_attr.txt t/txt/2_long_labels.txt t/txt/2_newlines.txt t/txt/2_selfloop.txt t/txt/2_selfloop_flow_down.txt t/txt/2_split_bug.txt t/txt/2_wrap.txt t/txt/2_zeros.txt t/txt/2nodes.txt t/txt/3_autosplit_hang.txt t/txt/3_cache_bug.txt t/txt/3_cluster.txt t/txt/3_colors.txt t/txt/3_corrupt.txt t/txt/3_edge_labels_from_class.txt t/txt/3_edge_repair.txt t/txt/3_edge_start.txt t/txt/3_empty_group.txt t/txt/3_flow.txt t/txt/3_group_align_center.txt t/txt/3_inherit.txt t/txt/3_invisible_both.txt t/txt/3_joining.txt t/txt/3_joint.txt t/txt/3_joint_short.txt t/txt/3_list_attr.txt t/txt/3_lists.txt t/txt/3_nodes_5_edges.txt t/txt/3_selfloop.txt t/txt/3_selfloop_flip.txt t/txt/3_selfloop_flow_down.txt t/txt/3_selfloop_flow_left.txt t/txt/3_selfloop_flow_up.txt t/txt/3_split_attribute.txt t/txt/3nodes.txt t/txt/4_2x2nodes.txt t/txt/4_att.txt t/txt/4_autosplit_class.txt t/txt/4_autosplit_empty.txt t/txt/4_autosplit_offset.txt t/txt/4_autosplit_shape.txt t/txt/4_bug_basename.txt t/txt/4_bug_joint_2.txt t/txt/4_collapse.txt t/txt/4_comma.txt t/txt/4_cross.txt t/txt/4_cross_inv.txt t/txt/4_cross_split.txt t/txt/4_cross_split_hor.txt t/txt/4_edge_cross.txt t/txt/4_edge_labels.txt t/txt/4_edge_types.txt t/txt/4_endless_loop.txt t/txt/4_endless_loop_2.txt t/txt/4_flow.txt t/txt/4_flow_chain.txt t/txt/4_invisible.txt t/txt/4_joint.txt t/txt/4_joint_bug_flags.txt t/txt/4_list_attr.txt t/txt/4_lists.txt t/txt/4_minlen.txt t/txt/4_near.txt t/txt/4_node_edge.txt t/txt/4_nodes_5_edges.txt t/txt/4_nodes_6_edges.txt t/txt/4_nodes_edge.txt t/txt/4groups.txt t/txt/4groups_class.txt t/txt/4nodes.txt t/txt/5_arrow_styles.txt t/txt/5_flow.txt t/txt/5_group_repair.txt t/txt/5_group_split.txt t/txt/5_joint.txt t/txt/5_joint_bug2.txt t/txt/5_joint_label.txt t/txt/5_long_edge_labels.txt t/txt/5_multicell.txt t/txt/5_offsets.txt t/txt/5_offsets_2.txt t/txt/5_rounded.txt t/txt/5_tree_joint.txt t/txt/6_autosplit_class.txt t/txt/6_chain_10_edges.txt t/txt/6_chained.txt t/txt/6_empty_row.txt t/txt/6_fanout.txt t/txt/6_group_align.txt t/txt/6_joint.txt t/txt/6_multicell.txt t/txt/6_multicell_offset.txt t/txt/6_nested_groups.txt t/txt/6_ranks.txt t/txt/6_split_join_loop.txt t/txt/7_cluster.txt t/txt/7_star.txt t/txt/7_tree.txt t/txt/8_align.txt t/txt/8_basename.txt t/txt/8_chain.txt t/txt/8_endless_loop.txt t/txt/8_flow.txt t/txt/8_invisible.txt t/txt/8_labels.txt t/txt/8_optimize_bend.txt t/txt/8_points.txt t/txt/9_chain.txt t/txt/9_cross.txt t/txt/9_flow_south.txt t/txt/dot/0_empty.txt t/txt/dot/10_numbers.txt t/txt/dot/16_split.txt t/txt/dot/2_bool.txt t/txt/dot/2_comment_inside_attr.txt t/txt/dot/2_graph_label_bottom.txt t/txt/dot/2_group_labelloc.txt t/txt/dot/2_ignore.txt t/txt/dot/2_linewidth.txt t/txt/dot/2_no_spaces.txt t/txt/dot/2_nospace.txt t/txt/dot/2_ports.txt t/txt/dot/2_setlinewidth.txt t/txt/dot/2_square_bracket_in_attr.txt t/txt/dot/2_strict.txt t/txt/dot/3_colors.txt t/txt/dot/3_empty_record.txt t/txt/dot/3_empty_record_LR.txt t/txt/dot/3_graph_label_long.txt t/txt/dot/3_ids.txt t/txt/dot/3_invis.txt t/txt/dot/3_node_label.txt t/txt/dot/3_output_lone.txt t/txt/dot/4_cluster_labeljust.txt t/txt/dot/4_compass.txt t/txt/dot/4_html_like.txt t/txt/dot/4_loose.txt t/txt/dot/4_record.txt t/txt/dot/4_strings.txt t/txt/dot/4_uppercase.txt t/txt/dot/5_scope_atr.txt t/txt/dot/5_scopes.txt t/txt/dot/5_scopes_chain.txt t/txt/dot/5_scopes_uni.txt t/txt/dot/6_2_cluster.txt t/txt/dot/6_comments.txt t/txt/dot/6_group_align.txt t/txt/dot/7_record.txt t/txt/dot/9_back.txt t/txt/dot/9_edge_styles.txt t/txt/dot/9_stacking.txt t/txt/dot/9_tree.txt t/txt/gdl/1_color_code.txt t/txt/gdl/2_bottom_to_top.txt t/txt/gdl/2_left_to_right.txt t/txt/gdl/2_right_to_left.txt t/txt/gdl/2_top_to_bottom.txt t/use_class.t t/vcg.t TODO Graph-Easy-0.73/inc/0000755000076400007640000000000012150110221014031 5ustar shlomifshlomifGraph-Easy-0.73/inc/Module/0000755000076400007640000000000012150110221015256 5ustar shlomifshlomifGraph-Easy-0.73/inc/Module/Install/0000755000076400007640000000000012150110221016664 5ustar shlomifshlomifGraph-Easy-0.73/inc/Module/Install/Win32.pm0000644000076400007640000000340312150107462020141 0ustar shlomifshlomif#line 1 package Module::Install::Win32; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.06'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } # determine if the user needs nmake, and download it if needed sub check_nmake { my $self = shift; $self->load('can_run'); $self->load('get_file'); require Config; return unless ( $^O eq 'MSWin32' and $Config::Config{make} and $Config::Config{make} =~ /^nmake\b/i and ! $self->can_run('nmake') ); print "The required 'nmake' executable not found, fetching it...\n"; require File::Basename; my $rv = $self->get_file( url => 'http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe', ftp_url => 'ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe', local_dir => File::Basename::dirname($^X), size => 51928, run => 'Nmake15.exe /o > nul', check_for => 'Nmake.exe', remove => 1, ); die <<'END_MESSAGE' unless $rv; ------------------------------------------------------------------------------- Since you are using Microsoft Windows, you will need the 'nmake' utility before installation. It's available at: http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe or ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe Please download the file manually, save it to a directory in %PATH% (e.g. C:\WINDOWS\COMMAND\), then launch the MS-DOS command line shell, "cd" to that directory, and run "Nmake15.exe" from there; that will create the 'nmake.exe' file needed by this module. You may then resume the installation process described in README. ------------------------------------------------------------------------------- END_MESSAGE } 1; Graph-Easy-0.73/inc/Module/Install/Fetch.pm0000644000076400007640000000462712150107462020301 0ustar shlomifshlomif#line 1 package Module::Install::Fetch; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.06'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub get_file { my ($self, %args) = @_; my ($scheme, $host, $path, $file) = $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return; if ( $scheme eq 'http' and ! eval { require LWP::Simple; 1 } ) { $args{url} = $args{ftp_url} or (warn("LWP support unavailable!\n"), return); ($scheme, $host, $path, $file) = $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return; } $|++; print "Fetching '$file' from $host... "; unless (eval { require Socket; Socket::inet_aton($host) }) { warn "'$host' resolve failed!\n"; return; } return unless $scheme eq 'ftp' or $scheme eq 'http'; require Cwd; my $dir = Cwd::getcwd(); chdir $args{local_dir} or return if exists $args{local_dir}; if (eval { require LWP::Simple; 1 }) { LWP::Simple::mirror($args{url}, $file); } elsif (eval { require Net::FTP; 1 }) { eval { # use Net::FTP to get past firewall my $ftp = Net::FTP->new($host, Passive => 1, Timeout => 600); $ftp->login("anonymous", 'anonymous@example.com'); $ftp->cwd($path); $ftp->binary; $ftp->get($file) or (warn("$!\n"), return); $ftp->quit; } } elsif (my $ftp = $self->can_run('ftp')) { eval { # no Net::FTP, fallback to ftp.exe require FileHandle; my $fh = FileHandle->new; local $SIG{CHLD} = 'IGNORE'; unless ($fh->open("|$ftp -n")) { warn "Couldn't open ftp: $!\n"; chdir $dir; return; } my @dialog = split(/\n/, <<"END_FTP"); open $host user anonymous anonymous\@example.com cd $path binary get $file $file quit END_FTP foreach (@dialog) { $fh->print("$_\n") } $fh->close; } } else { warn "No working 'ftp' program available!\n"; chdir $dir; return; } unless (-f $file) { warn "Fetching failed: $@\n"; chdir $dir; return; } return if exists $args{size} and -s $file != $args{size}; system($args{run}) if exists $args{run}; unlink($file) if $args{remove}; print(((!exists $args{check_for} or -e $args{check_for}) ? "done!" : "failed! ($!)"), "\n"); chdir $dir; return !$?; } 1; Graph-Easy-0.73/inc/Module/Install/Base.pm0000644000076400007640000000214712150107462020115 0ustar shlomifshlomif#line 1 package Module::Install::Base; use strict 'vars'; use vars qw{$VERSION}; BEGIN { $VERSION = '1.06'; } # Suspend handler for "redefined" warnings BEGIN { my $w = $SIG{__WARN__}; $SIG{__WARN__} = sub { $w }; } #line 42 sub new { my $class = shift; unless ( defined &{"${class}::call"} ) { *{"${class}::call"} = sub { shift->_top->call(@_) }; } unless ( defined &{"${class}::load"} ) { *{"${class}::load"} = sub { shift->_top->load(@_) }; } bless { @_ }, $class; } #line 61 sub AUTOLOAD { local $@; my $func = eval { shift->_top->autoload } or return; goto &$func; } #line 75 sub _top { $_[0]->{_top}; } #line 90 sub admin { $_[0]->_top->{admin} or Module::Install::Base::FakeAdmin->new; } #line 106 sub is_admin { ! $_[0]->admin->isa('Module::Install::Base::FakeAdmin'); } sub DESTROY {} package Module::Install::Base::FakeAdmin; use vars qw{$VERSION}; BEGIN { $VERSION = $Module::Install::Base::VERSION; } my $fake; sub new { $fake ||= bless(\@_, $_[0]); } sub AUTOLOAD {} sub DESTROY {} # Restore warning handler BEGIN { $SIG{__WARN__} = $SIG{__WARN__}->(); } 1; #line 159 Graph-Easy-0.73/inc/Module/Install/Metadata.pm0000644000076400007640000004327712150107462020774 0ustar shlomifshlomif#line 1 package Module::Install::Metadata; use strict 'vars'; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.06'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } my @boolean_keys = qw{ sign }; my @scalar_keys = qw{ name module_name abstract version distribution_type tests installdirs }; my @tuple_keys = qw{ configure_requires build_requires requires recommends bundles resources }; my @resource_keys = qw{ homepage bugtracker repository }; my @array_keys = qw{ keywords author }; *authors = \&author; sub Meta { shift } sub Meta_BooleanKeys { @boolean_keys } sub Meta_ScalarKeys { @scalar_keys } sub Meta_TupleKeys { @tuple_keys } sub Meta_ResourceKeys { @resource_keys } sub Meta_ArrayKeys { @array_keys } foreach my $key ( @boolean_keys ) { *$key = sub { my $self = shift; if ( defined wantarray and not @_ ) { return $self->{values}->{$key}; } $self->{values}->{$key} = ( @_ ? $_[0] : 1 ); return $self; }; } foreach my $key ( @scalar_keys ) { *$key = sub { my $self = shift; return $self->{values}->{$key} if defined wantarray and !@_; $self->{values}->{$key} = shift; return $self; }; } foreach my $key ( @array_keys ) { *$key = sub { my $self = shift; return $self->{values}->{$key} if defined wantarray and !@_; $self->{values}->{$key} ||= []; push @{$self->{values}->{$key}}, @_; return $self; }; } foreach my $key ( @resource_keys ) { *$key = sub { my $self = shift; unless ( @_ ) { return () unless $self->{values}->{resources}; return map { $_->[1] } grep { $_->[0] eq $key } @{ $self->{values}->{resources} }; } return $self->{values}->{resources}->{$key} unless @_; my $uri = shift or die( "Did not provide a value to $key()" ); $self->resources( $key => $uri ); return 1; }; } foreach my $key ( grep { $_ ne "resources" } @tuple_keys) { *$key = sub { my $self = shift; return $self->{values}->{$key} unless @_; my @added; while ( @_ ) { my $module = shift or last; my $version = shift || 0; push @added, [ $module, $version ]; } push @{ $self->{values}->{$key} }, @added; return map {@$_} @added; }; } # Resource handling my %lc_resource = map { $_ => 1 } qw{ homepage license bugtracker repository }; sub resources { my $self = shift; while ( @_ ) { my $name = shift or last; my $value = shift or next; if ( $name eq lc $name and ! $lc_resource{$name} ) { die("Unsupported reserved lowercase resource '$name'"); } $self->{values}->{resources} ||= []; push @{ $self->{values}->{resources} }, [ $name, $value ]; } $self->{values}->{resources}; } # Aliases for build_requires that will have alternative # meanings in some future version of META.yml. sub test_requires { shift->build_requires(@_) } sub install_requires { shift->build_requires(@_) } # Aliases for installdirs options sub install_as_core { $_[0]->installdirs('perl') } sub install_as_cpan { $_[0]->installdirs('site') } sub install_as_site { $_[0]->installdirs('site') } sub install_as_vendor { $_[0]->installdirs('vendor') } sub dynamic_config { my $self = shift; my $value = @_ ? shift : 1; if ( $self->{values}->{dynamic_config} ) { # Once dynamic we never change to static, for safety return 0; } $self->{values}->{dynamic_config} = $value ? 1 : 0; return 1; } # Convenience command sub static_config { shift->dynamic_config(0); } sub perl_version { my $self = shift; return $self->{values}->{perl_version} unless @_; my $version = shift or die( "Did not provide a value to perl_version()" ); # Normalize the version $version = $self->_perl_version($version); # We don't support the really old versions unless ( $version >= 5.005 ) { die "Module::Install only supports 5.005 or newer (use ExtUtils::MakeMaker)\n"; } $self->{values}->{perl_version} = $version; } sub all_from { my ( $self, $file ) = @_; unless ( defined($file) ) { my $name = $self->name or die( "all_from called with no args without setting name() first" ); $file = join('/', 'lib', split(/-/, $name)) . '.pm'; $file =~ s{.*/}{} unless -e $file; unless ( -e $file ) { die("all_from cannot find $file from $name"); } } unless ( -f $file ) { die("The path '$file' does not exist, or is not a file"); } $self->{values}{all_from} = $file; # Some methods pull from POD instead of code. # If there is a matching .pod, use that instead my $pod = $file; $pod =~ s/\.pm$/.pod/i; $pod = $file unless -e $pod; # Pull the different values $self->name_from($file) unless $self->name; $self->version_from($file) unless $self->version; $self->perl_version_from($file) unless $self->perl_version; $self->author_from($pod) unless @{$self->author || []}; $self->license_from($pod) unless $self->license; $self->abstract_from($pod) unless $self->abstract; return 1; } sub provides { my $self = shift; my $provides = ( $self->{values}->{provides} ||= {} ); %$provides = (%$provides, @_) if @_; return $provides; } sub auto_provides { my $self = shift; return $self unless $self->is_admin; unless (-e 'MANIFEST') { warn "Cannot deduce auto_provides without a MANIFEST, skipping\n"; return $self; } # Avoid spurious warnings as we are not checking manifest here. local $SIG{__WARN__} = sub {1}; require ExtUtils::Manifest; local *ExtUtils::Manifest::manicheck = sub { return }; require Module::Build; my $build = Module::Build->new( dist_name => $self->name, dist_version => $self->version, license => $self->license, ); $self->provides( %{ $build->find_dist_packages || {} } ); } sub feature { my $self = shift; my $name = shift; my $features = ( $self->{values}->{features} ||= [] ); my $mods; if ( @_ == 1 and ref( $_[0] ) ) { # The user used ->feature like ->features by passing in the second # argument as a reference. Accomodate for that. $mods = $_[0]; } else { $mods = \@_; } my $count = 0; push @$features, ( $name => [ map { ref($_) ? ( ref($_) eq 'HASH' ) ? %$_ : @$_ : $_ } @$mods ] ); return @$features; } sub features { my $self = shift; while ( my ( $name, $mods ) = splice( @_, 0, 2 ) ) { $self->feature( $name, @$mods ); } return $self->{values}->{features} ? @{ $self->{values}->{features} } : (); } sub no_index { my $self = shift; my $type = shift; push @{ $self->{values}->{no_index}->{$type} }, @_ if $type; return $self->{values}->{no_index}; } sub read { my $self = shift; $self->include_deps( 'YAML::Tiny', 0 ); require YAML::Tiny; my $data = YAML::Tiny::LoadFile('META.yml'); # Call methods explicitly in case user has already set some values. while ( my ( $key, $value ) = each %$data ) { next unless $self->can($key); if ( ref $value eq 'HASH' ) { while ( my ( $module, $version ) = each %$value ) { $self->can($key)->($self, $module => $version ); } } else { $self->can($key)->($self, $value); } } return $self; } sub write { my $self = shift; return $self unless $self->is_admin; $self->admin->write_meta; return $self; } sub version_from { require ExtUtils::MM_Unix; my ( $self, $file ) = @_; $self->version( ExtUtils::MM_Unix->parse_version($file) ); # for version integrity check $self->makemaker_args( VERSION_FROM => $file ); } sub abstract_from { require ExtUtils::MM_Unix; my ( $self, $file ) = @_; $self->abstract( bless( { DISTNAME => $self->name }, 'ExtUtils::MM_Unix' )->parse_abstract($file) ); } # Add both distribution and module name sub name_from { my ($self, $file) = @_; if ( Module::Install::_read($file) =~ m/ ^ \s* package \s* ([\w:]+) \s* ; /ixms ) { my ($name, $module_name) = ($1, $1); $name =~ s{::}{-}g; $self->name($name); unless ( $self->module_name ) { $self->module_name($module_name); } } else { die("Cannot determine name from $file\n"); } } sub _extract_perl_version { if ( $_[0] =~ m/ ^\s* (?:use|require) \s* v? ([\d_\.]+) \s* ; /ixms ) { my $perl_version = $1; $perl_version =~ s{_}{}g; return $perl_version; } else { return; } } sub perl_version_from { my $self = shift; my $perl_version=_extract_perl_version(Module::Install::_read($_[0])); if ($perl_version) { $self->perl_version($perl_version); } else { warn "Cannot determine perl version info from $_[0]\n"; return; } } sub author_from { my $self = shift; my $content = Module::Install::_read($_[0]); if ($content =~ m/ =head \d \s+ (?:authors?)\b \s* ([^\n]*) | =head \d \s+ (?:licen[cs]e|licensing|copyright|legal)\b \s* .*? copyright .*? \d\d\d[\d.]+ \s* (?:\bby\b)? \s* ([^\n]*) /ixms) { my $author = $1 || $2; # XXX: ugly but should work anyway... if (eval "require Pod::Escapes; 1") { # Pod::Escapes has a mapping table. # It's in core of perl >= 5.9.3, and should be installed # as one of the Pod::Simple's prereqs, which is a prereq # of Pod::Text 3.x (see also below). $author =~ s{ E<( (\d+) | ([A-Za-z]+) )> } { defined $2 ? chr($2) : defined $Pod::Escapes::Name2character_number{$1} ? chr($Pod::Escapes::Name2character_number{$1}) : do { warn "Unknown escape: E<$1>"; "E<$1>"; }; }gex; } elsif (eval "require Pod::Text; 1" && $Pod::Text::VERSION < 3) { # Pod::Text < 3.0 has yet another mapping table, # though the table name of 2.x and 1.x are different. # (1.x is in core of Perl < 5.6, 2.x is in core of # Perl < 5.9.3) my $mapping = ($Pod::Text::VERSION < 2) ? \%Pod::Text::HTML_Escapes : \%Pod::Text::ESCAPES; $author =~ s{ E<( (\d+) | ([A-Za-z]+) )> } { defined $2 ? chr($2) : defined $mapping->{$1} ? $mapping->{$1} : do { warn "Unknown escape: E<$1>"; "E<$1>"; }; }gex; } else { $author =~ s{E}{<}g; $author =~ s{E}{>}g; } $self->author($author); } else { warn "Cannot determine author info from $_[0]\n"; } } #Stolen from M::B my %license_urls = ( perl => 'http://dev.perl.org/licenses/', apache => 'http://apache.org/licenses/LICENSE-2.0', apache_1_1 => 'http://apache.org/licenses/LICENSE-1.1', artistic => 'http://opensource.org/licenses/artistic-license.php', artistic_2 => 'http://opensource.org/licenses/artistic-license-2.0.php', lgpl => 'http://opensource.org/licenses/lgpl-license.php', lgpl2 => 'http://opensource.org/licenses/lgpl-2.1.php', lgpl3 => 'http://opensource.org/licenses/lgpl-3.0.html', bsd => 'http://opensource.org/licenses/bsd-license.php', gpl => 'http://opensource.org/licenses/gpl-license.php', gpl2 => 'http://opensource.org/licenses/gpl-2.0.php', gpl3 => 'http://opensource.org/licenses/gpl-3.0.html', mit => 'http://opensource.org/licenses/mit-license.php', mozilla => 'http://opensource.org/licenses/mozilla1.1.php', open_source => undef, unrestricted => undef, restrictive => undef, unknown => undef, ); sub license { my $self = shift; return $self->{values}->{license} unless @_; my $license = shift or die( 'Did not provide a value to license()' ); $license = __extract_license($license) || lc $license; $self->{values}->{license} = $license; # Automatically fill in license URLs if ( $license_urls{$license} ) { $self->resources( license => $license_urls{$license} ); } return 1; } sub _extract_license { my $pod = shift; my $matched; return __extract_license( ($matched) = $pod =~ m/ (=head \d \s+ L(?i:ICEN[CS]E|ICENSING)\b.*?) (=head \d.*|=cut.*|)\z /xms ) || __extract_license( ($matched) = $pod =~ m/ (=head \d \s+ (?:C(?i:OPYRIGHTS?)|L(?i:EGAL))\b.*?) (=head \d.*|=cut.*|)\z /xms ); } sub __extract_license { my $license_text = shift or return; my @phrases = ( '(?:under )?the same (?:terms|license) as (?:perl|the perl (?:\d )?programming language)' => 'perl', 1, '(?:under )?the terms of (?:perl|the perl programming language) itself' => 'perl', 1, 'Artistic and GPL' => 'perl', 1, 'GNU general public license' => 'gpl', 1, 'GNU public license' => 'gpl', 1, 'GNU lesser general public license' => 'lgpl', 1, 'GNU lesser public license' => 'lgpl', 1, 'GNU library general public license' => 'lgpl', 1, 'GNU library public license' => 'lgpl', 1, 'GNU Free Documentation license' => 'unrestricted', 1, 'GNU Affero General Public License' => 'open_source', 1, '(?:Free)?BSD license' => 'bsd', 1, 'Artistic license 2\.0' => 'artistic_2', 1, 'Artistic license' => 'artistic', 1, 'Apache (?:Software )?license' => 'apache', 1, 'GPL' => 'gpl', 1, 'LGPL' => 'lgpl', 1, 'BSD' => 'bsd', 1, 'Artistic' => 'artistic', 1, 'MIT' => 'mit', 1, 'Mozilla Public License' => 'mozilla', 1, 'Q Public License' => 'open_source', 1, 'OpenSSL License' => 'unrestricted', 1, 'SSLeay License' => 'unrestricted', 1, 'zlib License' => 'open_source', 1, 'proprietary' => 'proprietary', 0, ); while ( my ($pattern, $license, $osi) = splice(@phrases, 0, 3) ) { $pattern =~ s#\s+#\\s+#gs; if ( $license_text =~ /\b$pattern\b/i ) { return $license; } } return ''; } sub license_from { my $self = shift; if (my $license=_extract_license(Module::Install::_read($_[0]))) { $self->license($license); } else { warn "Cannot determine license info from $_[0]\n"; return 'unknown'; } } sub _extract_bugtracker { my @links = $_[0] =~ m#L<( https?\Q://rt.cpan.org/\E[^>]+| https?\Q://github.com/\E[\w_]+/[\w_]+/issues| https?\Q://code.google.com/p/\E[\w_\-]+/issues/list )>#gx; my %links; @links{@links}=(); @links=keys %links; return @links; } sub bugtracker_from { my $self = shift; my $content = Module::Install::_read($_[0]); my @links = _extract_bugtracker($content); unless ( @links ) { warn "Cannot determine bugtracker info from $_[0]\n"; return 0; } if ( @links > 1 ) { warn "Found more than one bugtracker link in $_[0]\n"; return 0; } # Set the bugtracker bugtracker( $links[0] ); return 1; } sub requires_from { my $self = shift; my $content = Module::Install::_readperl($_[0]); my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+(v?[\d\.]+)/mg; while ( @requires ) { my $module = shift @requires; my $version = shift @requires; $self->requires( $module => $version ); } } sub test_requires_from { my $self = shift; my $content = Module::Install::_readperl($_[0]); my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+([\d\.]+)/mg; while ( @requires ) { my $module = shift @requires; my $version = shift @requires; $self->test_requires( $module => $version ); } } # Convert triple-part versions (eg, 5.6.1 or 5.8.9) to # numbers (eg, 5.006001 or 5.008009). # Also, convert double-part versions (eg, 5.8) sub _perl_version { my $v = $_[-1]; $v =~ s/^([1-9])\.([1-9]\d?\d?)$/sprintf("%d.%03d",$1,$2)/e; $v =~ s/^([1-9])\.([1-9]\d?\d?)\.(0|[1-9]\d?\d?)$/sprintf("%d.%03d%03d",$1,$2,$3 || 0)/e; $v =~ s/(\.\d\d\d)000$/$1/; $v =~ s/_.+$//; if ( ref($v) ) { # Numify $v = $v + 0; } return $v; } sub add_metadata { my $self = shift; my %hash = @_; for my $key (keys %hash) { warn "add_metadata: $key is not prefixed with 'x_'.\n" . "Use appopriate function to add non-private metadata.\n" unless $key =~ /^x_/; $self->{values}->{$key} = $hash{$key}; } } ###################################################################### # MYMETA Support sub WriteMyMeta { die "WriteMyMeta has been deprecated"; } sub write_mymeta_yaml { my $self = shift; # We need YAML::Tiny to write the MYMETA.yml file unless ( eval { require YAML::Tiny; 1; } ) { return 1; } # Generate the data my $meta = $self->_write_mymeta_data or return 1; # Save as the MYMETA.yml file print "Writing MYMETA.yml\n"; YAML::Tiny::DumpFile('MYMETA.yml', $meta); } sub write_mymeta_json { my $self = shift; # We need JSON to write the MYMETA.json file unless ( eval { require JSON; 1; } ) { return 1; } # Generate the data my $meta = $self->_write_mymeta_data or return 1; # Save as the MYMETA.yml file print "Writing MYMETA.json\n"; Module::Install::_write( 'MYMETA.json', JSON->new->pretty(1)->canonical->encode($meta), ); } sub _write_mymeta_data { my $self = shift; # If there's no existing META.yml there is nothing we can do return undef unless -f 'META.yml'; # We need Parse::CPAN::Meta to load the file unless ( eval { require Parse::CPAN::Meta; 1; } ) { return undef; } # Merge the perl version into the dependencies my $val = $self->Meta->{values}; my $perl = delete $val->{perl_version}; if ( $perl ) { $val->{requires} ||= []; my $requires = $val->{requires}; # Canonize to three-dot version after Perl 5.6 if ( $perl >= 5.006 ) { $perl =~ s{^(\d+)\.(\d\d\d)(\d*)}{join('.', $1, int($2||0), int($3||0))}e } unshift @$requires, [ perl => $perl ]; } # Load the advisory META.yml file my @yaml = Parse::CPAN::Meta::LoadFile('META.yml'); my $meta = $yaml[0]; # Overwrite the non-configure dependency hashs delete $meta->{requires}; delete $meta->{build_requires}; delete $meta->{recommends}; if ( exists $val->{requires} ) { $meta->{requires} = { map { @$_ } @{ $val->{requires} } }; } if ( exists $val->{build_requires} ) { $meta->{build_requires} = { map { @$_ } @{ $val->{build_requires} } }; } return $meta; } 1; Graph-Easy-0.73/inc/Module/Install/Can.pm0000644000076400007640000000615712150107462017751 0ustar shlomifshlomif#line 1 package Module::Install::Can; use strict; use Config (); use ExtUtils::MakeMaker (); use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.06'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } # check if we can load some module ### Upgrade this to not have to load the module if possible sub can_use { my ($self, $mod, $ver) = @_; $mod =~ s{::|\\}{/}g; $mod .= '.pm' unless $mod =~ /\.pm$/i; my $pkg = $mod; $pkg =~ s{/}{::}g; $pkg =~ s{\.pm$}{}i; local $@; eval { require $mod; $pkg->VERSION($ver || 0); 1 }; } # Check if we can run some command sub can_run { my ($self, $cmd) = @_; my $_cmd = $cmd; return $_cmd if (-x $_cmd or $_cmd = MM->maybe_command($_cmd)); for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), '.') { next if $dir eq ''; require File::Spec; my $abs = File::Spec->catfile($dir, $cmd); return $abs if (-x $abs or $abs = MM->maybe_command($abs)); } return; } # Can our C compiler environment build XS files sub can_xs { my $self = shift; # Ensure we have the CBuilder module $self->configure_requires( 'ExtUtils::CBuilder' => 0.27 ); # Do we have the configure_requires checker? local $@; eval "require ExtUtils::CBuilder;"; if ( $@ ) { # They don't obey configure_requires, so it is # someone old and delicate. Try to avoid hurting # them by falling back to an older simpler test. return $self->can_cc(); } # Do we have a working C compiler my $builder = ExtUtils::CBuilder->new( quiet => 1, ); unless ( $builder->have_compiler ) { # No working C compiler return 0; } # Write a C file representative of what XS becomes require File::Temp; my ( $FH, $tmpfile ) = File::Temp::tempfile( "compilexs-XXXXX", SUFFIX => '.c', ); binmode $FH; print $FH <<'END_C'; #include "EXTERN.h" #include "perl.h" #include "XSUB.h" int main(int argc, char **argv) { return 0; } int boot_sanexs() { return 1; } END_C close $FH; # Can the C compiler access the same headers XS does my @libs = (); my $object = undef; eval { local $^W = 0; $object = $builder->compile( source => $tmpfile, ); @libs = $builder->link( objects => $object, module_name => 'sanexs', ); }; my $result = $@ ? 0 : 1; # Clean up all the build files foreach ( $tmpfile, $object, @libs ) { next unless defined $_; 1 while unlink; } return $result; } # Can we locate a (the) C compiler sub can_cc { my $self = shift; my @chunks = split(/ /, $Config::Config{cc}) or return; # $Config{cc} may contain args; try to find out the program part while (@chunks) { return $self->can_run("@chunks") || (pop(@chunks), next); } return; } # Fix Cygwin bug on maybe_command(); if ( $^O eq 'cygwin' ) { require ExtUtils::MM_Cygwin; require ExtUtils::MM_Win32; if ( ! defined(&ExtUtils::MM_Cygwin::maybe_command) ) { *ExtUtils::MM_Cygwin::maybe_command = sub { my ($self, $file) = @_; if ($file =~ m{^/cygdrive/}i and ExtUtils::MM_Win32->can('maybe_command')) { ExtUtils::MM_Win32->maybe_command($file); } else { ExtUtils::MM_Unix->maybe_command($file); } } } } 1; __END__ #line 236 Graph-Easy-0.73/inc/Module/Install/Scripts.pm0000644000076400007640000000101112150107462020657 0ustar shlomifshlomif#line 1 package Module::Install::Scripts; use strict 'vars'; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.06'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub install_script { my $self = shift; my $args = $self->makemaker_args; my $exe = $args->{EXE_FILES} ||= []; foreach ( @_ ) { if ( -f $_ ) { push @$exe, $_; } elsif ( -d 'script' and -f "script/$_" ) { push @$exe, "script/$_"; } else { die("Cannot find script '$_'"); } } } 1; Graph-Easy-0.73/inc/Module/Install/Makefile.pm0000644000076400007640000002743712150107462020771 0ustar shlomifshlomif#line 1 package Module::Install::Makefile; use strict 'vars'; use ExtUtils::MakeMaker (); use Module::Install::Base (); use Fcntl qw/:flock :seek/; use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.06'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub Makefile { $_[0] } my %seen = (); sub prompt { shift; # Infinite loop protection my @c = caller(); if ( ++$seen{"$c[1]|$c[2]|$_[0]"} > 3 ) { die "Caught an potential prompt infinite loop ($c[1]|$c[2]|$_[0])"; } # In automated testing or non-interactive session, always use defaults if ( ($ENV{AUTOMATED_TESTING} or -! -t STDIN) and ! $ENV{PERL_MM_USE_DEFAULT} ) { local $ENV{PERL_MM_USE_DEFAULT} = 1; goto &ExtUtils::MakeMaker::prompt; } else { goto &ExtUtils::MakeMaker::prompt; } } # Store a cleaned up version of the MakeMaker version, # since we need to behave differently in a variety of # ways based on the MM version. my $makemaker = eval $ExtUtils::MakeMaker::VERSION; # If we are passed a param, do a "newer than" comparison. # Otherwise, just return the MakeMaker version. sub makemaker { ( @_ < 2 or $makemaker >= eval($_[1]) ) ? $makemaker : 0 } # Ripped from ExtUtils::MakeMaker 6.56, and slightly modified # as we only need to know here whether the attribute is an array # or a hash or something else (which may or may not be appendable). my %makemaker_argtype = ( C => 'ARRAY', CONFIG => 'ARRAY', # CONFIGURE => 'CODE', # ignore DIR => 'ARRAY', DL_FUNCS => 'HASH', DL_VARS => 'ARRAY', EXCLUDE_EXT => 'ARRAY', EXE_FILES => 'ARRAY', FUNCLIST => 'ARRAY', H => 'ARRAY', IMPORTS => 'HASH', INCLUDE_EXT => 'ARRAY', LIBS => 'ARRAY', # ignore '' MAN1PODS => 'HASH', MAN3PODS => 'HASH', META_ADD => 'HASH', META_MERGE => 'HASH', PL_FILES => 'HASH', PM => 'HASH', PMLIBDIRS => 'ARRAY', PMLIBPARENTDIRS => 'ARRAY', PREREQ_PM => 'HASH', CONFIGURE_REQUIRES => 'HASH', SKIP => 'ARRAY', TYPEMAPS => 'ARRAY', XS => 'HASH', # VERSION => ['version',''], # ignore # _KEEP_AFTER_FLUSH => '', clean => 'HASH', depend => 'HASH', dist => 'HASH', dynamic_lib=> 'HASH', linkext => 'HASH', macro => 'HASH', postamble => 'HASH', realclean => 'HASH', test => 'HASH', tool_autosplit => 'HASH', # special cases where you can use makemaker_append CCFLAGS => 'APPENDABLE', DEFINE => 'APPENDABLE', INC => 'APPENDABLE', LDDLFLAGS => 'APPENDABLE', LDFROM => 'APPENDABLE', ); sub makemaker_args { my ($self, %new_args) = @_; my $args = ( $self->{makemaker_args} ||= {} ); foreach my $key (keys %new_args) { if ($makemaker_argtype{$key}) { if ($makemaker_argtype{$key} eq 'ARRAY') { $args->{$key} = [] unless defined $args->{$key}; unless (ref $args->{$key} eq 'ARRAY') { $args->{$key} = [$args->{$key}] } push @{$args->{$key}}, ref $new_args{$key} eq 'ARRAY' ? @{$new_args{$key}} : $new_args{$key}; } elsif ($makemaker_argtype{$key} eq 'HASH') { $args->{$key} = {} unless defined $args->{$key}; foreach my $skey (keys %{ $new_args{$key} }) { $args->{$key}{$skey} = $new_args{$key}{$skey}; } } elsif ($makemaker_argtype{$key} eq 'APPENDABLE') { $self->makemaker_append($key => $new_args{$key}); } } else { if (defined $args->{$key}) { warn qq{MakeMaker attribute "$key" is overriden; use "makemaker_append" to append values\n}; } $args->{$key} = $new_args{$key}; } } return $args; } # For mm args that take multiple space-seperated args, # append an argument to the current list. sub makemaker_append { my $self = shift; my $name = shift; my $args = $self->makemaker_args; $args->{$name} = defined $args->{$name} ? join( ' ', $args->{$name}, @_ ) : join( ' ', @_ ); } sub build_subdirs { my $self = shift; my $subdirs = $self->makemaker_args->{DIR} ||= []; for my $subdir (@_) { push @$subdirs, $subdir; } } sub clean_files { my $self = shift; my $clean = $self->makemaker_args->{clean} ||= {}; %$clean = ( %$clean, FILES => join ' ', grep { length $_ } ($clean->{FILES} || (), @_), ); } sub realclean_files { my $self = shift; my $realclean = $self->makemaker_args->{realclean} ||= {}; %$realclean = ( %$realclean, FILES => join ' ', grep { length $_ } ($realclean->{FILES} || (), @_), ); } sub libs { my $self = shift; my $libs = ref $_[0] ? shift : [ shift ]; $self->makemaker_args( LIBS => $libs ); } sub inc { my $self = shift; $self->makemaker_args( INC => shift ); } sub _wanted_t { } sub tests_recursive { my $self = shift; my $dir = shift || 't'; unless ( -d $dir ) { die "tests_recursive dir '$dir' does not exist"; } my %tests = map { $_ => 1 } split / /, ($self->tests || ''); require File::Find; File::Find::find( sub { /\.t$/ and -f $_ and $tests{"$File::Find::dir/*.t"} = 1 }, $dir ); $self->tests( join ' ', sort keys %tests ); } sub write { my $self = shift; die "&Makefile->write() takes no arguments\n" if @_; # Check the current Perl version my $perl_version = $self->perl_version; if ( $perl_version ) { eval "use $perl_version; 1" or die "ERROR: perl: Version $] is installed, " . "but we need version >= $perl_version"; } # Make sure we have a new enough MakeMaker require ExtUtils::MakeMaker; if ( $perl_version and $self->_cmp($perl_version, '5.006') >= 0 ) { # This previous attempted to inherit the version of # ExtUtils::MakeMaker in use by the module author, but this # was found to be untenable as some authors build releases # using future dev versions of EU:MM that nobody else has. # Instead, #toolchain suggests we use 6.59 which is the most # stable version on CPAN at time of writing and is, to quote # ribasushi, "not terminally fucked, > and tested enough". # TODO: We will now need to maintain this over time to push # the version up as new versions are released. $self->build_requires( 'ExtUtils::MakeMaker' => 6.59 ); $self->configure_requires( 'ExtUtils::MakeMaker' => 6.59 ); } else { # Allow legacy-compatibility with 5.005 by depending on the # most recent EU:MM that supported 5.005. $self->build_requires( 'ExtUtils::MakeMaker' => 6.36 ); $self->configure_requires( 'ExtUtils::MakeMaker' => 6.36 ); } # Generate the MakeMaker params my $args = $self->makemaker_args; $args->{DISTNAME} = $self->name; $args->{NAME} = $self->module_name || $self->name; $args->{NAME} =~ s/-/::/g; $args->{VERSION} = $self->version or die <<'EOT'; ERROR: Can't determine distribution version. Please specify it explicitly via 'version' in Makefile.PL, or set a valid $VERSION in a module, and provide its file path via 'version_from' (or 'all_from' if you prefer) in Makefile.PL. EOT if ( $self->tests ) { my @tests = split ' ', $self->tests; my %seen; $args->{test} = { TESTS => (join ' ', grep {!$seen{$_}++} @tests), }; } elsif ( $Module::Install::ExtraTests::use_extratests ) { # Module::Install::ExtraTests doesn't set $self->tests and does its own tests via harness. # So, just ignore our xt tests here. } elsif ( -d 'xt' and ($Module::Install::AUTHOR or $ENV{RELEASE_TESTING}) ) { $args->{test} = { TESTS => join( ' ', map { "$_/*.t" } grep { -d $_ } qw{ t xt } ), }; } if ( $] >= 5.005 ) { $args->{ABSTRACT} = $self->abstract; $args->{AUTHOR} = join ', ', @{$self->author || []}; } if ( $self->makemaker(6.10) ) { $args->{NO_META} = 1; #$args->{NO_MYMETA} = 1; } if ( $self->makemaker(6.17) and $self->sign ) { $args->{SIGN} = 1; } unless ( $self->is_admin ) { delete $args->{SIGN}; } if ( $self->makemaker(6.31) and $self->license ) { $args->{LICENSE} = $self->license; } my $prereq = ($args->{PREREQ_PM} ||= {}); %$prereq = ( %$prereq, map { @$_ } # flatten [module => version] map { @$_ } grep $_, ($self->requires) ); # Remove any reference to perl, PREREQ_PM doesn't support it delete $args->{PREREQ_PM}->{perl}; # Merge both kinds of requires into BUILD_REQUIRES my $build_prereq = ($args->{BUILD_REQUIRES} ||= {}); %$build_prereq = ( %$build_prereq, map { @$_ } # flatten [module => version] map { @$_ } grep $_, ($self->configure_requires, $self->build_requires) ); # Remove any reference to perl, BUILD_REQUIRES doesn't support it delete $args->{BUILD_REQUIRES}->{perl}; # Delete bundled dists from prereq_pm, add it to Makefile DIR my $subdirs = ($args->{DIR} || []); if ($self->bundles) { my %processed; foreach my $bundle (@{ $self->bundles }) { my ($mod_name, $dist_dir) = @$bundle; delete $prereq->{$mod_name}; $dist_dir = File::Basename::basename($dist_dir); # dir for building this module if (not exists $processed{$dist_dir}) { if (-d $dist_dir) { # List as sub-directory to be processed by make push @$subdirs, $dist_dir; } # Else do nothing: the module is already present on the system $processed{$dist_dir} = undef; } } } unless ( $self->makemaker('6.55_03') ) { %$prereq = (%$prereq,%$build_prereq); delete $args->{BUILD_REQUIRES}; } if ( my $perl_version = $self->perl_version ) { eval "use $perl_version; 1" or die "ERROR: perl: Version $] is installed, " . "but we need version >= $perl_version"; if ( $self->makemaker(6.48) ) { $args->{MIN_PERL_VERSION} = $perl_version; } } if ($self->installdirs) { warn qq{old INSTALLDIRS (probably set by makemaker_args) is overriden by installdirs\n} if $args->{INSTALLDIRS}; $args->{INSTALLDIRS} = $self->installdirs; } my %args = map { ( $_ => $args->{$_} ) } grep {defined($args->{$_} ) } keys %$args; my $user_preop = delete $args{dist}->{PREOP}; if ( my $preop = $self->admin->preop($user_preop) ) { foreach my $key ( keys %$preop ) { $args{dist}->{$key} = $preop->{$key}; } } my $mm = ExtUtils::MakeMaker::WriteMakefile(%args); $self->fix_up_makefile($mm->{FIRST_MAKEFILE} || 'Makefile'); } sub fix_up_makefile { my $self = shift; my $makefile_name = shift; my $top_class = ref($self->_top) || ''; my $top_version = $self->_top->VERSION || ''; my $preamble = $self->preamble ? "# Preamble by $top_class $top_version\n" . $self->preamble : ''; my $postamble = "# Postamble by $top_class $top_version\n" . ($self->postamble || ''); local *MAKEFILE; open MAKEFILE, "+< $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!"; eval { flock MAKEFILE, LOCK_EX }; my $makefile = do { local $/; }; $makefile =~ s/\b(test_harness\(\$\(TEST_VERBOSE\), )/$1'inc', /; $makefile =~ s/( -I\$\(INST_ARCHLIB\))/ -Iinc$1/g; $makefile =~ s/( "-I\$\(INST_LIB\)")/ "-Iinc"$1/g; $makefile =~ s/^(FULLPERL = .*)/$1 "-Iinc"/m; $makefile =~ s/^(PERL = .*)/$1 "-Iinc"/m; # Module::Install will never be used to build the Core Perl # Sometimes PERL_LIB and PERL_ARCHLIB get written anyway, which breaks # PREFIX/PERL5LIB, and thus, install_share. Blank them if they exist $makefile =~ s/^PERL_LIB = .+/PERL_LIB =/m; #$makefile =~ s/^PERL_ARCHLIB = .+/PERL_ARCHLIB =/m; # Perl 5.005 mentions PERL_LIB explicitly, so we have to remove that as well. $makefile =~ s/(\"?)-I\$\(PERL_LIB\)\1//g; # XXX - This is currently unused; not sure if it breaks other MM-users # $makefile =~ s/^pm_to_blib\s+:\s+/pm_to_blib :: /mg; seek MAKEFILE, 0, SEEK_SET; truncate MAKEFILE, 0; print MAKEFILE "$preamble$makefile$postamble" or die $!; close MAKEFILE or die $!; 1; } sub preamble { my ($self, $text) = @_; $self->{preamble} = $text . $self->{preamble} if defined $text; $self->{preamble}; } sub postamble { my ($self, $text) = @_; $self->{postamble} ||= $self->admin->postamble; $self->{postamble} .= $text if defined $text; $self->{postamble} } 1; __END__ #line 544 Graph-Easy-0.73/inc/Module/Install/WriteAll.pm0000644000076400007640000000237612150107462020772 0ustar shlomifshlomif#line 1 package Module::Install::WriteAll; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.06'; @ISA = qw{Module::Install::Base}; $ISCORE = 1; } sub WriteAll { my $self = shift; my %args = ( meta => 1, sign => 0, inline => 0, check_nmake => 1, @_, ); $self->sign(1) if $args{sign}; $self->admin->WriteAll(%args) if $self->is_admin; $self->check_nmake if $args{check_nmake}; unless ( $self->makemaker_args->{PL_FILES} ) { # XXX: This still may be a bit over-defensive... unless ($self->makemaker(6.25)) { $self->makemaker_args( PL_FILES => {} ) if -f 'Build.PL'; } } # Until ExtUtils::MakeMaker support MYMETA.yml, make sure # we clean it up properly ourself. $self->realclean_files('MYMETA.yml'); if ( $args{inline} ) { $self->Inline->write; } else { $self->Makefile->write; } # The Makefile write process adds a couple of dependencies, # so write the META.yml files after the Makefile. if ( $args{meta} ) { $self->Meta->write; } # Experimental support for MYMETA if ( $ENV{X_MYMETA} ) { if ( $ENV{X_MYMETA} eq 'JSON' ) { $self->Meta->write_mymeta_json; } else { $self->Meta->write_mymeta_yaml; } } return 1; } 1; Graph-Easy-0.73/inc/Module/Install.pm0000644000076400007640000003013512150107456017244 0ustar shlomifshlomif#line 1 package Module::Install; # For any maintainers: # The load order for Module::Install is a bit magic. # It goes something like this... # # IF ( host has Module::Install installed, creating author mode ) { # 1. Makefile.PL calls "use inc::Module::Install" # 2. $INC{inc/Module/Install.pm} set to installed version of inc::Module::Install # 3. The installed version of inc::Module::Install loads # 4. inc::Module::Install calls "require Module::Install" # 5. The ./inc/ version of Module::Install loads # } ELSE { # 1. Makefile.PL calls "use inc::Module::Install" # 2. $INC{inc/Module/Install.pm} set to ./inc/ version of Module::Install # 3. The ./inc/ version of Module::Install loads # } use 5.005; use strict 'vars'; use Cwd (); use File::Find (); use File::Path (); use vars qw{$VERSION $MAIN}; BEGIN { # All Module::Install core packages now require synchronised versions. # This will be used to ensure we don't accidentally load old or # different versions of modules. # This is not enforced yet, but will be some time in the next few # releases once we can make sure it won't clash with custom # Module::Install extensions. $VERSION = '1.06'; # Storage for the pseudo-singleton $MAIN = undef; *inc::Module::Install::VERSION = *VERSION; @inc::Module::Install::ISA = __PACKAGE__; } sub import { my $class = shift; my $self = $class->new(@_); my $who = $self->_caller; #------------------------------------------------------------- # all of the following checks should be included in import(), # to allow "eval 'require Module::Install; 1' to test # installation of Module::Install. (RT #51267) #------------------------------------------------------------- # Whether or not inc::Module::Install is actually loaded, the # $INC{inc/Module/Install.pm} is what will still get set as long as # the caller loaded module this in the documented manner. # If not set, the caller may NOT have loaded the bundled version, and thus # they may not have a MI version that works with the Makefile.PL. This would # result in false errors or unexpected behaviour. And we don't want that. my $file = join( '/', 'inc', split /::/, __PACKAGE__ ) . '.pm'; unless ( $INC{$file} ) { die <<"END_DIE" } Please invoke ${\__PACKAGE__} with: use inc::${\__PACKAGE__}; not: use ${\__PACKAGE__}; END_DIE # This reportedly fixes a rare Win32 UTC file time issue, but # as this is a non-cross-platform XS module not in the core, # we shouldn't really depend on it. See RT #24194 for detail. # (Also, this module only supports Perl 5.6 and above). eval "use Win32::UTCFileTime" if $^O eq 'MSWin32' && $] >= 5.006; # If the script that is loading Module::Install is from the future, # then make will detect this and cause it to re-run over and over # again. This is bad. Rather than taking action to touch it (which # is unreliable on some platforms and requires write permissions) # for now we should catch this and refuse to run. if ( -f $0 ) { my $s = (stat($0))[9]; # If the modification time is only slightly in the future, # sleep briefly to remove the problem. my $a = $s - time; if ( $a > 0 and $a < 5 ) { sleep 5 } # Too far in the future, throw an error. my $t = time; if ( $s > $t ) { die <<"END_DIE" } Your installer $0 has a modification time in the future ($s > $t). This is known to create infinite loops in make. Please correct this, then run $0 again. END_DIE } # Build.PL was formerly supported, but no longer is due to excessive # difficulty in implementing every single feature twice. if ( $0 =~ /Build.PL$/i ) { die <<"END_DIE" } Module::Install no longer supports Build.PL. It was impossible to maintain duel backends, and has been deprecated. Please remove all Build.PL files and only use the Makefile.PL installer. END_DIE #------------------------------------------------------------- # To save some more typing in Module::Install installers, every... # use inc::Module::Install # ...also acts as an implicit use strict. $^H |= strict::bits(qw(refs subs vars)); #------------------------------------------------------------- unless ( -f $self->{file} ) { foreach my $key (keys %INC) { delete $INC{$key} if $key =~ /Module\/Install/; } local $^W; require "$self->{path}/$self->{dispatch}.pm"; File::Path::mkpath("$self->{prefix}/$self->{author}"); $self->{admin} = "$self->{name}::$self->{dispatch}"->new( _top => $self ); $self->{admin}->init; @_ = ($class, _self => $self); goto &{"$self->{name}::import"}; } local $^W; *{"${who}::AUTOLOAD"} = $self->autoload; $self->preload; # Unregister loader and worker packages so subdirs can use them again delete $INC{'inc/Module/Install.pm'}; delete $INC{'Module/Install.pm'}; # Save to the singleton $MAIN = $self; return 1; } sub autoload { my $self = shift; my $who = $self->_caller; my $cwd = Cwd::cwd(); my $sym = "${who}::AUTOLOAD"; $sym->{$cwd} = sub { my $pwd = Cwd::cwd(); if ( my $code = $sym->{$pwd} ) { # Delegate back to parent dirs goto &$code unless $cwd eq $pwd; } unless ($$sym =~ s/([^:]+)$//) { # XXX: it looks like we can't retrieve the missing function # via $$sym (usually $main::AUTOLOAD) in this case. # I'm still wondering if we should slurp Makefile.PL to # get some context or not ... my ($package, $file, $line) = caller; die <<"EOT"; Unknown function is found at $file line $line. Execution of $file aborted due to runtime errors. If you're a contributor to a project, you may need to install some Module::Install extensions from CPAN (or other repository). If you're a user of a module, please contact the author. EOT } my $method = $1; if ( uc($method) eq $method ) { # Do nothing return; } elsif ( $method =~ /^_/ and $self->can($method) ) { # Dispatch to the root M:I class return $self->$method(@_); } # Dispatch to the appropriate plugin unshift @_, ( $self, $1 ); goto &{$self->can('call')}; }; } sub preload { my $self = shift; unless ( $self->{extensions} ) { $self->load_extensions( "$self->{prefix}/$self->{path}", $self ); } my @exts = @{$self->{extensions}}; unless ( @exts ) { @exts = $self->{admin}->load_all_extensions; } my %seen; foreach my $obj ( @exts ) { while (my ($method, $glob) = each %{ref($obj) . '::'}) { next unless $obj->can($method); next if $method =~ /^_/; next if $method eq uc($method); $seen{$method}++; } } my $who = $self->_caller; foreach my $name ( sort keys %seen ) { local $^W; *{"${who}::$name"} = sub { ${"${who}::AUTOLOAD"} = "${who}::$name"; goto &{"${who}::AUTOLOAD"}; }; } } sub new { my ($class, %args) = @_; delete $INC{'FindBin.pm'}; { # to suppress the redefine warning local $SIG{__WARN__} = sub {}; require FindBin; } # ignore the prefix on extension modules built from top level. my $base_path = Cwd::abs_path($FindBin::Bin); unless ( Cwd::abs_path(Cwd::cwd()) eq $base_path ) { delete $args{prefix}; } return $args{_self} if $args{_self}; $args{dispatch} ||= 'Admin'; $args{prefix} ||= 'inc'; $args{author} ||= ($^O eq 'VMS' ? '_author' : '.author'); $args{bundle} ||= 'inc/BUNDLES'; $args{base} ||= $base_path; $class =~ s/^\Q$args{prefix}\E:://; $args{name} ||= $class; $args{version} ||= $class->VERSION; unless ( $args{path} ) { $args{path} = $args{name}; $args{path} =~ s!::!/!g; } $args{file} ||= "$args{base}/$args{prefix}/$args{path}.pm"; $args{wrote} = 0; bless( \%args, $class ); } sub call { my ($self, $method) = @_; my $obj = $self->load($method) or return; splice(@_, 0, 2, $obj); goto &{$obj->can($method)}; } sub load { my ($self, $method) = @_; $self->load_extensions( "$self->{prefix}/$self->{path}", $self ) unless $self->{extensions}; foreach my $obj (@{$self->{extensions}}) { return $obj if $obj->can($method); } my $admin = $self->{admin} or die <<"END_DIE"; The '$method' method does not exist in the '$self->{prefix}' path! Please remove the '$self->{prefix}' directory and run $0 again to load it. END_DIE my $obj = $admin->load($method, 1); push @{$self->{extensions}}, $obj; $obj; } sub load_extensions { my ($self, $path, $top) = @_; my $should_reload = 0; unless ( grep { ! ref $_ and lc $_ eq lc $self->{prefix} } @INC ) { unshift @INC, $self->{prefix}; $should_reload = 1; } foreach my $rv ( $self->find_extensions($path) ) { my ($file, $pkg) = @{$rv}; next if $self->{pathnames}{$pkg}; local $@; my $new = eval { local $^W; require $file; $pkg->can('new') }; unless ( $new ) { warn $@ if $@; next; } $self->{pathnames}{$pkg} = $should_reload ? delete $INC{$file} : $INC{$file}; push @{$self->{extensions}}, &{$new}($pkg, _top => $top ); } $self->{extensions} ||= []; } sub find_extensions { my ($self, $path) = @_; my @found; File::Find::find( sub { my $file = $File::Find::name; return unless $file =~ m!^\Q$path\E/(.+)\.pm\Z!is; my $subpath = $1; return if lc($subpath) eq lc($self->{dispatch}); $file = "$self->{path}/$subpath.pm"; my $pkg = "$self->{name}::$subpath"; $pkg =~ s!/!::!g; # If we have a mixed-case package name, assume case has been preserved # correctly. Otherwise, root through the file to locate the case-preserved # version of the package name. if ( $subpath eq lc($subpath) || $subpath eq uc($subpath) ) { my $content = Module::Install::_read($subpath . '.pm'); my $in_pod = 0; foreach ( split //, $content ) { $in_pod = 1 if /^=\w/; $in_pod = 0 if /^=cut/; next if ($in_pod || /^=cut/); # skip pod text next if /^\s*#/; # and comments if ( m/^\s*package\s+($pkg)\s*;/i ) { $pkg = $1; last; } } } push @found, [ $file, $pkg ]; }, $path ) if -d $path; @found; } ##################################################################### # Common Utility Functions sub _caller { my $depth = 0; my $call = caller($depth); while ( $call eq __PACKAGE__ ) { $depth++; $call = caller($depth); } return $call; } # Done in evals to avoid confusing Perl::MinimumVersion eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@; sub _read { local *FH; open( FH, '<', $_[0] ) or die "open($_[0]): $!"; my $string = do { local $/; }; close FH or die "close($_[0]): $!"; return $string; } END_NEW sub _read { local *FH; open( FH, "< $_[0]" ) or die "open($_[0]): $!"; my $string = do { local $/; }; close FH or die "close($_[0]): $!"; return $string; } END_OLD sub _readperl { my $string = Module::Install::_read($_[0]); $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg; $string =~ s/(\n)\n*__(?:DATA|END)__\b.*\z/$1/s; $string =~ s/\n\n=\w+.+?\n\n=cut\b.+?\n+/\n\n/sg; return $string; } sub _readpod { my $string = Module::Install::_read($_[0]); $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg; return $string if $_[0] =~ /\.pod\z/; $string =~ s/(^|\n=cut\b.+?\n+)[^=\s].+?\n(\n=\w+|\z)/$1$2/sg; $string =~ s/\n*=pod\b[^\n]*\n+/\n\n/sg; $string =~ s/\n*=cut\b[^\n]*\n+/\n\n/sg; $string =~ s/^\n+//s; return $string; } # Done in evals to avoid confusing Perl::MinimumVersion eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@; sub _write { local *FH; open( FH, '>', $_[0] ) or die "open($_[0]): $!"; foreach ( 1 .. $#_ ) { print FH $_[$_] or die "print($_[0]): $!"; } close FH or die "close($_[0]): $!"; } END_NEW sub _write { local *FH; open( FH, "> $_[0]" ) or die "open($_[0]): $!"; foreach ( 1 .. $#_ ) { print FH $_[$_] or die "print($_[0]): $!"; } close FH or die "close($_[0]): $!"; } END_OLD # _version is for processing module versions (eg, 1.03_05) not # Perl versions (eg, 5.8.1). sub _version ($) { my $s = shift || 0; my $d =()= $s =~ /(\.)/g; if ( $d >= 2 ) { # Normalise multipart versions $s =~ s/(\.)(\d{1,3})/sprintf("$1%03d",$2)/eg; } $s =~ s/^(\d+)\.?//; my $l = $1 || 0; my @v = map { $_ . '0' x (3 - length $_) } $s =~ /(\d{1,3})\D?/g; $l = $l . '.' . join '', @v if @v; return $l + 0; } sub _cmp ($$) { _version($_[1]) <=> _version($_[2]); } # Cloned from Params::Util::_CLASS sub _CLASS ($) { ( defined $_[0] and ! ref $_[0] and $_[0] =~ m/^[^\W\d]\w*(?:::\w+)*\z/s ) ? $_[0] : undef; } 1; # Copyright 2008 - 2012 Adam Kennedy. Graph-Easy-0.73/Makefile.PL0000644000076400007640000000117511675050456015266 0ustar shlomifshlomif # We need at least Perl 5.8.2 for proper Unicode support require 5.008002; use strict; # Load the Module::Install bundled in ./inc/ use inc::Module::Install; # Get most of the details from the primary module all_from 'lib/Graph/Easy.pm'; requires 'Scalar::Util' => 1.13; requires 'perl' => 5.008002; recommends 'Graph::Easy::As_svg' => 0.23; build_requires 'Test::More' => 0.62; license 'gpl'; author 'Tels '; install_script 'bin/graph-easy'; # Do not index these no_index directory => 'examples'; no_index directory => 'bench'; no_index directory => 'todos'; # Generate the Makefile WriteAll; Graph-Easy-0.73/META.yml0000644000076400007640000000136312150107463014552 0ustar shlomifshlomif--- abstract: 'Convert or render graphs (as ASCII, HTML, SVG or via Graphviz)' author: - '- 2008 by Tels L' - 'Tels ' build_requires: ExtUtils::MakeMaker: 6.59 Test::More: 0.62 configure_requires: ExtUtils::MakeMaker: 6.59 distribution_type: module dynamic_config: 1 generated_by: 'Module::Install version 1.06' license: gpl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 module_name: Graph::Easy name: Graph-Easy no_index: directory: - bench - examples - inc - t recommends: Graph::Easy::As_svg: 0.23 requires: Scalar::Util: 1.13 perl: 5.008002 resources: license: http://opensource.org/licenses/gpl-license.php version: 0.72 Graph-Easy-0.73/INSTALL0000644000076400007640000000314111675050456014340 0ustar shlomifshlomif =pod =head1 Graph-Easy I This package was formerly known as Graph::Simple. =head1 INSTALLATION =head2 Linux, Unix, and similiar systems: To install this module type the following: Untar the package: tar -xzf Graph-Easy-x.xx.tar.gz where x.xx is the current revision. Then change into the directory: chdir Graph-Easy-x.xx/ Verify the package: cpansign --verify If the package fails to verify, do NOT run Makefile.pl nor install it and notify me immidiately. B<< YOUR SYSTEM MIGHT GET COMPROMISED! >> When the package verified okay, then proceed with creating the make file and running the testsuite: perl Makefile.PL make test If all tests pass, install the package as root user: sudo make install =head2 Windows You need two things under Windows: =over 2 =item Perl You can get it from ActiveState: http://activestate.com/store/activeperl/download =item nmake See here for how to get and install nmake: http://johnbokma.com/perl/make-for-windows.html =back After installing C and C, you can install Graph::Easy normally, just replacing C with C in the install instructions above: perl Makefile.PL nmake nmake test nmake install =head1 SEE ALSO You also might want to install the following packages from CPAN: Graph::Easy::As_svg provide SVG (Scalable Vector Graphics) output Graph::Easy::Manual comprehensive manual in POD and HTML =head1 AUTHOR Copyright (C) 2004 - 2007 by Tels L This library is free software; you can redistribute it and/or modify it under the same terms of the GPL version 2. =cut Graph-Easy-0.73/MANIFEST.SKIP0000644000076400007640000000041011675112636015200 0ustar shlomifshlomif^blib.* ^fun\z ^gdl ^Graph-Easy-[0-9] ^Makefile.(old|bak)\z ^Makefile\z ^MYMETA\.yml\z ^MYMETA\.json\z ^[^\\\/]*\.pl pm_to_blib \.svn .*\.tar\.gz tmon.out ^todos[\\\/] ^[\w\._-]+\.(html|txt|png|gif|dot|pl|svg|old|bak|org|vcg|gdl|ps|graphml) ^wikicrawl ~\z .*\.swp Graph-Easy-0.73/README0000644000076400007640000000413011675050456014166 0ustar shlomifshlomifGraph-Easy ========== This module lets you create graphs (nodes/vertices connected by edges/arcs, not pie charts!) and then lay them out on a flat surface. Once laid out, the graph can be converted into various output formats like ASCII art, HTML or SVG. You can also output the graph in graphviz format and let dot/neato/circo etc. do the layout for you. Graphs can be either generated by Perl code, parsed from a simple text format that is human readable and maintainable, or parsed from Graphviz code. For instance this input: [ Bonn ] -> [ Berlin ] [ Berlin ] -> [ Frankfurt ] { border: 1px dotted black; } [ Frankfurt ] -> [ Dresden ] [ Berlin ] ..> [ Potsdam ] [ Potsdam ] => [ Cottbus ] would be rendered in ASCII as: +------+ +--------+ ............. +---------+ | Bonn | --> | Berlin | --> : Frankfurt : --> | Dresden | +------+ +--------+ ............. +---------+ : : v +---------+ +---------+ | Potsdam | ==> | Cottbus | +---------+ +---------+ The HTML or SVG output would look similiar except be more pretty :o) Manual ====== The manual is contained in the extra package Graph::Easy::Manual, which also contains a Pod2HTML converter, that can handle embedded graphs in POD files. You can also view the manual online at: http://bloodgate.com/perl/graph/manual/ Many more examples and documentation, especially on integrating this into a Mediawiki installation, can be found at: http://bloodgate.com/perl/graph/ Have fun! SVG Output ========== You also might want to install Graph::Easy::As_svg from CPAN, it provides you with the ability to generate SVG (Scalable Vector Graphics) files. Installation ============ See INSTALL on how to install this module. AUTHOR ====== Copyright (C) 2004 - 2008 by Tels http://bloodgate.com/ This library is free software; you can redistribute it and/or modify it under the same terms of the GPL version 2. This module was formerly known as Graph-Simple, but has been renamed because it can also easily create non-simple graphs. Graph-Easy-0.73/t/0000755000076400007640000000000012150110221013523 5ustar shlomifshlomifGraph-Easy-0.73/t/stress/0000755000076400007640000000000012150110221015046 5ustar shlomifshlomifGraph-Easy-0.73/t/stress/0020.txt0000644000076400007640000000021711675050456016220 0ustar shlomifshlomif# bend edges with labels [ Ahrweiler ] -- Eifel --> [ Lahn ] [ Lahn ] == Eifel ==> [ Lahnstein ] [ Lahnstein ] -- Eifel --> [ Ahrweiler ] Graph-Easy-0.73/t/stress/0012.txt0000644000076400007640000000026711675050456016226 0ustar shlomifshlomif# placement near other nodes [ Oberammergau ] -> [ Regensburg ] [ Regensburg ] -> [ Wasserburg ] [ Wasserburg ] -> [ Bremen ] [ Siegburg ] -> [ Bremen ] [ Kiel ] -> [ Bremen ] Graph-Easy-0.73/t/stress/0011.txt0000644000076400007640000000023711675050456016222 0ustar shlomifshlomif# placement near other nodes [ Oberammergau ] -> [ Regensburg ] [ Regensburg ] -> [ Wasserburg ] [ Wasserburg ] -> [ Bremen ] [ Siegburg ] -> [ Bremen ] Graph-Easy-0.73/t/stress/drop.txt0000644000076400007640000000021511675050456016601 0ustar shlomifshlomif [ Bonn ] --> [ Berlin ] --> [ Frankfurt ] [ Bonn ] --> [ Berlin ] --> [ Bonn ] [ Ulm ] --> [ Berlin ] --> [ Berlin ] [ Mainz ] --> [ Bonn ] Graph-Easy-0.73/t/stress/0005.txt0000644000076400007640000000015511675050456016224 0ustar shlomifshlomif# 3 nodes linked in a triangle [ Zwenkau ] -> [ Borna ] [ Borna ] -> [ Riesa ] [ Riesa ] -> [ Zwenkau ] Graph-Easy-0.73/t/stress/0003.txt0000644000076400007640000000042211675050456016217 0ustar shlomifshlomif# six nodes, and one edge with a bend [ Oranienburg ] -> [ Henningsdorf ] [ Oranienburg ] -> [ Herzberg ] [ Henningsdorf ] -> [ Strausberg ] [ Strausberg ] -> [ Herzberg ] [ Strausberg ] -> [ Neuruppin ] [ Oranienburg ] -> [ Potsdam ] [ Neuruppin ] -> [ Herzberg ] Graph-Easy-0.73/t/stress/0004.txt0000644000076400007640000000023211675050456016217 0ustar shlomifshlomif# star - 4 nodes around a center [ Berlin ] -> [ Rostock ] [ Berlin ] -> [ Dresden ] [ Berlin ] -> [ Magdeburg ] [ Berlin ] -> [ Frankfurt (Oder) ] Graph-Easy-0.73/t/stress/0006.txt0000644000076400007640000000024311675050456016223 0ustar shlomifshlomif# 5 nodes linked in a circle [ Oberammergau ] -> [ Regensburg ] [ Regensburg ] -> [ Wasserburg ] [ Wasserburg ] -> [ Bremen ] [ Bremen ] -> [ Oberammergau ] Graph-Easy-0.73/t/stress/0002.txt0000644000076400007640000000037111675050456016221 0ustar shlomifshlomif# six nodes, possible to do without a bend [ Oranienburg ] -> [ Henningsdorf ] [ Oranienburg ] -> [ Herzberg ] [ Henningsdorf ] -> [ Strausberg ] [ Strausberg ] -> [ Herzberg ] [ Strausberg ] -> [ Neuruppin ] [ Oranienburg ] -> [ Postdam ] Graph-Easy-0.73/t/stress/anon.txt0000644000076400007640000000034211675050456016571 0ustar shlomifshlomifnode.anon { fill: #ffe0e0; } [ ] --> [ ] { border-style: bold; } --> [ ] { border-color: blue; border-style: solid; } --> [ ] { border-color: blue; } --> [ 1 ] { label: " "; border: none; } --> [ 2 ] { shape: invisible; } Graph-Easy-0.73/t/stress/0010.txt0000644000076400007640000000044211675050456016217 0ustar shlomifshlomif# placement near other nodes [ Oberammergau ] -> [ Regensburg ] [ Regensburg ] -> [ Wasserburg ] [ Wasserburg ] -> [ Bremen ] [ Regensburg ] -> [ Siegburg ] # to be placed around Bremen and Oberammergau [ Oberammergau ] -> [ Kiel ] [ Bremen ] -> [ Kiel ] [ Siegburg ] -> [ Kiel ] Graph-Easy-0.73/t/stress/0001.txt0000644000076400007640000000036211675050456016220 0ustar shlomifshlomif# six nodes, and one long edge [ Oranienburg ] -> [ Henningsdorf ] [ Henningsdorf ] -> [ Strausberg ] [ Oranienburg ] -> [ Wittenberge ] [ Strausberg ] -> [ Herzberg ] [ Strausberg ] -> [ Neuruppin ] [ Wittenberge ] -> [ Herzberg ] Graph-Easy-0.73/t/graph.t0000644000076400007640000001752512150107274015041 0ustar shlomifshlomif#!/usr/bin/perl -w use Test::More; use strict; BEGIN { plan tests => 72; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy") or die($@); }; can_ok ('Graph::Easy', qw/ new _init timeout strict output_format output seed randomize debug border_attribute anon_nodes /); ############################################################################# my $graph = Graph::Easy->new(); is (ref($graph), 'Graph::Easy'); ok (defined $graph->{seed}, 'seed was initialized'); is ($graph->error(), '', 'no error yet'); is ($graph->output_format(), 'html', 'default output format is html'); is ($graph->timeout(), 5, '5 seconds'); is ($graph->strict(), 1, 'is strict'); is ($graph->nodes(), 0, '0 nodes'); is ($graph->edges(), 0, '0 edges'); is ($graph->border_attribute('graph'), 'none', 'graph border is none'); is ($graph->border_attribute('group'), 'dashed', 'group border is dashed 1px black'); is ($graph->border_attribute('node'), 'solid', 'node border is solid 1px black'); is (join (',', $graph->edges()), '', '0 edges'); like ($graph->output(), qr/table/, 'default output worked'); my $bonn = Graph::Easy::Node->new( name => 'Bonn' ); my $berlin = Graph::Easy::Node->new( 'Berlin' ); my $edge = $graph->add_edge ($bonn, $berlin); is (ref($edge), 'Graph::Easy::Edge', 'add_edge() returns the new edge'); is ($graph->nodes(), 2, '2 nodes added'); is ($graph->edges(), 1, '1 edge'); is ($graph->as_txt(), "[ Bonn ] --> [ Berlin ]\n", 'as_txt for 2 nodes'); is (ref($graph->edge($bonn,$berlin)), 'Graph::Easy::Edge', 'edge from objects'); is ($graph->edge($berlin,$bonn), undef, 'berlin not connecting to bonn'); is (ref($graph->edge('Bonn', 'Berlin')), 'Graph::Easy::Edge', 'edge from names'); my @E = $graph->edges(); my $en = ''; for my $e (@E) { $en .= $e->style() . '.'; } is ($en, 'solid.', 'edges() in list context'); ############################################################################# my $ffm = Graph::Easy::Node->new( name => 'Frankfurt a. M.' ); # test add_edge ($n1,$n2, $label) $graph->add_edge ($ffm, $bonn, 'train'); is ($graph->nodes (), 3, '3 nodes'); is ($graph->edges (), 2, '2 edges'); my $e = $graph->edge ($ffm,$bonn); is ($e->label(), 'train', 'add_edge($n,$n2,"label") works'); # print $graph->as_ascii(); ############################################################################# # as_txt() (simple nodes) is ( $graph->as_txt(), <<'HERE', [ Frankfurt a. M. ] -- train --> [ Bonn ] [ Bonn ] --> [ Berlin ] HERE 'as_txt() for 3 nodes with 2 edges'); my $schweinfurt = Graph::Easy::Node->new( name => 'Schweinfurt' ); $graph->add_edge ($schweinfurt, $bonn); is ($graph->nodes (), 4, '4 nodes'); is ($graph->edges (), 3, '3 edges'); is ( $graph->as_txt(), <<'HERE', [ Frankfurt a. M. ] -- train --> [ Bonn ] [ Schweinfurt ] --> [ Bonn ] [ Bonn ] --> [ Berlin ] HERE 'as_txt() for 4 nodes with 3 edges'); ############################################################################# # as_txt() (nodes with attributes) $bonn->set_attribute('class', 'cities'); is ( $graph->as_txt(), <<'HERE' [ Bonn ] { class: cities; } [ Frankfurt a. M. ] -- train --> [ Bonn ] [ Schweinfurt ] --> [ Bonn ] [ Bonn ] --> [ Berlin ] HERE , 'as_txt() for 4 nodes with 3 edges and attributes'); $bonn->set_attribute('border', 'none'); $bonn->set_attribute('color', 'red'); $berlin->set_attribute('color', 'blue'); is ($bonn->attribute('borderstyle'), 'none', 'borderstyle set to none'); is ($bonn->attribute('border'), 'none', 'border set to none'); is ($bonn->border_attribute(), 'none', 'border set to none'); # border is second-to-last, class is the last attribute: is ( $graph->as_txt(), <<'HERE' [ Berlin ] { color: blue; } [ Bonn ] { color: red; border: none; class: cities; } [ Frankfurt a. M. ] -- train --> [ Bonn ] [ Schweinfurt ] --> [ Bonn ] [ Bonn ] --> [ Berlin ] HERE , 'as_txt() for 4 nodes with 3 edges and class attribute'); # set only 1px and dashed $graph->set_attribute('graph', 'border', '1px dotted'); $graph->set_attribute('node', 'border', 'blue solid 2px'); # override "borderstyle" $graph->set_attribute('graph', 'border-style', 'dashed'); is ($graph->attribute('borderstyle'), 'dashed', 'borderstyle set on graph'); is ($graph->attribute('borderwidth'), '1', 'borderwidth set on graph'); is ($graph->attribute('bordercolor'), '#000000', 'bordercolor is default black'); is ($graph->attribute('border'), 'dashed', 'border set on graph'); is ($graph->border_attribute(), 'dashed', 'border set on graph'); # the same with the class attribute for the graph is ($graph->attribute('graph','borderstyle'), 'dashed', 'borderstyle set on class graph'); is ($graph->attribute('graph','borderwidth'), '1', 'borderwidth set on class graph'); is ($graph->attribute('graph','bordercolor'), '#000000', 'bordercolor is default black'); is ($graph->attribute('graph','border'), 'dashed', 'border set on class graph'); is ($graph->border_attribute('graph'), 'dashed', 'border set on class graph'); # the same with the class attribute for class "node" is ($graph->attribute('node','borderstyle'), 'solid', 'borderstyle set on class node'); is ($graph->attribute('node','borderwidth'), '2', 'borderwidth set on class node'); is ($graph->attribute('node','bordercolor'), 'blue', 'borderwidth set on class node'); is ($graph->attribute('node','border'), 'solid 2px blue', 'border set on class node'); is ($graph->border_attribute('node'), 'solid 2px blue', 'border set on class node'); # graph/node/edge attributes come first # graph "border: dashed" because "black" and "1px" are the defaults # node "border: solid 2px blue" because these are not the defaults (color/width changed # means we also get the style explicitely) is ( $graph->as_txt(), <<'HERE' graph { border: dashed; } node { border: solid 2px blue; } [ Berlin ] { color: blue; } [ Bonn ] { color: red; border: none; class: cities; } [ Frankfurt a. M. ] -- train --> [ Bonn ] [ Schweinfurt ] --> [ Bonn ] [ Bonn ] --> [ Berlin ] HERE , 'as_txt() for 4 nodes with 3 edges and graph/node/edge attributes'); ############################################################################# # output and output_format: $graph = Graph::Easy->new(); is (ref($graph), 'Graph::Easy', 'new worked'); $graph->add_edge ($bonn, $berlin); like ($graph->output(), qr/table/, 'default output worked'); $graph->set_attribute('graph', 'output', 'ascii'); is ($graph->output_format(), 'ascii', 'output format changed to ascii'); unlike ($graph->output(), qr//, 'ascii output worked'); ############################################################################# # add_group() my $group = $graph->add_group('G'); is (ref($group), 'Graph::Easy::Group', 'add_group()'); ############################################################################# # merge_nodes() with node B in a group (fixed in v0.39) $graph = Graph::Easy->new(); my ($A,$B) = $graph->add_edge('Bonn','Berlin','true'); $group = $graph->add_group('Cities'); is (scalar $group->nodes(), 0, 'no node in group'); $group->add_node($A); is (scalar $group->nodes(), 1, 'one node in group'); $group->add_node($B); is (scalar $group->nodes(), 2, 'one node in group'); is (scalar $graph->nodes(), 2, 'two nodes in graph'); is (scalar $graph->edges(), 1, 'one edge in graph'); is (scalar $group->edges(), 0, 'no edge in group'); $graph->layout(); # the edge is only added in the layout stage is (scalar $group->edges(), 0, 'no edge leading from/to group'); is (scalar $group->edges_within(), 1, 'one edge in group'); $graph->merge_nodes($A,$B); is (scalar $graph->edges(), 0, 'no edges in graph'); is (scalar $group->edges_within(), 0, 'no edges in group'); is (scalar $group->edges(), 0, 'no edge leading from/to group'); is (scalar $group->nodes(), 1, 'one node in group'); is (scalar $graph->nodes(), 1, 'one node in graph'); is (keys %{$A->{edges}}, 0, 'no edges in A'); is (keys %{$B->{edges}}, 0, 'no edges in B'); is ($B->{group}, undef, "B's group status got revoked"); Graph-Easy-0.73/t/parse_att.t0000644000076400007640000001326511675050456015731 0ustar shlomifshlomif#!/usr/bin/perl -w use Test::More; use strict; BEGIN { plan tests => 86; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy::Parser") or die($@); use_ok ("Graph::Easy") or die($@); }; can_ok ("Graph::Easy::Parser", qw/ _parse_attributes /); ############################################################################# # parser object my $parser = Graph::Easy::Parser->new(); is (ref($parser), 'Graph::Easy::Parser'); is ($parser->error(), '', 'no error yet'); my $line = 0; $parser->no_fatal_errors(1); foreach () { chomp; next if $_ =~ /^(\s*\z|#)/; # skip empty lines or comments my ($in,$result) = split /\|/, $_; my $txt = $in; $txt =~ s/\\n/\n/g; # insert real newlines # ^ => to '|' since '|' is the sep. $txt =~ s/[\^]/\|/g; $parser->reset(); my $class = 'node'; $class = 'edge' if $txt =~ /^(start|end|labelcolor|arrow)/; $class = 'graph' if $txt =~ /^labelpos/; # need to cache this value $parser->{_match_single_attribute} = $parser->_match_single_attribute(); my $att = $parser->_parse_attributes($txt, $class); # reuse parser object if ($parser->error()) { if ($result =~ /^error=/) { my $res = $result; $res =~ s/^error=//; my $resq = quotemeta($res); like ($parser->error(), qr/$resq/, $res); } else { print '# Got unexpected error: ' . $parser->error(), "\n"; fail ("$txt"); } next; } my $exp = ''; foreach my $k (sort keys %$att) { if (ref($att->{$k}) eq 'ARRAY') { $exp .= "$k="; for my $k1 (@{$att->{$k}}) { my $v = $parser->{_graph}->unquote_attribute('graph',$k,$k1); $exp .= "$v,"; } $exp =~ s/,\z//; $exp .= ";"; } else { my $v = $parser->{_graph}->unquote_attribute('graph',$k,$att->{$k}); $exp .= "$k=$v;"; } } is ($exp, $result, $in); } __DATA__ | color: red;|color=red; color : red;|color=red; color : lime ; |color=lime; color : yellow |color=yellow; color: rgb(1,1,1);|color=rgb(1,1,1); color: rgb(255,1,1);|color=rgb(255,1,1); color: rgb(255,255,1);|color=rgb(255,255,1); color: rgb(255,255,255);|color=rgb(255,255,255); color: #ff0;|color=#ff0; color: #0f0;|color=#0f0; color: slategrey;|color=slategrey; color: slategrey;|color=slategrey; color: gray;|color=gray; color: gray;|color=gray; # color names are case-insensitive color: Slategrey;|color=slategrey; color: SlateGrey;|color=slategrey; color: SLATEGREY;|color=slategrey; colorscheme: w3c;|colorscheme=w3c; colorscheme: x11;|colorscheme=x11; colorscheme: puor6;|colorscheme=puor6; colorscheme: puor16|error=Error in attribute: 'puor16' is not a valid colorscheme for a node border-style: double;|borderstyle=double; border-width: 1;|borderwidth=1; border-color: red;|bordercolor=red; color: red; border: none; |border=none;color=red; color:|error=Error in attribute: 'color:' doesn't look valid : red;|error=Error in attribute: ': red;' doesn't look valid : red|error=Error in attribute: ': red' doesn't look valid color: reddish|error=Error in attribute: 'reddish' is not a valid color for a node color:;background: red|error=Error in attribute: 'color:;background: red' doesn't look valid shape:fruggle;|error=Error in attribute: 'fruggle' is not a valid shape for a node color: rgb(256, 0, 0);|error=Error in attribute: 'rgb(256, 0, 0)' is not a valid color for a node color: rgb(0, 256, 0);|error=Error in attribute: 'rgb(0, 256, 0)' is not a valid color for a node color: rgb(0, 0, 256);|error=Error in attribute: 'rgb(0, 0, 256)' is not a valid color for a node shape: qiggle;|error=Error in attribute: 'qiggle' is not a valid shape for a node offset: -3,-2;|offset=-3,-2; offset: 3,-2;|offset=3,-2; offset: -3,2;|offset=-3,2; offset: 2, 0;|offset=2, 0; offset: 2 , 0;|offset=2 , 0; offset: 2 , 0;|offset=2 , 0; offset: 2 , 0 ;|offset=2 , 0; fill: brown;|fill=brown; point-style: qiggle;|error=Error in attribute: 'qiggle' is not a valid pointstyle for a node toint-shape: qiggle;|error=Error in attribute: 'toint-shape' is not a valid attribute name for a node autolink: qiggle;|error=Error in attribute: 'qiggle' is not a valid autolink for a node size: 1, 2;|size=1, 2; start: south, 1;|start=south, 1; start: south , 1;|start=south , 1; start: right , -1;|start=right , -1; end: south, 1;|end=south, 1; end: south , 1;|end=south , 1; end: right , -1;|end=right , -1; end: right,12345;|error=Error in attribute: 'right,12345' is not a valid end for a edge start: right,12345;|error=Error in attribute: 'right,12345' is not a valid start for a edge autolabel: 20;|autolabel=20; autolabel: name,1;|error=Error in attribute: 'name,1' is not a valid autolabel for a node autolabel: name,10;|autolabel=name,10; autolabel: name, 10;|autolabel=name, 10; autolabel: name ,10;|autolabel=name ,10; autolabel: name , 10;|autolabel=name , 10; fill: red^green^yellow;|fill=red,green,yellow; link: http://bloodgate.com/^index.html^/test;|link=http://bloodgate.com/,index.html,/test; link: http://bloodgate.com/ ^ index.html^/test;|link=http://bloodgate.com/,index.html,/test; shape: rect^img^rect;|shape=rect,img,rect; # attribute with a ";" inside quotes, and escaped quotes label: "baz;bar"; color: red;|color=red;label=baz;bar; label: "test";|label=test; label: "test;";|label=test;; label: "\"test\"";|label="test"; label: "\"test;\"";|label="test;"; # alias names bordercolor: red;|bordercolor=red; borderstyle: solid;|borderstyle=solid; borderwidth: 1px;|borderwidth=1px; fontsize: 80%;|fontsize=80%; textstyle: bold;|textstyle=bold; textwrap: auto;|textwrap=auto; pointstyle: diamond;|pointstyle=diamond; arrowstyle: filled;|arrowstyle=filled; labelcolor: peachpuff;|labelcolor=peachpuff; labelpos: bottom;|labelpos=bottom; Graph-Easy-0.73/t/layout.t0000644000076400007640000001203011675050456015251 0ustar shlomifshlomif#!/usr/bin/perl -w use Test::More; use strict; BEGIN { plan tests => 32; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy::Layout") or die($@); }; can_ok ("Graph::Easy", qw/ _trace_path _find_path _create_cell _path_is_clear _clear_tries _find_path_astar _find_path_loop _find_chains _assign_ranks /); can_ok ("Graph::Easy::Node", qw/ _shuffle_dir /); isnt ($Graph::Easy::VERSION, undef, 'VERSION in Layout'); use Graph::Easy; Graph::Easy::Edge::Cell->import (qw/ EDGE_HOR EDGE_VER EDGE_LABEL_CELL EDGE_SHORT_S EDGE_END_S EDGE_START_N /); ############################################################################# # layout tests my $graph = Graph::Easy->new(); is (ref($graph), 'Graph::Easy'); is ($graph->error(), '', 'no error yet'); my ($src, $dst, $edge) = $graph->add_edge('Bonn','Berlin'); my $e = 3; # elements per path cell (x,y,type) ############################################################################# # _shuffle_dir() my $array = [0,1,2,3]; is (join (",",@{ $src->_shuffle_dir($array,0) }), '3,0,2,1', 'shuffle 0' ); is (join (",",@{ $src->_shuffle_dir($array,90) }), '0,1,2,3', 'shuffle 90' ); is (join (",",@{ $src->_shuffle_dir($array) }), '0,1,2,3', 'shuffle ' ); is (join (",",@{ $src->_shuffle_dir($array,270) }), '2,3,1,0', 'shuffle 270'); is (join (",",@{ $src->_shuffle_dir($array,180) }), '1,2,0,3', 'shuffle 180'); ############################################################################# # _near_places() $src->{x} = 1; $src->{y} = 1; my $cells = {}; my @places = $src->_near_places($cells); is (scalar @places, 4 * 2, '4 places'); @places = $src->_near_places($cells,2); # $d == 2 is (scalar @places, 4 * 2, '4 places'); @places = $src->_near_places($cells,3); # $d == 3 is (scalar @places, 4 * 2, '4 places'); @places = $src->_near_places($cells,3,0); # $d == 3, type is 0 is (scalar @places, 4 * $e, '4 places'); # #1+3,1+0,1 ... is (join (',', @places), '4,1,16,1,4,32,-2,1,64,1,-2,128', 'places'); ############################################################################# # _find_path() $src->{x} = 1; $src->{y} = 1; $dst->{x} = 1; $dst->{y} = 1; my $coords = $graph->_find_path( $src, $dst, $edge); is (scalar @$coords, 1*$e, 'same cell => short edge path'); $src->{x} = 1; $src->{y} = 1; $dst->{x} = 2; $dst->{y} = 2; $coords = $graph->_find_path( $src, $dst, $edge); #print STDERR "# " . Dumper($coords) . "\n"; #print STDERR "# " . Dumper($graph->{cells}) . "\n"; is (scalar @$coords, 1*$e, 'path with a bend'); # mark one cell as already occupied $graph->{cells}->{"1,2"} = $src; $src->{x} = 1; $src->{y} = 1; $dst->{x} = 1; $dst->{y} = 3; $coords = $graph->_find_path( $src, $dst, $edge); #print STDERR "# " . Dumper($coords) . "\n"; #print STDERR "# " . Dumper($graph->{cells}) . "\n"; is (scalar @$coords, 3*$e, 'u shaped path (|---^)'); # block src over/under to avoid an U-shaped path $graph->{cells}->{"2,1"} = $src; $graph->{cells}->{"0,1"} = $src; $graph->{cache} = {}; $coords = $graph->_find_path( $src, $dst, $edge); #print STDERR "# " . Dumper($coords) . "\n"; # XXX TODO: check what path is actually generated here is (scalar @$coords, 7*$e, 'cell already blocked'); delete $graph->{cells}->{"1,2"}; $coords = $graph->_find_path( $src, $dst, $edge); is (scalar @$coords, 1*$e, 'straight path down'); is (join (":", @$coords), '1:2:' . (EDGE_SHORT_S() + EDGE_LABEL_CELL()), 'path 1,1 => 1,3'); $src->{x} = 1; $src->{y} = 0; $dst->{x} = 1; $dst->{y} = 5; $coords = $graph->_find_path( $src, $dst, $edge); is (scalar @$coords, 4*$e, 'straight path down'); my $type = EDGE_VER(); my $type_label = EDGE_VER() + EDGE_LABEL_CELL() + EDGE_START_N(); my $type_end = EDGE_VER() + EDGE_END_S(); is (join (":", @$coords), "1:1:$type_label:1:2:$type:1:3:$type:1:4:$type_end", 'path 1,0 => 1,5'); ############################################################################# ############################################################################# # as_ascii() will load Graph::Easy::Layout::Grid, this provides some # additional methods: my $ascii = $graph->as_ascii(); can_ok ("Graph::Easy", qw/ _balance_sizes _prepare_layout / ); ############################################################################# # _balance_sizes my $sizes = [ 3, 4, 5 ]; $graph->_balance_sizes( $sizes, 3+4+5); is_deeply ( $sizes, [ 3,4,5 ], 'constraint already met'); $graph->_balance_sizes( $sizes = [ 3, 4, 5 ], 3+4+5-1); is_deeply ( $sizes, [ 3,4,5 ], 'constraint already met'); $graph->_balance_sizes( $sizes = [ 3, 4, 5 ], 3+4+5+1); is_deeply ( $sizes, [ 4,4,5 ], 'smallest gets bigger'); $graph->_balance_sizes( $sizes = [ 3, 3, 3 ], 3*3 + 2); is_deeply ( $sizes, [ 4,4,3 ], 'first two smallest get bigger'); $graph->_balance_sizes( $sizes = [ 3, 3, 3 ], 3*3 + 3); is_deeply ( $sizes, [ 4,4,4 ], 'all got bigger'); $graph->_balance_sizes( $sizes = [ 3, 3, 3 ], 3*3 + 4); is_deeply ( $sizes, [ 5,4,4 ], 'all got bigger'); $graph->_balance_sizes( $sizes = [ 10, 10, 3 ], 20+7); is_deeply ( $sizes, [ 10,10,7 ], 'last got bigger'); Graph-Easy-0.73/t/dot/0000755000076400007640000000000012150110221014311 5ustar shlomifshlomifGraph-Easy-0.73/t/dot/4_loose.dot0000644000076400007640000000003611675050456016414 0ustar shlomifshlomifgraph { A--B B--C C--D D--A } Graph-Easy-0.73/t/layouter.t0000644000076400007640000000321211675050456015602 0ustar shlomifshlomif#!/usr/bin/perl -w use Test::More; use strict; # Test parsing and laying out the graphs (with no strict checks on the # output except that it should work). Tests all inputs with the four # flow directions. BEGIN { plan tests => 34; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy") or die($@); use_ok ("Graph::Easy::Parser") or die($@); }; ############################################################################# # parser object my $parser = Graph::Easy::Parser->new( debug => 0); is (ref($parser), 'Graph::Easy::Parser'); is ($parser->error(), '', 'no error yet'); opendir DIR, "layouter" or die ("Cannot read dir 'in': $!"); my @files = readdir(DIR); closedir(DIR); foreach my $f (sort @files) { next unless -f "layouter/$f"; # only files next unless $f =~ /\.txt/; # ignore anything else print "# at $f\n"; my $txt = readfile("layouter/$f"); for my $flow (qw/down up right west left/) { my $t = "graph { flow: $flow; }\n" . $txt; my $graph = $parser->from_text($t); # reuse parser object # $graph->debug(1); if (!defined $graph) { fail ("Graph input was invalid: " . $parser->error()); next; } my $ascii = $graph->as_ascii(); is ($graph->error(), '', 'no error on layout'); # print a debug output $ascii =~ s/\n/\n# /g; $t =~ s/\n/\n# /g; print "# Input:\n#\n# $t\n"; print "# Generated:\n#\n# $ascii\n"; } # for all directions } 1; sub readfile { my ($file) = @_; open FILE, $file or die ("Cannot read file $file: $!"); local $/ = undef; # slurp mode my $doc = ; close FILE; $doc; } Graph-Easy-0.73/t/base.t0000644000076400007640000000125411675050456014654 0ustar shlomifshlomif#!/usr/bin/perl -w use Test::More; use strict; BEGIN { plan tests => 6; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy::Base") or die($@); }; can_ok ("Graph::Easy::Base", qw/ new error error_as_html _init self class sub_class main_class fatal_errors no_fatal_errors /); ############################################################################# # Base tests my $base = Graph::Easy::Base->new(); is (ref($base), 'Graph::Easy::Base', 'new seemed to work'); is ($base->error(), '', 'no error yet'); $base->{class} = 'group.test'; is ($base->main_class(), 'group', 'main_class works'); is ($base->error(), '', 'no error yet'); Graph-Easy-0.73/t/layouter/0000755000076400007640000000000012150110221015367 5ustar shlomifshlomifGraph-Easy-0.73/t/layouter/layouter_chain.txt0000644000076400007640000000022311675050456021163 0ustar shlomifshlomif[ Bonn ] -> [ Berlin ] -> [ Kassel ] [ Bonn ] -> [ Koblenz ] -> [ Berlin ] [ Bonn ] -> [ Ulm ] -> [ Koblenz ] [ Ulm ] -> [ Bautzen ] -> [ Berlin ] Graph-Easy-0.73/t/layouter/layouter.txt0000644000076400007640000000020511675050456020021 0ustar shlomifshlomif[ Bonn ] -> [ Berlin ] -> [ Kassel ] [ Bonn ] -> [ Koblenz ] [ Bonn ] -> [ Ulm ] -> [ Koblenz ] [ Ulm ] -> [ Bautzen ] -> [ Berlin ] Graph-Easy-0.73/t/layouter/layouter_loop.txt0000644000076400007640000000025311675050456021055 0ustar shlomifshlomif[ Bonn ] -> [ Berlin ] -> [ Kassel ] [ Bonn ] -> [ Koblenz ] -> [ Berlin ] [ Bonn ] -> [ Ulm ] -> [ Koblenz ] [ Ulm ] -> [ Bautzen ] -> [ Berlin ] [ Bautzen ] -> [ Bonn ] Graph-Easy-0.73/t/layouter/multiples.txt0000644000076400007640000000073211675050456020200 0ustar shlomifshlomifnode { background: yellow; } [ Bonn ] ..> [ Berlin ] -> [ Kassel ] [ Bonn ] .-> [ Koblenz ] [ Bonn ] -> [ Ulm ] -> [ Koblenz ] [ Ulm ] -> [ Bautzen ] -> [ Berlin ] [ 1Bonn ] ..> [ 1Berlin ] -> [ 1Kassel ] [ 1Bonn ] .-> [ 1Koblenz ] [ 1Bonn ] -> [ 1Ulm ] -> [ 1Koblenz ] [ 1Ulm ] -> [ 1Bautzen ] -> [ 1Berlin ] [ 2Bonn ] ..> [ 2Berlin ] -> [ 2Kassel ] [ 2Bonn ] .-> [ 2Koblenz ] [ 2Bonn ] -> [ 2Ulm ] -> [ 2Koblenz ] [ 2Ulm ] -> [ 2Bautzen ] -> [ 2Berlin ] Graph-Easy-0.73/t/layouter/state.txt0000644000076400007640000000012611675050456017277 0ustar shlomifshlomif[ Start ] --> [ Main ] [ Main ] -- Until not done --> [ Main ] [ Main ] --> [ End ] Graph-Easy-0.73/t/layouter/edge_label.txt0000644000076400007640000000013611675050456020223 0ustar shlomifshlomif[ Bonn ] -- train --> [ Berlin ] -> [ Potsdam ] [ Berlin ] -> [ Ulm ] [ Bonn ] -> [ Potsdam ] Graph-Easy-0.73/t/html.t0000644000076400007640000003463511675050456014717 0ustar shlomifshlomif#!/usr/bin/perl -w use Test::More; use strict; BEGIN { plan tests => 74; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy") or die($@); }; ############################################################################# my $graph = Graph::Easy->new(); is (ref($graph), 'Graph::Easy'); is ($graph->error(), '', 'no error yet'); is ($graph->nodes(), 0, '0 nodes'); is ($graph->edges(), 0, '0 edges'); is (join (',', $graph->edges()), '', '0 edges'); my $html = $graph->as_html(); like ($html, qr/
new( name => 'Bonn' ); my $berlin = Graph::Easy::Node->new( 'Berlin' ); my $edge = $graph->add_edge ($bonn, $berlin); $html = $graph->as_html(); like ($html, qr/Bonn/, 'contains Bonn'); like ($html, qr/Berlin/, 'contains Berlin'); ############################################################################# # with some nodes with atributes $bonn->set_attribute( 'autotitle' => 'name' ); $html = $graph->as_html(); like ($html, qr/title='Bonn'/, 'contains title="Bonn"'); unlike ($html, qr/title=['"]Berlin['"]/, "doesn't contain title Berlin"); ############################################################################# # edges do not have a name, will fallback to the label $edge->set_attribute( 'autotitle' => 'name' ); $html = $graph->as_html(); like ($html, qr/title='Bonn'/, 'contains title="Bonn"'); unlike ($html, qr/title=['"]Berlin['"]/, "doesn't contain title Berlin"); unlike ($html, qr/title=['"]['"]/, "no empty title"); $edge->set_attribute( 'label' => 'my edge' ); $html = $graph->as_html(); like ($html, qr/title="my edge"/, 'contains title="my edge"'); ############################################################################# # check that "shape:" does not appear in CSS or HTML $bonn->set_attribute( 'shape' => 'circle' ); $graph->set_attribute ( 'node', 'shape', 'ellipse' ); my $css = $graph->css(); $html = $graph->as_html(); unlike ($css, qr/shape/, 'shape does not appear in CSS'); unlike ($html, qr/shape/, 'shape does not appear in HTML'); ############################################################################# # "shape: invisible" should result in an empty td tag w/ "border: none" $bonn->set_attribute( 'shape' => 'invisible' ); $css = $graph->css(); $html = $graph->as_html(); unlike ($html, qr/display:\s*none/, 'shape invisible is not display: none'); like ($html, qr/td.*border:\s*none/, 'shape invisible results in border: none'); ############################################################################# # label colors $graph->set_attribute( 'edge', 'label-color' => 'blue' ); $edge->set_attribute( 'label-color' => 'red' ); $css = $graph->css(); $html = $graph->as_html(); unlike ($html, qr/border-bottom:.*;\s*color: #0000ff/, 'no edge is green'); like ($html, qr/border-bottom:.*;\s*color: #ff0000/, 'some edge is red'); ############################################################################# # edge color vs. label colors $edge->set_attribute( 'color' => 'green' ); $html = $graph->as_html(); unlike ($html, qr/border-bottom:.*#0000ff/, 'no edge got blue'); unlike ($html, qr/border-bottom:.*;\s*color: #0000ff/, 'no edge got blue'); like ($html, qr/border-bottom:.*#008000.*;\s*color: #ff0000/, 'color green, label-color red'); ############################################################################# # caption from label $graph->set_attribute( 'graph', 'label' => 'My Graph Label' ); $html = $graph->as_html(); like ($html, qr/
My Graph Label<\/td>/, 'graph caption from label'); ############################################################################# # caption with label-pos $graph->set_attribute( 'graph', 'label' => 'My Graph Label' ); $graph->set_attribute( 'graph', 'label-pos' => 'bottom' ); $html = $graph->as_html(); like ($html, qr/My Graph Label<\/td>/, 'graph caption from label'); ############################################################################# # html_file includes and charset: $html = $graph->as_html_file(); my $charset = quotemeta('<meta http-equiv="Content-Type" content="text/html; charset=utf-8">'); like ($html, qr/$charset/, 'html_file includes charset definition'); like ($html, qr/<title>My Graph Label<\/title>/, 'html_file includes <title>'); ############################################################################# # egdes with links, titles and colors $graph = Graph::Easy->new(); $edge = $graph->add_edge('Friedrichshafen', 'Immenstaad'); $edge->set_attribute('title', 'Vrooom!'); $edge->set_attribute('color', 'orange'); $edge->set_attribute('text-style', 'none'); $edge->set_attribute('font-size', '1.5em'); $edge->set_attribute('link', 'http://bloodgate.com'); $edge->set_attribute('label', 'Schiff'); # This tests edge->as_html(), which will not be called for normal operations, # in these cases we would convert the single edge cells to HTML. my $edge_html = <<EDGE <td colspan=4 rowspan=4 class='edge' title='Vrooom!'><a href='http://bloodgate.com' style="color: #ffa500; text-decoration: none; font-size: 1.5em">Schiff</a></td> EDGE ; is ($edge->as_html(), $edge_html, 'edge->as_html()'); # entire graph as html $html = $graph->as_html(); $edge_html = <<EDGE_CELL <td colspan=2 rowspan=2 class="edge lh" style="border-bottom: solid 2px #ffa500;" title="Vrooom!"><a href='http://bloodgate.com' style='color: #ffa500; text-decoration: none; font-size: 1.5em;'>Schiff</a></td> EDGE_CELL ; my $like = quotemeta($edge_html); like ($html, qr/$like/, 'graph->as_html() contains proper edge html'); ############################################################################# # edge style double, double-dash, bold etc $graph = Graph::Easy->new(); $edge = $graph->add_edge('Friedrichshafen', 'Immenstaad'); $edge->set_attribute('style', 'double'); $edge_html = <<EDGE_2 <td colspan=4 rowspan=4 class='edge'></td> EDGE_2 ; is ($edge->as_html(), $edge_html, 'edge->as_html()'); $edge_html = <<EDGE_CELL <td colspan=2 rowspan=2 class="edge lh" style="border-bottom: double #000000;"> </td> EDGE_CELL ; $like = quotemeta($edge_html); $html = $graph->as_html(); like ($html, qr/$like/, 'edge->as_html()'); $edge->set_attribute('style', 'double-dash'); $edge_html = <<EDGE_CELL <td colspan=2 rowspan=2 class="edge lh" style="border-bottom: double #000000;"> </td> EDGE_CELL ; $like = quotemeta($edge_html); $html = $graph->as_html(); like ($html, qr/$like/, 'edge->as_html()'); ############################################################################# # edge color and label-color $edge->set_attribute('label-color', 'blue'); $edge_html = <<EDGE_CELL <td colspan=2 rowspan=2 class="edge lh" style="border-bottom: double #000000;color: #0000ff;"> </td> EDGE_CELL ; $like = quotemeta($edge_html); $html = $graph->as_html(); like ($html, qr/$like/, 'edge->as_html()'); ############################################################################# # a node with a link and a fill color at the same time my $f = $graph->node('Friedrichshafen'); $f->set_attribute('link', 'http://bloodgate.com'); $f->set_attribute('fill', 'red'); $html = $f->as_html(); is ($html, <<EOF <td colspan=4 rowspan=4 class='node' style="background: #ff0000"><a href='http://bloodgate.com'>Friedrichshafen</a></td> EOF , 'fill is on the TD, not the A HREF'); ############################################################################# # a node with a link and a border at the same time $f->set_attribute('border', 'orange'); $html = $f->as_html(); is ($html, <<EOF <td colspan=4 rowspan=4 class='node' style="background: #ff0000;border: solid 1px #ffa500"><a href='http://bloodgate.com'>Friedrichshafen</a></td> EOF , 'border is on the TD, not the A HREF'); ############################################################################# # as_html_file() includes the proper classes $html = $graph->as_html_file(); for my $c (qw/eb lh lv va el sh shl/) { like ($html, qr/table.graph \.$c/, "includes '$c'"); } ############################################################################# # group labels are left-aligned $graph = Graph::Easy->new(); my $group = $graph->add_group('Cities'); my ($A,$B) = $graph->add_edge('Krefeld', 'Düren'); $group->add_nodes($A,$B); $css = $graph->css(); like ($css, qr/group[^\}]*text-align: left;/, 'contains text-align: left'); ############################################################################# # setting a graph color does not override nodes/edges/groups $graph->set_attribute('color', 'red'); $css = $graph->css(); for my $e (qw/node_anon edge group_anon/) { unlike ($css, qr/table.graph\s+\.$e\s+\{[^\}]*[^-]color: #ff0000;/m, "contains not $e color red"); } ############################################################################# # setting a graph font/fill does not override nodes/edges/groups $graph->set_attribute('font', 'times'); $graph->set_attribute('fill', 'blue'); $graph->set_attribute('font-size', '8em'); $graph->set_attribute('align', 'left'); $css = $graph->css(); unlike ($css, qr/table.graph\s+\{[^\}]*font-family: /m, "doesn't contain font-family"); unlike ($css, qr/table.graph\s+\{[^\}]*fill: /m, "doesn't contain fill"); unlike ($css, qr/table.graph\s+\{[^\}]*color: /m, "doesn't contain color"); unlike ($css, qr/table.graph\s+\{[^\}]*background[^\}]*background/m, "doesn't contain two times background"); unlike ($css, qr/table.graph\s+\{[^\}]*text-align/m, "doesn't contain font-size"); unlike ($css, qr/table.graph\s+\{[^\}]*font-size/m, "doesn't contain text-align"); ############################################################################# # multiline labels with \c, \r, and \l in them $graph = Graph::Easy->new(); ($A,$B) = $graph->add_edge('Köln', 'Rüdesheim'); $A->set_attribute('label', 'Köln\r(am Rhein)\l(NRW)\c(Deutschland)'); $html = $graph->as_html_file(); like ($html, qr/class='node'>Köln<br><span class="r">\(am Rhein\)<\/span><br><span class="l">\(NRW\)<\/span><br>\(Deutschland\)</, 'Köln with multiline text'); $A->set_attribute('align', 'right'); $html = $graph->as_html_file(); like ($html, qr/class='node' style="text-align: center"><span class="r">Köln<\/span><br><span class="r">\(am Rhein\)<\/span><br><span class="l">\(NRW\)<\/span><br>\(Deutschland\)</, 'Köln with multiline text'); ############################################################################# # multiline labels with "textwrap: N;" $graph = Graph::Easy->new(); ($A,$B) = $graph->add_edge('Köln', 'Rüdesheim'); $A->set_attribute('label', 'Köln\r(am Rhein)\l(NRW)\c(Deutschland)'); $A->set_attribute('textwrap', 10); #print join (" ", $A->_label_as_html() ); $html = $graph->as_html_file(); like ($html, qr/class='node'>Köln \(am<br>Rhein\)<br>\(NRW\)<br>\(Deutschland\)</, 'Köln with multiline text'); ############################################################################# # invisible edges $graph = Graph::Easy->new(); ($A,$B,$edge) = $graph->add_edge('Hamm', 'Hagen'); $edge->set_attribute('style','invisible'); $edge->set_attribute('label','foobarbaz'); $edge->set_attribute('color','red'); $html = $graph->as_html_file(); unlike ($html, qr/invisible/, 'no border on invisible edges'); unlike ($html, qr/#ff0000/, 'no color on invisible edges'); unlike ($html, qr/foobarbaz/, 'no label on invisible edges'); ############################################################################# # inheritance of attributes via classes $graph = Graph::Easy->new(); ($A,$B,$edge) = $graph->add_edge('green', 'blue.foo'); $graph->set_attribute('color','red'); $graph->set_attribute('node','color','blue'); $graph->set_attribute('node.foo','color','inherit'); $graph->set_attribute('node.bar','color','green'); $graph->set_attribute('edge','color','inherit'); $graph->set_attribute('edge.foo','color','inherit'); $A->set_attribute('class','bar'); $B->set_attribute('class','foo'); $edge->set_attribute('class','foobar'); # no color set my ($C,$D,$E) = $graph->add_edge('blue','red'); $E->set_attribute('class','foo'); # inherits red $D->set_attribute('color','inherit'); # inherits red from graph is ($A->attribute('color'),'green', 'node.bar is green'); is ($B->attribute('color'),'blue', 'node.foo inherits blue from node'); is ($C->attribute('color'),'blue', 'node is just blue'); is ($D->attribute('color'),'red', 'inherits red from graph'); is ($edge->attribute('color'),'black', 'no color set, so defaults to black'); is ($E->attribute('color'),'red', 'inherit red from graph'); ############################################################################# # comments $graph = Graph::Easy->new(); ($A,$B,$edge) = $graph->add_edge('green', 'blue.foo'); $graph->set_attribute('comment', 'My comment --> graph'); $A->set_attribute('comment', 'My comment --> A'); $edge->set_attribute('comment', 'My comment --> edge'); $html = $graph->as_html_file(); like ($html, qr/<!-- My comment --> graph -->/, 'graph comment'); like ($html, qr/<!-- My comment --> A -->/, 'node comment'); like ($html, qr/<!-- My comment --> edge -->/, 'edge comment'); ############################################################################# # colorscheme and class attributes $graph = Graph::Easy->new(); ($A,$B,$edge) = $graph->add_edge('A', 'B'); $graph->set_attribute('colorscheme', 'pastel19'); $graph->set_attribute('node.yellow', 'fill', '1'); $graph->set_attribute('node.yellow', 'color', 'silver'); $A->set_attribute('class', 'yellow'); $html = $graph->as_html_file(); like ($html, qr/node_yellow(.|\n)*background: #fbb4ae;/, 'background is not 1'); like ($html, qr/node_yellow(.|\n)*color: silver;/, 'color is silver'); ############################################################################# # support for \N, \E, \H, \T, \G in titles and labels $graph = Graph::Easy->new(); ($A,$B,$edge) = $graph->add_edge('A', 'B'); $graph->set_attribute('label', 'My Graph'); $graph->set_attribute('node', 'title', 'My \N in \G'); $graph->set_attribute('edge', 'title', 'My \E in \G (\T => \H)'); $html = $graph->as_html_file(); like ($html, qr/title='My A in My Graph'/, 'title with \N and \G'); like ($html, qr/title='My B in My Graph'/, 'title with \N and \G'); like ($html, qr/title="My A->B in My Graph \(A => B\)"/, 'title with \E, \H, \T'); # support for \L in titles $graph->set_attribute('node', 'label', 'labeled "My \N"'); $graph->set_attribute('node', 'title', 'My \L'); $html = $graph->as_html_file(); like ($html, qr/title='My labeled "My A"'/, 'title with \L'); ���������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/layers.t��������������������������������������������������������������������������0000644�0000764�0000764�00000010752�11675050456�015244� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # test ranking of nodes, especially _assign_ranks(): use Test::More; use strict; BEGIN { plan tests => 60; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy::Layout") or die($@); }; use Graph::Easy; ############################################################################# # rank tests my $graph = Graph::Easy->new(); is (ref($graph), 'Graph::Easy'); is ($graph->error(), '', 'no error yet'); my $A = Graph::Easy::Node->new( name => 'A' ); my $B = Graph::Easy::Node->new( 'B' ); my $C = Graph::Easy::Node->new( 'C' ); my $D = Graph::Easy::Node->new( 'D' ); my $E = Graph::Easy::Node->new( 'E' ); is ($B->name(), 'B'); is ($A->{rank}, undef, 'no ranks assigned yet'); $graph->_assign_ranks(); is ($A->{rank}, undef, 'A not part of graph'); is ($A->connections(), 0); $graph->add_edge( $A, $B ); $graph->_assign_ranks(); is ($A->connections(), 1); is ($B->connections(), 1); is_rank($A, 0); is_rank($B, 1); $graph->add_edge( $B, $C ); $graph->_assign_ranks(); is_rank($A, 0); is_rank($B, 1); is_rank($C, 2); $graph->add_edge( $C, $D ); $graph->_assign_ranks(); is_rank($A, 0); is_rank($B, 1); is_rank($C, 2); is_rank($D, 3); $graph = Graph::Easy->new(); $graph->add_edge( $C, $D ); $graph->add_edge( $A, $B ); $graph->_assign_ranks(); is_rank($A, 0); is_rank($B, 1); is_rank($C, 0); is_rank($D, 1); $graph->add_edge( $D, $E ); $graph->_assign_ranks(); is_rank($A, 0); is_rank($B, 1); is_rank($C, 0); is_rank($D, 1); is_rank($E, 2); print "# IDs A B C D E: ". $A->{id}. " ". $B->{id}. " ". $C->{id}. " ". $D->{id}. " ". $E->{id}. "\n"; # circular path C->D->E->C $graph->add_edge( $E, $C ); $graph->_assign_ranks(); is_rank($A, 0); is_rank($B, 1); is_rank($C, 0); is_rank($D, 1); is_rank($E, 2); ############################################################################# # looping node $graph = Graph::Easy->new(); $graph->add_edge( $A, $A ); $graph->_assign_ranks(); is ($A->connections(), 2); is_rank($A, 0); ############################################################################# # multiedged graph $graph = Graph::Easy->new(); $graph->add_edge( $A, $B ); $graph->add_edge( $A, $B ); # add second edge $graph->_assign_ranks(); # second edge does not alter result is (scalar $A->successors(), 1); is ($A->connections(), 2); is (scalar $B->predecessors(), 1); is ($B->connections(), 2); is_rank($A, 0); is_rank($B, 1); ############################################################################# # near nodes (2 in rank 0, one in rank 1, 1 in rank 2) $graph = Graph::Easy->new(); $graph->add_node($A); $graph->add_node($B); $graph->add_node($C); $graph->add_node($D); $graph->add_edge( $A, $B ); $graph->add_edge( $C, $B ); $graph->add_edge( $B, $D ); $graph->_assign_ranks(); is ($A->connections(), 1); is ($B->connections(), 3); is ($C->connections(), 1); is ($D->connections(), 1); is_rank($A, 0); is_rank($B, 1); is_rank($C, 0); is_rank($D, 2); my @nodes = $graph->sorted_nodes(); is_deeply (\@nodes, [ $A, $B, $C, $D ], 'nodes sorted on id'); @nodes = $graph->sorted_nodes('rank'); is_deeply (\@nodes, [ $A, $C, $B, $D ], 'nodes sorted on rank'); @nodes = $graph->sorted_nodes('rank', 'name'); is_deeply (\@nodes, [ $A, $C, $B, $D ], 'nodes sorted on rank and name'); $A->{name} = 'a'; @nodes = $graph->sorted_nodes('rank', 'name'); is_deeply (\@nodes, [ $C, $A, $B, $D ], 'nodes sorted on rank and name'); $A->{name} = 'Z'; @nodes = $graph->sorted_nodes('rank', 'name'); is_deeply (\@nodes, [ $C, $A, $B, $D ], 'nodes sorted on rank and name'); @nodes = $graph->sorted_nodes('rank', 'id'); is_deeply (\@nodes, [ $A, $C, $B, $D ], 'nodes sorted on rank and id'); @nodes = $graph->sorted_nodes('name', 'id'); is_deeply (\@nodes, [ $B, $C, $D, $A ], 'nodes sorted on name and id'); ############################################################################# # explicit set ranks $graph = Graph::Easy->new(); $graph->add_edge( $A, $B ); $graph->add_edge( $B, $C ); $graph->add_edge( $C, $D ); $graph->add_edge( $D, $E ); $C->set_attribute('rank', '0'); $E->set_attribute('rank', '5'); $graph->_assign_ranks(); is_rank($A, 0); is_rank($B, 1); is_rank($C, 0); is_rank($D, 1); is_rank($E, 5); 1; ############################################################################# sub is_rank { my ($n, $l) = @_; # Rank is "-1..-inf" for automatically assigned ranks, and "1..inf" for # user supplied ranks: my $rank = abs($n->{rank})-1; print STDERR "# called from: ", join(" ", caller),"\n" unless is ($rank, $l, "$n->{name} has rank $l"); } ����������������������Graph-Easy-0.73/t/as_vcg.t��������������������������������������������������������������������������0000644�0000764�0000764�00000002231�11675050456�015200� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # Some basic as_vcg tests use Test::More; use strict; BEGIN { plan tests => 7; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy") or die($@); use_ok ("Graph::Easy::Parser") or die($@); }; can_ok ('Graph::Easy', qw/ as_vcg as_vcg_file /); ############################################################################# my $graph = Graph::Easy->new(); my $vcg = $graph->as_vcg(); my $vcg_file = $graph->as_vcg_file(); # remove time stamp: $vcg =~ s/ at.*//; $vcg_file =~ s/ at.*//; is ($vcg, $vcg_file, 'as_vcg and as_vcg_file are equal'); $graph->add_edge('A','B'); like ($graph->as_vcg(), qr/edge: { sourcename: "A" targetname: "B" }/, 'as_vcg matches'); # set edge label my @edges = $graph->edges(); $edges[0]->set_attribute('label', 'my car'); like ($graph->as_vcg(), qr/edge: { label: "my car" sourcename: "A" targetname: "B" }/, 'as_vcg matches'); ############################################################################# # graph label $graph = Graph::Easy->new(); $graph->set_attribute('label', 'my graph label'); like ($graph->as_vcg(), qr/title: "my graph label"/, 'as_vcg has graph label'); �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/use_class.t�����������������������������������������������������������������������0000644�0000764�0000764�00000004331�11675050456�015722� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use Test::More; use strict; BEGIN { plan tests => 8; chdir 't' if -d 't'; use lib '../lib'; use_ok qw/Graph::Easy/; use_ok qw/Graph::Easy::Parser/; } ###################################################### package Graph::Easy::MyNode; use Graph::Easy::Node; use base qw/Graph::Easy::Node/; # override here methods for your node class ###################################################### # when overriding nodes, we also need ::Anon package Graph::Easy::MyNode::Anon; #use Graph::Easy::MyNode; use base qw/Graph::Easy::MyNode/; use base qw/Graph::Easy::Node::Anon/; ###################################################### # and :::Empty package Graph::Easy::MyNode::Empty; #use Graph::Easy::MyNode; use base qw/Graph::Easy::MyNode/; ###################################################### package Graph::Easy::MyGraph; use Graph::Easy; use base qw/Graph::Easy/; ###################################################### package Graph::Easy::MyGroup; use Graph::Easy::Group; use base qw/Graph::Easy::Group/; ###################################################### package Graph::Easy::MyEdge; use Graph::Easy::Edge; use base qw/Graph::Easy::Edge/; ###################################################### package main; use Graph::Easy::Parser; use Graph::Easy; my $parser = Graph::Easy::Parser->new(); $parser->use_class('node', 'Graph::Easy::MyNode'); $parser->use_class('edge', 'Graph::Easy::MyEdge'); $parser->use_class('graph', 'Graph::Easy::MyGraph'); $parser->use_class('group', 'Graph::Easy::MyGroup'); my $graph = $parser->from_text("( Cities: [ Bonn ] -> [ Berlin| |Spree ] -> [ ])"); is (ref($graph), 'Graph::Easy::MyGraph', 'graph worked'); my $group = $graph->group('Cities:'); is (ref($group), 'Graph::Easy::MyGroup', 'group worked'); my $bonn = $graph->node('Bonn'); is (ref($bonn), 'Graph::Easy::MyNode', 'node worked'); my @nodes = $graph->nodes(); my $empty = $graph->node('BerlinSpree.1'); is (ref($empty), 'Graph::Easy::MyNode::Empty', 'empty node worked'); $graph = $parser->from_text("[ ]"); is (ref($graph), 'Graph::Easy::MyGraph', 'graph with anon node worked'); @nodes = $graph->nodes(); my $anon = $nodes[0]; is (ref($anon), 'Graph::Easy::MyNode::Anon', 'anon node worked'); �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/node.t����������������������������������������������������������������������������0000644�0000764�0000764�00000045361�11675050456�014676� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use Test::More; use strict; BEGIN { plan tests => 203; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy::Node") or die($@); use_ok ("Graph::Easy") or die($@); use_ok ("Graph::Easy::As_ascii") or die($@); }; can_ok ("Graph::Easy::Node", qw/ new as_html as_ascii error class dimensions name sorted_successors successors predecessors has_predecessors has_as_predecessor has_as_successor connections edges edges_to incoming outgoing width background height columns rows size flow angle parent pos offset x y class title link shape default_attribute del_attribute set_attribute get_attribute set_attributes attribute default_attribute color_attribute get_attributes border_attribute group add_to_group origin is_multicelled nodes_sharing_start nodes_sharing_end as_html _place _do_place _check_place _place_children find_grandparent _near_places _allowed_places /); ############################################################################# my $node = Graph::Easy::Node->new(); is (ref($node), 'Graph::Easy::Node'); is ($node->error(), '', 'no error yet'); is ($node->x(), undef, 'x == undef'); is ($node->y(), undef, 'y == undef'); is ($node->label(), 'Node #0', 'label'); is ($node->name(), 'Node #0', 'name'); is ($node->class(), 'node', 'class node'); is ($node->title(), '', 'no title per default'); is (join(",", $node->pos()), "0,0", 'pos = 0,0'); is ($node->width(), undef, 'w = undef'); # no graph => thus no width yet is ($node->height(), undef, 'h == undef'); is ($node->shape(), 'rect', 'default shape is "rect"'); is ($node->border_attribute(), '', 'border_attribute()'); is ($node->connections(), 0, 'no connections yet'); is ($node->is_multicelled(), 0, 'no multicelled'); is ($node->rows(), 1, '1 row'); is ($node->columns(), 1, '1 column'); # These are inherited: is ($node->attribute('border'), '', 'attribute("border")'); is ($node->attribute('border-style'), 'solid', 'attribute("border-style")'); is (join(",",$node->dimensions()), "7,1", 'dimensions = (7,1)'); is ($node->origin(), undef, 'not clustered'); is (join(",",$node->offset()), '0,0', 'not clustered'); is (scalar $node->successors(), undef, 'no outgoing links'); is (scalar $node->sorted_successors(), 0, 'no outgoing links'); is (scalar $node->predecessors(), undef, 'no incoming links'); is (scalar $node->incoming(), undef, 'no incoming links'); is (scalar $node->outgoing(), undef, 'no outgoing links'); my $edge = Graph::Easy::Node->new(); $edge->set_attribute('class' => 'edge'); is ($edge->class(), 'node.edge', 'class edge'); is ($edge->border_attribute(), '', 'border_attribute()'); my $other = Graph::Easy::Node->new(); is (scalar $node->edges_to($other), undef, 'no graph, no links'); is (scalar $node->edges(), undef, 'no graph, no edges'); ############################################################################# # predecessors(), successors(), connections() and edges_to() tests my $graph = Graph::Easy->new( ); $other = Graph::Easy::Node->new( 'Name' ); $edge = $graph->add_edge ($node, $other); is ($node->{graph}, $graph, "node's graph points to \$graph"); is ($other->{graph}, $graph, "other's graph points to \$graph"); is ($node->successors(), 1, '1 outgoing'); is (scalar $node->sorted_successors(), 1, '1 outgoing'); is ($node->predecessors(), 0, '0 incoming'); is (scalar $node->edges_to($other), 1, '1 link to $other'); is ($node->connections(), 1, '1 connection'); is (scalar $node->edges(), 1, '1 edge'); is ($node->has_as_successor($other), 1, 'node -> other'); is ($node->has_as_successor($node), 0, '! node -> node'); is ($node->has_as_predecessor($node), 0, '! node -> node'); is ($node->has_as_predecessor($other), 0, '! node -> node'); is ($other->has_as_successor($other), 0, '! other -> node'); is ($other->has_as_successor($node), 0, '! other -> other'); is ($other->has_as_predecessor($node), 1, ' node -> other'); is ($other->has_as_predecessor($other), 0, '! other -> other'); my @E = $node->edges_to($other); is (scalar @E, 1, '1 link to $other'); is ($E[0], $edge, 'first link to $other is $edge'); @E = $node->edges(); is ($E[0], $edge, '1 edge'); is ($other->successors(), 0, '0 outgoing'); is (scalar $other->sorted_successors(), 0, '0 outgoing'); is ($other->predecessors(), 1, '1 incoming'); is ($other->connections(), 1, '1 connection'); $graph->add_edge('First', 'Name'); @E = $node->edges_to($other); is (scalar @E, 1, '1 link to $other'); is ($E[0], $edge, 'first link to $other is $edge'); $graph->add_edge('Name', 'Name'); ############################################################################# # as_txt/as_html my $r = 'colspan=4 rowspan=4'; use_ok ('Graph::Easy::As_txt'); can_ok ('Graph::Easy::Node', qw/attributes_as_txt as_txt as_pure_txt/); is ($node->as_txt(), '[ Node \#0 ]', 'as_txt'); is ($node->as_html(), " <td $r class='node'>Node #0</td>\n", 'as_html'); # no quoting of () nec. $node->{name} = 'Frankfurt (Oder)'; is ($node->as_txt(), '[ Frankfurt (Oder) ]', 'as_txt'); is ($node->as_html(), " <td $r class='node'>Frankfurt (Oder)</td>\n", 'as_html'); # quoting of | $node->{name} = 'Frankfurt |-|'; is ($node->as_txt(), '[ Frankfurt \|-\| ]', 'as_txt'); is ($node->as_html(), " <td $r class='node'>Frankfurt |-|</td>\n", 'as_html'); # quoting of [] and {} $node->{name} = 'Frankfurt [ { #1 } ]'; is ($node->as_txt(), '[ Frankfurt \[ \{ \#1 \} \] ]', 'as_txt'); is ($node->as_html(), " <td $r class='node'>Frankfurt [ { #1 } ]</td>\n", 'as_html'); # quoting of &, < and > $node->{name} = 'Frankfurt < & >'; is ($node->as_txt(), '[ Frankfurt < & > ]', 'as_txt'); is ($node->as_html(), " <td $r class='node'>Frankfurt < & ></td>\n", 'as_html'); ############################################################################# # as_txt with labels $node->set_attribute('label', 'thelabel'); $node->{name} = 'name'; is ($node->as_txt(), '[ name ] { label: thelabel; }', 'as_txt'); # reset node for next tests $node->{name} = 'Node #0'; $node->del_attribute('label'); # test setting after deletion $node->set_attribute('label', 'my label'); is ($node->as_txt(), '[ Node \#0 ] { label: my label; }', 'as_txt'); # reset node for next tests $node->del_attribute('label'); ############################################################################# # as_txt/as_html w/ subclass and attributes $node->{class} = 'node.cities'; is ($node->as_txt(), '[ Node \#0 ] { class: cities; }', 'as_txt'); is ($node->as_html(), " <td $r class='node_cities'>Node #0</td>\n", 'as_html'); is ($node->as_pure_txt(), '[ Node \#0 ]', 'as_txt_node'); $node->set_attribute ( 'color', 'blue' ); is ($node->as_txt(), '[ Node \#0 ] { color: blue; class: cities; }', 'as_txt'); is ($node->as_html(), " <td $r class='node_cities' style=\"color: #0000ff\">Node #0</td>\n", 'as_html'); is ($node->as_pure_txt(), '[ Node \#0 ]', 'as_pure_txt'); $node->set_attributes ( { color => 'purple' } ); is ($node->as_txt(), '[ Node \#0 ] { color: purple; class: cities; }', 'as_txt'); is ($node->as_html(), " <td $r class='node_cities' style=\"color: #800080\">Node #0</td>\n", 'as_html'); is ($node->as_pure_txt(), '[ Node \#0 ]', 'as_pure_txt'); ############################################################################# # set_attributes(class => foo) $node->set_attributes ( { class => 'foo', color => 'orange' } ); is ($node->class(), 'node.foo', 'class set correctly'); is ($node->sub_class(), 'foo', 'class set correctly'); is ($node->attribute('color'), 'orange', 'color set correctly'); is ($node->as_txt(), '[ Node \#0 ] { color: orange; class: foo; }', 'as_txt'); is ($node->as_html(), " <td $r class='node_foo' style=\"color: #ffa500\">Node #0</td>\n", 'as_html'); $node->set_attribute ( 'class', 'bar' ); is ($node->as_txt(), '[ Node \#0 ] { color: orange; class: bar; }', 'as_txt'); is ($node->as_html(), " <td $r class='node_bar' style=\"color: #ffa500\">Node #0</td>\n", 'as_html'); ############################################################################# # set_attribute() with encoded entities (%3a etc) and quotation marks foreach my $l ( 'http://bloodgate.com/', '"http://bloodgate.com/"', '"http%3a//bloodgate.com/"', ) { $node->set_attribute('link', $l); is ($node->as_txt(), '[ Node \#0 ] { color: orange; link: http://bloodgate.com/; class: bar; }', 'as_txt'); is ($node->as_html(), " <td $r class='node_bar'><a href='http://bloodgate.com/' style=\"color: #ffa500\">Node #0</a></td>\n", 'as_html'); } foreach my $l ( 'perl/', '"perl/"', ) { $node->set_attribute('link', $l); is ($node->as_txt(), '[ Node \#0 ] { color: orange; link: perl/; class: bar; }', 'as_txt'); is ($node->as_html(), " <td $r class='node_bar'><a href='/wiki/index.php/perl/' style=\"color: #ffa500\">Node #0</a></td>\n", 'as_html'); } $node->set_attribute('link', "test test&"); is ($node->as_txt(), '[ Node \#0 ] { color: orange; link: test test&; class: bar; }', 'as_txt'); is ($node->as_html(), " <td $r class='node_bar'><a href='/wiki/index.php/test+test&' style=\"color: #ffa500\">Node #0</a></td>\n", 'as_html'); $node->set_attribute('color', "\\#801010"); is ($node->as_txt(), '[ Node \#0 ] { color: #801010; link: test test&; class: bar; }', 'as_txt'); is ($node->as_html(), " <td $r class='node_bar'><a href='/wiki/index.php/test+test&' style=\"color: #801010\">Node #0</a></td>\n", 'as_html'); # test quotation marks in link: $node->set_attribute('link', "test'test"); is ($node->as_txt(), '[ Node \#0 ] { color: #801010; link: test\'test; class: bar; }', 'as_txt'); is ($node->as_html(), " <td $r class='node_bar'><a href='/wiki/index.php/test%27test' style=\"color: #801010\">Node #0</a></td>\n", 'as_html'); # quotation mark at the end (but not at the start) $node->set_attribute('link', "test'"); is ($node->as_txt(), '[ Node \#0 ] { color: #801010; link: test\'; class: bar; }', 'as_txt'); is ($node->as_html(), " <td $r class='node_bar'><a href='/wiki/index.php/test%27' style=\"color: #801010\">Node #0</a></td>\n", 'as_html'); ############################################################################# # multicelled nodes is ($node->is_multicelled(), 0, 'no multicelled'); is (join (",",$node->size()), '1,1', 'size 1,1'); $node->set_attribute('size', '5,3'); $node->_calc_size(); is (join (",",$node->size()), '5,3', 'size 5,3'); is ($node->is_multicelled(), 1, 'is multicelled'); is ($node->attribute('size'), '5,3', 'attribute("size")'); $node->set_attribute('size', '1,1'); $node->_calc_size(); is ($node->{att}->{rows}, 1, 'rows still present'); is ($node->{att}->{columns}, 1, 'columns still present'); is ($node->as_txt(), "[ Node \\#0 ] { color: #801010; link: test'; class: bar; }", 'size not in output'); $node->del_attribute('size'); is (exists $node->{att}->{rows} ? 1 : 0, 0, 'rows no longer present'); is (exists $node->{att}->{columns} ? 1 : 0, 0, 'columns no longer present'); ############################################################################# # skipping of attributes (should not appear in HTML) $node->set_attribute('link', "test test&"); $node->set_attribute('flow','right'); $node->set_attribute('point-style','diamond'); is ($node->as_txt(), '[ Node \#0 ] { color: #801010; flow: right; link: test test&; pointstyle: diamond; class: bar; }', 'as_txt'); is ($node->as_html(), " <td $r class='node_bar'><a href='/wiki/index.php/test+test&' style=\"color: #801010\">Node #0</a></td>\n", 'as_html'); ############################################################################# # group tests is ($node->group(), undef, 'no groups yet'); use Graph::Easy::Group; my $group = Graph::Easy::Group->new( { name => 'foo' } ); $node->add_to_group($group); is ($node->group(), $group, 'group foo'); is ($node->attribute('group'), $group->{name}, 'group foo'); ############################################################################# # title tests $node->set_attribute('title', "foo title"); is ($node->title(), 'foo title', 'foo title'); $node->del_attribute('title'); $node->set_attribute('autotitle', 'none'); is ($node->title(), '', 'no title if autotitle: none'); $node->set_attribute('autotitle', 'name'); is ($node->title(), $node->name(), 'title equals name'); $node->set_attribute('autotitle', 'label'); is ($node->title(), $node->name(), 'title equals name'); $node->set_attribute('label', 'label'); is ($node->title(), 'label', 'title equals label'); $node->set_attribute('link', ''); $node->set_attribute('autotitle', 'link'); is ($node->title(), '', 'title "" if no link'); $node->set_attribute('link', 'http://bloodgate.com/'); is ($node->title(), $node->link(), 'title eq link'); $node->set_attribute('title','my title'); is ($node->title(), 'my title', 'title will override autotitle'); ############################################################################# # invisible nodes, and nodes with shape none $node = Graph::Easy::Node->new( { name => "anon 0", label => 'X' } ); $node->set_attribute('shape', "invisible"); is ($node->as_ascii(), "", 'invisible text node'); $node->set_attribute('shape', "none"); $node->_correct_size(); is ($node->as_ascii(), " \n X \n ", 'no border for shape "none"'); ############################################################################# # as_ascii() and label vs name (bug until v0.16) $node = Graph::Easy::Node->new( { name => "Node #01234", label => 'label' } ); is ($node->label(), 'label', 'node label eq "label"'); $node->_correct_size(); is ($node->width(), '9', 'width 9 (length("label") + 2 (padding) + 2 (border)'); is ($node->height(), '3', 'height 3'); like ($node->as_ascii(), qr/label/, 'as_ascii uses label, not name'); ############################################################################# # node placement (unclustered) $node = Graph::Easy::Node->new(); my $cells = { }; my $parent = { cells => $cells }; is ($node->_do_place(1,1,$parent), 1, 'node can be placed'); is ($cells->{"1,1"}, $node, 'node was really placed'); is (scalar keys %$cells, 1, 'one entry'); is ($node->_do_place(1,1,$parent), 0, 'node cannot be placed again'); is ($cells->{"1,1"}, $node, 'node still there placed'); is (scalar keys %$cells, 1, 'one entry'); ############################################################################# # outgoing/incoming $graph = Graph::Easy->new(); my ($A,$B); ($A,$B, $edge) = $graph->add_edge('A','B'); is ($A->incoming(), 0, 'no incoming'); is ($B->outgoing(), 0, 'no outgoing'); is ($B->incoming(), 1, 'one incoming'); is ($A->outgoing(), 1, 'one outgoing'); my $C; ($B,$C, $edge) = $graph->add_edge('B', 'C'); is ($B->incoming(), 1, 'one incoming'); is ($C->incoming(), 1, 'one incoming'); is ($A->outgoing(), 1, 'one outgoing'); is ($B->outgoing(), 1, 'one outgoing'); $graph->add_edge('A', 'C'); is ($C->incoming(), 2, 'two incoming'); is ($A->outgoing(), 2, 'one outgoing'); $graph->add_edge('C', 'C'); is ($C->incoming(), 3, 'C -> C'); is ($C->outgoing(), 1, 'C -> C'); ############################################################################# # _allowed_places() $graph = Graph::Easy->new(); ($A,$B, $edge) = $graph->add_edge('A','B'); my @allowed = $A->_allowed_places ( [ 0,0, 0,1, 0,2, 0,3 ], [ 0,0, 0,2, 1,2 ]); is_deeply (\@allowed, [ 0,0, 0,2 ], '_allowed_places'); @allowed = $A->_allowed_places ( [ 0,0, 0,1, 0,2, 0,3 ], [ ]); is_deeply (\@allowed, [ ], '_allowed_places'); @allowed = $A->_allowed_places ( [ 0,0, 0,1, 0,2, 0,3 ], [ 3,1, 1,2, 0,4 ]); is_deeply (\@allowed, [ ], '_allowed_places'); @allowed = $A->_allowed_places ( [ 0,0, 0,1, 0,2, 0,3 ], [ 3,1, 1,2, 0,3 ]); is_deeply (\@allowed, [ 0,3 ], '_allowed_places'); ############################################################################# # _allow() $A->{x} = 1; $A->{y} = 2; $A->{cx} = 3; $A->{cy} = 2; my $allow = $A->_allow('south',''); is_deeply ($allow, [ 1,4, 2,4, 3,4 ], 'south'); $allow = $A->_allow('south','0'); is_deeply ($allow, [ 1,4 ], 'south,0'); $allow = $A->_allow('south','1'); is_deeply ($allow, [ 2,4 ], 'south,1'); $allow = $A->_allow('south','2'); is_deeply ($allow, [ 3,4 ], 'south,2'); $allow = $A->_allow('south','3'); is_deeply ($allow, [ 3,4 ], 'south,3'); $allow = $A->_allow('south','-1'); is_deeply ($allow, [ 3,4 ], 'south,-1'); $allow = $A->_allow('south','-2'); is_deeply ($allow, [ 2,4 ], 'south,-2'); $allow = $A->_allow('south','-3'); is_deeply ($allow, [ 1,4 ], 'south,-3'); $allow = $A->_allow('south','-4'); is_deeply ($allow, [ 1,4 ], 'south,-4'); $allow = $A->_allow('north',''); is_deeply ($allow, [ 1,1, 2,1, 3,1 ], 'north'); $allow = $A->_allow('north','0'); is_deeply ($allow, [ 1,1 ], 'north,0'); $allow = $A->_allow('north','2'); is_deeply ($allow, [ 3,1 ], 'north,0'); $allow = $A->_allow('north','-1'); is_deeply ($allow, [ 3,1 ], 'north,0'); $allow = $A->_allow('west',''); is_deeply ($allow, [ 0,2, 0,3 ], 'west'); $allow = $A->_allow('west','0'); is_deeply ($allow, [ 0,2 ], 'west'); $allow = $A->_allow('west','1'); is_deeply ($allow, [ 0,3 ], 'west'); $allow = $A->_allow('east',''); is_deeply ($allow, [ 4,2, 4,3 ], 'east'); $allow = $A->_allow('east','1'); is_deeply ($allow, [ 4,3 ], 'east,1'); $allow = $A->_allow('east','2'); is_deeply ($allow, [ 4,3 ], 'east,2'); $allow = $A->_allow('east','-1'); is_deeply ($allow, [ 4,3 ], 'east,-1'); ############################################################################# # parent() $graph = Graph::Easy->new(); ($A,$B, $edge) = $graph->add_edge('A','B'); is ($A->parent(), $graph, 'parent is graph'); $group = $graph->add_group('Test'); $group->add_node($A); is ($A->parent(), $group, 'parent is group'); ############################################################################# # angle() my @angles = qw/south south front left -90 back -45 45 +45/; my @expect = qw/180 180 90 0 0 270 45 45 135/; is ($A->angle(), 0, 'default is 0 pointing up'); $A->set_attribute('rotate', 'south'); my $i = 0; for my $e (@expect) { my $an = $angles[$i++]; $A->set_attribute('rotate', $an); is ($A->angle(), $e, "expect $e for $an"); } $A->del_attribute('flow', 'south'); is ($A->{_cached_flow}, undef, 'flow uncached by set_attribute'); $A->flow(); # cache again $A->set_attribute('flow', 'south'); is ($A->{_cached_flow}, undef, 'flow uncached by set_attribute'); @angles = qw/south south front left -90 back -45 45 +45/; @expect = qw/180 180 180 90 90 0 135 45 225/; $i = 0; for my $e (@expect) { my $an = $angles[$i++]; $A->set_attribute('rotate', $an); is ($A->angle(), $e, "expect $e for $an"); } ############################################################################# # Deleting a node should work if the node is a child node (fail untill v0.49) $graph = Graph::Easy->new(); $A = $graph->add_node('A'); $B = $graph->add_node('B'); $B->set_attribute('origin','A'); $B->set_attribute('offset','2,2'); $graph->del_node('B'); is ($graph->as_ascii(), "+---+\n| A |\n+---+\n", 'only one node rendered'); �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/graphviz.t������������������������������������������������������������������������0000644�0000764�0000764�00000053203�11675050456�015575� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # test as_graphviz() output use Test::More; use strict; BEGIN { plan tests => 157; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy") or die($@); }; ############################################################################# my $graph = Graph::Easy->new(); is (ref($graph), 'Graph::Easy'); is ($graph->error(), '', 'no error yet'); is ($graph->nodes(), 0, '0 nodes'); is ($graph->edges(), 0, '0 edges'); is (join (',', $graph->edges()), '', '0 edges'); my $grviz = $graph->as_graphviz(); like ($grviz, qr/digraph.*\{/, 'looks like digraph'); unlike ($grviz, qr/#/, 'and has proper comment'); like ($grviz, qr#// Generated#, 'and has proper comment'); ############################################################################# # after first call to as_graphviz, these should now exist: can_ok ("Graph::Easy::Node", qw/ attributes_as_graphviz as_graphviz_txt /); ############################################################################# # with some nodes my $bonn = Graph::Easy::Node->new( name => 'Bonn' ); my $berlin = Graph::Easy::Node->new( 'Berlin' ); my $edge = $graph->add_edge ($bonn, $berlin); $grviz = $graph->as_graphviz(); like ($grviz, qr/Bonn/, 'contains Bonn'); like ($grviz, qr/Berlin/, 'contains Bonn'); like ($grviz, qr/arrowhead=open/, 'contains open arrowheads'); unlike ($grviz, qr/\w+=,/, "doesn't contain empty defintions"); ############################################################################# # with attributes on the graph $graph->set_attribute( 'graph', 'fill' => 'red' ); like ($graph->as_graphviz(), qr/bgcolor="#ff0000"/, 'contains bgcolor="#ff0000"'); ############################################################################# # with label/label-pos attributes on the graph $graph->set_attribute( 'graph', 'label' => 'My Label' ); $grviz = $graph->as_graphviz(); like ($grviz, qr/label="My Label"/, 'graph label'); like ($grviz, qr/labelloc=top/, 'default is top (dot 1.1 seems to get this wrong)'); $graph->set_attribute( 'graph', 'label-pos' => 'top' ); $grviz = $graph->as_graphviz(); like ($grviz, qr/label="My Label"/, 'graph label'); like ($grviz, qr/labelloc=top/, 'default is top'); $graph->set_attribute( 'graph', 'label-pos' => 'bottom' ); $grviz = $graph->as_graphviz(); like ($grviz, qr/label="My Label"/, 'graph label'); like ($grviz, qr/labelloc=bottom/, 'now bottom'); ############################################################################# # with some nodes with atributes $bonn->set_attribute( 'shape' => 'rect' ); $grviz = $graph->as_graphviz(); like ($grviz, qr/[^"]Bonn[^"]/, 'contains Bonn unquoted'); like ($grviz, qr/[^"]Berlin[^"]/, 'contains Bonn unquoted'); like ($grviz, qr/shape=box/, 'contains shape'); ############################################################################# # remapped attributes, quoted attributes $bonn->set_attributes( { fill => '#808080', title => 'title string', color => 'red', 'border-color' => 'brown', class => 'city', } ); $grviz = $graph->as_graphviz(); like ($grviz, qr/fillcolor="#808080"/, 'contains fillcolor'); like ($grviz, qr/tooltip="title string"/, 'contains tooltip'); like ($grviz, qr/color="#a52a2a"/, 'contains color'); like ($grviz, qr/fontcolor="#ff0000"/, 'contains fontcolor'); unlike ($grviz, qr/(city|class)/, "doesn't contain class"); ############################################################################# # quoting (including " in node names) $bonn->{name} = 'Bonn"'; $grviz = $graph->as_graphviz(); like ($grviz, qr/"Bonn\\""/, 'quoted Bonn"'); $bonn->{name} = 'Bonn und Umgebung'; $grviz = $graph->as_graphviz(); like ($grviz, qr/"Bonn und Umgebung"/, 'quoted "Bonn und Umgebung"'); is (join(",", $graph->_graphviz_remap_edge_style('style', 'bold')), 'style,bold', 'style,bold'); my ($name,$style) = $graph->_graphviz_remap_edge_style('style', 'double'); is ($name, undef, 'style=double suppressed'); is ($style, undef, 'style=double suppressed'); ($name,$style) = $graph->_graphviz_remap_edge_style('style', 'solid'); is ($name, undef, 'style=solid suppressed'); is ($style, undef, 'style=solid suppressed'); $bonn->{name} = '2A'; $grviz = $graph->as_graphviz(); like ($grviz, qr/"2A"/, '"2A" must be quoted'); $bonn->{name} = '123'; $grviz = $graph->as_graphviz(); like ($grviz, qr/ 123 /, '"123" needs no quotes'); # strict should come last in this list: for (qw/0AB graph subgraph edge node Graph Edge Strict strict/) { $bonn->{name} = $_; $grviz = $graph->as_graphviz(); like ($grviz, qr/"$_"/, "'$_' needs quotes"); } $bonn->set_attribute('label', 'Graph::Easy'); $grviz = $graph->as_graphviz(); like ($grviz, qr/label="Graph::Easy"/, 'label with non \w needs quoting'); ############################################################################# # flow directions $graph->set_attribute('graph','flow','south'); $grviz = $graph->as_graphviz(); unlike ($grviz, qr/rankdir/, 'flow south needs no rankdir'); like ($grviz, qr/"strict" -> Berlin/, 'edge direction normal'); $graph->set_attribute('graph','flow','west'); $grviz = $graph->as_graphviz(); like ($grviz, qr/rankdir=LR/, 'flow west has LR and reversed edges'); like ($grviz, qr/Berlin -> "strict"/, 'edge direction reversed'); like ($grviz, qr/dir=back/, 'edge direction reversed'); $graph->set_attribute('graph','flow','up'); $grviz = $graph->as_graphviz(); unlike ($grviz, qr/rankdir/, 'flow west has TB and reversed edges'); like ($grviz, qr/Berlin -> "strict"/, 'edge direction reversed'); like ($grviz, qr/dir=back/, 'edge direction reversed'); ############################################################################# # arrow styles # flow is up, so arrowhead becomes arrowtail: $graph->set_attribute('edge', 'arrow-style', 'closed'); is ($graph->get_attribute('edge', 'arrow-style'), 'closed'); $grviz = $graph->as_graphviz(); like ($grviz, qr/arrowtail=empty/, 'arrow-style closed => empty'); $graph->set_attribute('edge', 'arrow-style', 'filled'); is ($graph->get_attribute('edge', 'arrow-style'), 'filled'); $grviz = $graph->as_graphviz(); like ($grviz, qr/arrowtail=normal/, 'arrow-style filled => normal'); # set flow to down, so arrowtail becomes arrowhead again $graph->set_attribute('graph','flow','down'); $grviz = $graph->as_graphviz(); like ($grviz, qr/arrowhead=normal/, 'arrow-style filled => normal'); $graph->del_attribute('edge','arrow-style'); $edge->set_attribute('arrow-style','filled'); is ($graph->error(),'', 'no error'); $grviz = $graph->as_graphviz(); like ($grviz, qr/arrowhead=normal/, 'arrow-style filled => normal'); $edge->set_attribute('arrow-style','none'); is ($graph->error(),'', 'no error'); $grviz = $graph->as_graphviz(); like ($grviz, qr/arrowhead=none/, 'arrow-style none'); ############################################################################# ############################################################################# # undirected edges my $e = $graph->add_edge('A','B'); $e->undirected(1); $e->bidirectional(0); $grviz = $graph->as_graphviz(); like ($grviz, qr/A -> B.*arrowhead=none/, 'arrowhead on undirected edge'); like ($grviz, qr/A -> B.*arrowtail=none/, 'arrowtail on undirected edge'); ############################################################################# # bidirectional edges $e->undirected(0); $e->bidirectional(1); $grviz = $graph->as_graphviz(); like ($grviz, qr/A -> B.*arrowhead=open/, 'arrowhead on bidirectional edge'); like ($grviz, qr/A -> B.*arrowtail=open/, 'arrowtail on bidirectional edge'); ############################################################################# ############################################################################# # label-color vs. color $e->bidirectional(0); $e->set_attribute('color','red'); $e->set_attribute('label-color','blue'); $e->set_attribute('label','A to B'); $grviz = $graph->as_graphviz(); like ($grviz, qr/A -> B \[ color="#ff0000", fontcolor="#0000ff", label/, 'label-color'); ############################################################################# # missing label-color (fall back to color) $e->del_attribute('label-color'); $grviz = $graph->as_graphviz(); like ($grviz, qr/A -> B \[ color="#ff0000", fontcolor="#ff0000", label/, 'label-color'); $e->del_attribute('label','A to B'); ############################################################################# # no label, no fontcolor nec.: $e->del_attribute('label'); $grviz = $graph->as_graphviz(); like ($grviz, qr/A -> B \[ color="#ff0000" \]/, 'label-color'); ############################################################################# # link vs. autolink and linkbase $graph->set_attribute('node','linkbase','http://bloodgate.com/'); $grviz = $graph->as_graphviz(); unlike ($grviz, qr/bloodgate.com/, 'linkbase alone does nothing'); unlike ($grviz, qr/link/, 'linkbase alone does nothing'); $graph->set_attribute('node','autolink','name'); $grviz = $graph->as_graphviz(); like ($grviz, qr/URL="http:\/\/bloodgate.com/, 'linkbase plus link'); $graph->del_attribute('node','autolink'); $graph->set_attribute('graph','autolink','name'); is ($graph->attribute('graph','autolink'), 'name', 'autolink=name'); $grviz = $graph->as_graphviz(); like ($grviz, qr/URL="http:\/\/bloodgate.com/, 'linkbase plus link'); ############################################################################# # link vs. autolink and linkbase $bonn->set_attribute('point-style', 'star'); is ($graph->error(),'', 'no error'); $grviz = $graph->as_graphviz(); unlike ($grviz, qr/point-style/, 'point-style is filtered out'); ############################################################################# # node shape "none" $bonn->{name} = 'Bonn'; $bonn->set_attribute( 'shape' => 'none' ); $grviz = $graph->as_graphviz(); like ($grviz, qr/[^"]Bonn[^"]/, 'contains Bonn unquoted'); like ($grviz, qr/Bonn.*shape=plaintext/, 'contains shape=plaintext'); # some different node shapes for my $s (qw/ invhouse invtrapezium invtriangle triangle octagon hexagon pentagon house septagon trapezium /) { $bonn->set_attribute( 'shape' => $s ); $grviz = $graph->as_graphviz(); like ($grviz, qr/[^"]Bonn[^"]/, 'contains Bonn unquoted'); like ($grviz, qr/Bonn.*shape=$s/, "contains shape=$s"); } ############################################################################# # font-size support $bonn->set_attribute( 'font-size' => '2em' ); $grviz = $graph->as_graphviz(); like ($grviz, qr/[^"]Bonn[^"]/, 'contains Bonn unquoted'); like ($grviz, qr/Bonn.*fontsize=22/, '11px eq 1em'); ############################################################################# # bold-dash, broad and wide edges $bonn->set_attribute( 'border-style' => 'broad' ); $grviz = $graph->as_graphviz(); like ($grviz, qr/[^"]Bonn[^"]/, 'contains Bonn unquoted'); like ($grviz, qr/Bonn.*style="filled,setlinewidth\(5\)"/, '5 pixel for broad border'); ############################################################################# # quoting of special characters $bonn->set_attribute( 'label' => '$a = 2;' ); $grviz = $graph->as_graphviz(); like ($graph->as_graphviz(), qr/Bonn.*label="\$a = 2;"/, 'contains label unquoted'); $bonn->set_attribute( 'label' => '2"' ); $grviz = $graph->as_graphviz(); like ($grviz, qr/Bonn.*label="2\\""/, 'contains label 2"'); ############################################################################# # groups as clusters $graph = Graph::Easy->new(); ($bonn, $berlin, $edge) = $graph->add_edge ('Bonn', 'Berlin'); my $group = $graph->add_group ('Test:'); $group->add_node($bonn); $group->add_node($berlin); $grviz = $graph->as_graphviz(); like ($grviz, qr/subgraph "cluster\d+"\s+\{/, 'contains cluster'); ############################################################################# # nodes w/o links and attributes in a group $graph = Graph::Easy->new(); $bonn = $graph->add_node ('Bonn'); $berlin = $graph->add_node ('Berlin'); $group = $graph->add_group ('Test:'); $group->add_node($bonn); $group->add_node($berlin); $grviz = $graph->as_graphviz(); like ($grviz, qr/Bonn(.|\n)*Berlin(.|\n)*\}(.|\n)*\}/, 'contains nodes inside group'); ############################################################################# # node with border-style: none: $graph = Graph::Easy->new(); $bonn = $graph->add_node ('Bonn'); $bonn->set_attribute('border-style', 'none'); $grviz = $graph->as_graphviz(); like ($grviz, qr/Bonn.*color="#ffffff".*style=filled/, 'contains color white, style filled'); ############################################################################# # node with shape: rounded; $bonn->del_attribute('border-style'); $bonn->set_attribute( 'shape' => 'rounded' ); $grviz = $graph->as_graphviz(); like ($grviz, qr/[^"]Bonn[^"]/, 'contains Bonn unquoted'); like ($grviz, qr/Bonn.*style="rounded,filled"/, 'contains rounded,filled'); ############################################################################# # invisible nodes and node with shape: point; $bonn->del_attribute('border-style'); $bonn->set_attribute( 'shape' => 'invisible' ); $grviz = $graph->as_graphviz(); like ($grviz, qr/[^"]Bonn[^"]/, 'contains Bonn unquoted'); like ($grviz, qr/Bonn.*shape=plaintext/, 'contains shape plaintext'); like ($grviz, qr/Bonn.*label=" "/, 'contains label=" "'); $bonn->del_attribute('border-style'); $bonn->set_attribute( 'shape' => 'point' ); $grviz = $graph->as_graphviz(); like ($grviz, qr/[^"]Bonn[^"]/, 'contains Bonn unquoted'); like ($grviz, qr/Bonn.*shape=plaintext/, 'contains shape plaintext'); like ($grviz, qr/Bonn.*label="*"/, 'contains label="*"'); ############################################################################# # edge styles double and double-dash $graph = Graph::Easy->new(); ($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin'); $edge->set_attribute('style','double'); $grviz = $graph->as_graphviz(); like ($grviz, qr/[^"]Bonn[^"].*color="#000000:#000000/, 'contains Bonn and black:black'); unlike ($grviz, qr/style="?solid/, "doesn't contain solid"); $edge->set_attribute('style','double-dash'); $grviz = $graph->as_graphviz(); like ($grviz, qr/[^"]Bonn[^"].*color="#000000:#000000/, 'contains Bonn and black:black'); unlike ($grviz, qr/style="?solid/, "doesn't contain solid"); like ($grviz, qr/style="?dashed/, 'contains solid'); ############################################################################# # root node (also testing that a root of '0' actually works) $graph = Graph::Easy->new(); ($bonn,$berlin,$edge) = $graph->add_edge ('0','1'); $graph->set_attribute('root','0'); $grviz = $graph->as_graphviz(); like ($grviz, qr/root=0/, 'contains root=0'); like ($grviz, qr/0.*rank=0/, 'contains rank=0'); $graph = Graph::Easy->new(); ($bonn,$berlin,$edge) = $graph->add_edge ('a','b'); $graph->set_attribute('root','b'); $grviz = $graph->as_graphviz(); like ($grviz, qr/root=b/, 'contains root=0'); like ($grviz, qr/b.*rank=0/, 'contains rank=0'); ############################################################################# # headport/tailport $graph = Graph::Easy->new(); ($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin'); $edge->set_attribute('start','west'); $edge->set_attribute('end','east'); $grviz = $graph->as_graphviz(); like ($grviz, qr/tailport=w/, 'contains tailport=w'); like ($grviz, qr/headport=e/, 'contains headport=e'); # headport/tailport with relative flow $edge->set_attribute('start','right'); $edge->set_attribute('end','left'); $grviz = $graph->as_graphviz(); like ($grviz, qr/tailport=s/, 'contains tailport=s'); like ($grviz, qr/headport=n/, 'contains headport=n'); ############################################################################# # colorscheme support $graph = Graph::Easy->new(); ($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin'); $graph->add_group('Cities'); $graph->set_attribute('node','colorscheme','pastel19'); $graph->set_attribute('color','red'); $edge->set_attribute('color','1'); $berlin->set_attribute('color','1'); $berlin->set_attribute('colorscheme','set23'); $bonn->set_attribute('color','1'); $grviz = $graph->as_graphviz(); like ($grviz, qr/graph(.|\n)*color="#ff0000"/, 'contains graph color=#ff0000'); like ($grviz, qr/Bonn.*color="#fbb4ae"/, 'contains Bonn color=#fbb4ae'); like ($grviz, qr/Berlin.*color="#66c2a5"/, 'contains Berlin color=#66c2a5'); like ($grviz, qr/->.*Berlin.*color="#a6cee3"/, 'contains edge with default color 1 from set312'); ############################################################################# # test inheritance of colorscheme for edges, groups and anon things: $graph->set_attribute('colorscheme','pastel19'); $grviz = $graph->as_graphviz(); like ($grviz, qr/->.*Berlin.*color="#fbb4ae"/, 'contains edge with color 1 from pastel19'); ############################################################################# # autolabel is skipped $graph->set_attribute('node','autolabel','15'); $grviz = $graph->as_graphviz(); unlike ($grviz, qr/autolabel/, "doesn't contain autolabel"); ############################################################################# # test that the attributes group, rows and columns are skipped $graph = Graph::Easy->new(); ($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin'); $group = $graph->add_group('Cities'); $group->add_nodes($bonn, $berlin); $bonn->set_attribute('size','2,2'); $graph->layout(); $grviz = $graph->as_graphviz(); unlike ($grviz, qr/rows=/, 'does not contain rows='); unlike ($grviz, qr/columns=/, 'does not contain columns='); unlike ($grviz, qr/group=/, 'does not contain group='); ############################################################################# # test output of fillcolor and color of groups $graph = Graph::Easy->new(); ($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin'); $group = $graph->add_group('Cities'); $group->add_nodes($bonn, $berlin); $group->set_attribute('fill','red'); $group->set_attribute('color','blue'); $graph->layout(); $grviz = $graph->as_graphviz(); like ($grviz, qr/fillcolor="#ff0000"/, 'fillcolor=red'); like ($grviz, qr/fontcolor="#0000ff"/, 'fontcolor=blue'); ############################################################################# # test group class attributes $graph = Graph::Easy->new(); ($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin'); $group = $graph->add_group('Cities'); $group->add_nodes($bonn, $berlin); $graph->set_attribute('group','fill','red'); $graph->layout(); $grviz = $graph->as_graphviz(); like ($grviz, qr/cluster(.|\n)*fillcolor="#ff0000"/, 'fillcolor=blue'); ############################################################################# # node->as_graphviz() $graph = Graph::Easy->new(); ($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin'); $group = $graph->add_group('Cities'); $group->add_nodes($bonn, $berlin); $grviz = $graph->as_graphviz(); unlike ($grviz, qr/Berlin.*label=.*Berlin/, "label isn't output needlessly"); ############################################################################# # HSV colors and alpha channel should be preserved in output $graph = Graph::Easy->new(); ($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin'); $group = $graph->add_group('Cities'); $group->add_nodes($bonn, $berlin); # as hex (not preserved) due to alpha channel $bonn->set_attribute('color', 'hsv(0, 1.0, 0.5, 0.5)'); $berlin->set_attribute('color', '#ff000080'); # preserved $graph->set_attribute('color', 'hsv(0, 0.6, 0.7)'); $grviz = $graph->as_graphviz(); like ($grviz, qr/fontcolor="0 0.6 0.7"/, "graph color was preserved"); like ($grviz, qr/Berlin.*fontcolor="#ff000080"/, "Berlin's color got converted"); like ($grviz, qr/Bonn.*fontcolor="#8000007f"/, "Bonn's color got converted"); ############################################################################# # edge label $graph = Graph::Easy->new(); ($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin'); $edge->set_attribute('label','car'); $grviz = $graph->as_graphviz(); like ($grviz, qr/label=car/, "edge label appears in output"); ############################################################################# # fill as class attribute $graph = Graph::Easy->new(); ($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin'); $bonn->set_attribute('class','red'); $graph->set_attribute('node.red', 'fill', 'red'); $grviz = $graph->as_graphviz(); like ($grviz, qr/fillcolor="#ff0000"/, "contains fill red"); ############################################################################# # \c in labels $graph = Graph::Easy->new(); $graph->set_attribute('label', 'foo\cbar'); ($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin'); $bonn->set_attribute('label', 'bar\cbar'); $grviz = $graph->as_graphviz(); unlike ($grviz, qr/\\c/, "no \\c in output"); ############################################################################# # borderwidth == 0 overrides style $graph = Graph::Easy->new(); ($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin'); $bonn->set_attribute('borderstyle','dashed'); $bonn->set_attribute('borderwidth','0'); $berlin->set_attribute('borderstyle','double'); $berlin->set_attribute('borderwidth','0'); $grviz = $graph->as_graphviz(); print $grviz; unlike ($grviz, qr/style=.*dashed/, "no dashed in output"); unlike ($grviz, qr/peripheries/, "no peripheries in output"); ############################################################################# # subgraph #$graph = Graph::Easy->new(); my $g = Graph::Easy->new; my $a_ = $g->add_group('A'); my $b_ = $g->add_group('B'); my $c = $g->add_group('C'); my $d = $g->add_group('D'); my $n1 = $g->add_node('one'); my $n2 = $g->add_node('two'); my $n3 = $g->add_node('three'); my $n4 = $g->add_node('four'); $a_->add_member($n1); $b_->add_member($c); $b_->add_member($n2); $a_->add_member($b_); $c->add_member($n3); $d->add_member($n4); $grviz = $g->as_graphviz(); is($a_->{_order},1,'subgraph A is level 1'); is($d->{_order},1,'subgraph D is level 1'); is($b_->{_order},2,'subgraph B is level 2'); is($c->{_order},3,'subgraph C is level 3'); like($grviz,qr/subgraph "cluster\d+" {\n label="A";\n subgraph "cluster\d+" {/,'subgraph indent'); ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/node_mc.t�������������������������������������������������������������������������0000644�0000764�0000764�00000005414�11675050456�015350� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # test nodes with more than one cell use Test::More; use strict; BEGIN { plan tests => 30; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy::Node") or die($@); use_ok ("Graph::Easy") or die($@); }; can_ok ("Graph::Easy::Node", qw/ new /); ############################################################################# my $node = Graph::Easy::Node->new(); is (ref($node), 'Graph::Easy::Node'); is ($node->error(), '', 'no error yet'); is ($node->connections(), 0, 'no connections yet'); my $other = Graph::Easy::Node->new(); ############################################################################# # connections() tests my $graph = Graph::Easy->new( ); $other = Graph::Easy::Node->new( 'Name' ); $graph->add_edge ($node, $other); is ($node->connections(), 1, 'one connection'); ############################################################################# # grow tests for (1..4) { my $o = Graph::Easy::Node->new( "Name $_" ); $graph->add_edge ($node, $o); } is ($node->connections(), 5, '5 connections'); $node->_grow(); is ($node->connections(), 5, '5 connections'); is ($node->columns(), 1, '1 column'); is ($node->rows(), 3, '3 rows'); is ($node->is_multicelled(), 1, 'is multicelled'); ############################################################################# # edges_to() tests # this will delete the old Graph::Easy object in graph, and clean out # the refs in the nodes/edges. Thus $node will have {edges} == undef. $graph = Graph::Easy->new(); is ($node->{edges}, undef, 'cleanup worked'); $other = Graph::Easy::Node->new( "other" ); my @E; for (1..5) { push @E, scalar $graph->add_edge ($node, $other); } @E = sort { $a->{id} <=> $b->{id} } @E; is ($node->connections(), 5, '5 connections'); is (scalar $node->edges_to($other), 5, '5 edges from node to other'); my @E2 = $node->edges_to($other); @E2 = sort { $a->{id} <=> $b->{id} } @E2; for (1..5) { is ($E[$_], $E2[$_], 'edges_to() worked'); } my @suc = $node->successors(); is (scalar @suc, 1, 'one successor'); is ($suc[0], $other, 'one successor'); #use Data::Dumper; print Dumper(\@suc); ############################################################################# # node placement (multi-cell) my $cells = { }; my $parent = { cells => $cells }; is ($node->_do_place(1,1,$parent), 1, 'node can be placed'); is (scalar keys %$cells, 3, '3 entries (3 rows)'); is ($cells->{"1,1"}, $node, 'node was really placed'); my $filler = $cells->{"1,2"}; is (ref($filler), 'Graph::Easy::Node::Cell', 'filler cell'); is ($filler->node(), $node, 'filler associated with node'); is ($node->_do_place(1,1,$parent), 0, 'node cannot be placed again'); is ($cells->{"1,1"}, $node, 'node still there placed'); is (scalar keys %$cells, 3, 'still three entries'); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/group/����������������������������������������������������������������������������0000755�0000764�0000764�00000000000�12150110221�014657� 5����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/group/0230.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000335�11675050456�016035� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# groups (with border none) group.cities { label: Cities; } ( Cities [ Ulm ] -> [ Lahn ] -> [ Bonn ] -> [ Trier ] ) { class: cities; background: #ff80a0; border: none; } [ Koblenz ] -> [ Berlin ] [ Frankfurt ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/group/0010.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000503�11675050456�016026� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Elan and Roy on Teamwork # http://www.cafepress.com/orderofthestick.10272636?zoom=yes#zoom [ Teamwork ] { background: yellow; title: Elan; } -is the key to --> [ Victory ] { background: red; title: Roy; } --> [ Order of the Stick ] { background: #f080a0; } [ Victory ] -Treasure--> [ Haley ] { background: lightblue; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/group/0131.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000620�11675050456�016032� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Three non-overlapping groups group.dmz { background: #ffa070; } group.outer { background: #f07070; } ( Internal Network [ Workstation ] ) { background: #70b070; } [ Workstation ] --> [ Inner Firewall ] ( DMZ [ Inner Firewall ] -> [ Proxy ] --> [ Outer Firewall ] ) { class: dmz; } ( Outer [ Internet ] ) { class: outer } [ Outer Firewall ] --> [ Internet ] [ Proxy ] --> [ Database ] ����������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/re_layout.t�����������������������������������������������������������������������0000644�0000764�0000764�00000004524�11675050456�015750� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # test layouts after setting attributes use Test::More; use strict; BEGIN { plan tests => 12; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy") or die($@); }; can_ok ('Graph::Easy', qw/ new /); ############################################################################# # setup a graph my $graph = Graph::Easy->new(); is (ref($graph), 'Graph::Easy'); is ($graph->error(), '', 'no error yet'); my ($bonn,$berlin,$edge) = $graph->add_edge('Bonn','Berlin'); ############################################################################# # lay out as ascii my $ascii = $graph->as_ascii(); is ($ascii, <<EOF +------+ +--------+ | Bonn | --> | Berlin | +------+ +--------+ EOF , 'as_ascii'); ############################################################################# # change label of Bonn to be longer $bonn->set_attribute('label', 'Frankfurt a. Main'); $ascii = $graph->as_ascii(); is ($ascii, <<EOF +-------------------+ +--------+ | Frankfurt a. Main | --> | Berlin | +-------------------+ +--------+ EOF , 'as_ascii'); $bonn->set_attribute('label', 'Frankfurt\n(a. Main)'); $ascii = $graph->as_ascii(); is ($ascii, <<EOF +-----------+ +--------+ | Frankfurt | | Berlin | | (a. Main) | --> | | +-----------+ +--------+ EOF , 'as_ascii'); # Change label of Bonn to be shorter (and one line high, this also tests # resetting the height of Berlin even though we did not change an attribute # on Berlin itself: $bonn->set_attribute('label', 'Frankfurt'); $ascii = $graph->as_ascii(); is ($ascii, <<EOF +-----------+ +--------+ | Frankfurt | --> | Berlin | +-----------+ +--------+ EOF , 'as_ascii'); is ($bonn->{w}, 13, 'w is 13'); is ($bonn->{h}, 3, 'h is 2'); ############################################################################# # change edge label $edge->set_attribute('label', 'Test'); $ascii = $graph->as_ascii(); is ($ascii, <<EOF +-----------+ Test +--------+ | Frankfurt | ------> | Berlin | +-----------+ +--------+ EOF , 'as_ascii'); $edge->set_attribute('label', 'Testtest'); $ascii = $graph->as_ascii(); is ($ascii, <<EOF +-----------+ Testtest +--------+ | Frankfurt | ----------> | Berlin | +-----------+ +--------+ EOF , 'as_ascii'); ############################################################################# ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/copy.t����������������������������������������������������������������������������0000644�0000764�0000764�00000007156�12150106473�014711� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # Test the copy() method use Test::More; use strict; BEGIN { plan tests => 55; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy") or die($@); }; can_ok ('Graph::Easy', qw/ new copy /); ############################################################################# my $graph = Graph::Easy->new(); check_graph($graph); my $copy = $graph->copy(); check_graph($copy); my $bonn = Graph::Easy::Node->new( name => 'Bonn' ); my $berlin = Graph::Easy::Node->new( 'Berlin' ); my $edge = $graph->add_edge ($bonn, $berlin); my $group = $graph->add_group ('Cities'); is (ref($edge), 'Graph::Easy::Edge', 'add_edge() returns the new edge'); $bonn->set_attribute('color','red'); $edge->set_attribute('fill','blue'); $graph->set_attribute('graph','fill','purple'); check_members($graph); $copy = $graph->copy(); check_members($copy); ############################################################################# # settings on the graph object itself $graph->fatal_errors(); $graph->catch_warnings(1); $graph->catch_errors(1); check_settings($graph); $copy = $graph->copy(); check_settings($copy); ############################################################################# # groups with nodes $graph = Graph::Easy->new('( Cities [ Bonn ] -> [ Berlin ] )' ); $copy = $graph->copy(); $group = $graph->group('Cities'); is (scalar $group->nodes(), 2, '2 nodesi in original group'); $group = $copy->group('Cities'); is (scalar $group->nodes(), 2, '2 nodes in copied group'); ############################################################################# ############################################################################# sub check_settings { my $graph = shift; is ($graph->{_catch_warnings}, 1, 'catch warnings'); is ($graph->{_catch_errors}, 1, 'catch errors'); is ($graph->{fatal_errors}, 1, 'fatal errors'); } sub check_members { my $graph = shift; # use Data::Dumper; print Dumper($graph); is ($graph->nodes(), 2, '2 nodes added'); is ($graph->edges(), 1, '1 edge'); is ($graph->as_txt(), <<EOF graph { fill: purple; } [ Bonn ] { color: red; } ( Cities ) [ Bonn ] --> { fill: blue; } [ Berlin ] EOF , 'as_txt for 2 nodes'); is (ref($graph->edge($bonn,$berlin)), 'Graph::Easy::Edge', 'edge from objects'); is ($graph->edge($berlin,$bonn), undef, 'berlin not connecting to bonn'); is (ref($graph->edge('Bonn', 'Berlin')), 'Graph::Easy::Edge', 'edge from names'); my @E = $graph->edges(); my $en = ''; for my $e (@E) { $en .= $e->style() . '.'; } is ($en, 'solid.', 'edges() in list context'); is( $graph->node('Bonn')->attribute('color'),'red', 'Bonn is red'); is( $graph->edge('Bonn','Berlin')->attribute('fill'),'blue', 'Bonn->Berlin is blue'); is( $graph->get_attribute('fill'), 'purple', 'graph is purple'); } ############################################################################# sub check_graph { my $graph = shift; # use Data::Dumper; print Dumper($graph); is (ref($graph), 'Graph::Easy'); is ($graph->error(), '', 'no error yet'); is ($graph->output_format(), 'html', 'default output format is html'); is ($graph->timeout(), 5, '5 seconds'); is ($graph->strict(), 1, 'is strict'); is ($graph->nodes(), 0, '0 nodes'); is ($graph->edges(), 0, '0 edges'); is ($graph->border_attribute('graph'), 'none', 'graph border is none'); is ($graph->border_attribute('group'), 'dashed', 'group border is dashed 1px black'); is ($graph->border_attribute('node'), 'solid', 'node border is solid 1px black'); is (join (',', $graph->edges()), '', '0 edges'); like ($graph->output(), qr/table/, 'default output worked'); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/easypm.t��������������������������������������������������������������������������0000644�0000764�0000764�00000027503�12150106670�015232� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use Test::More; use strict; BEGIN { plan tests => 138; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy") or die($@); }; can_ok ("Graph::Easy", qw/ new css as_html as_html_page as_txt as_vcg as_boxart as_gdl as_ascii as_ascii_html as_graphviz as_svg as_ascii_file as_html_file as_svg_file as_vcg_file as_boxart_file as_gdl_file as_graphml as_graphml_file as_debug html_page_header html_page_footer error edge node nodes edges edges_within anon_nodes svg_information add_edge add_node add_anon_node merge_nodes del_node del_edge flip_edges rename_node rename_group set_attributes set_attribute get_attribute get_attributes get_color_attribute default_attribute raw_attribute color_attribute attribute del_attribute score id group groups add_group del_group is_simple_graph is_simple is_directed is_undirected text_style text_styles text_styles_as_css animation_as_graph /); ############################################################################# # adding edges/nodes my $graph = Graph::Easy->new(); is (ref($graph), 'Graph::Easy'); is ($graph->error(), '', 'no error yet'); is ($graph->is_simple_graph(), 1, 'simple graph (0 nodes)'); is ($graph->is_simple(), 1, 'simple graph (0 nodes)'); is ($graph->is_directed(), 1, 'directed graph'); my $node = Graph::Easy::Node->new( name => 'Bonn' ); my $node2 = Graph::Easy::Node->new( name => 'Berlin' ); is (scalar $graph->nodes(), 0, 'no nodes'); is (scalar $graph->anon_nodes(), 0, 'no anon nodes'); is (scalar $graph->edges(), 0, 'no edges'); $graph->add_edge( $node, $node2 ); is (scalar $graph->nodes(), 2, '2 nodes'); is (scalar $graph->edges(), 1, '1 edges'); is ($graph->is_simple_graph(), 1, 'simple graph (2 nodes, 1 edge)'); my $node3 = Graph::Easy::Node->new( name => 'Frankfurt'); $graph->add_edge( $node2, $node3 ); is (scalar $graph->nodes(), 3, '3 nodes'); is (scalar $graph->edges(), 2, '2 edges'); is ($graph->is_simple_graph(), 1, 'still simple graph'); my $node4 = Graph::Easy::Node->new( name => 'Dresden' ); $graph->add_edge( $node3, $node4 ); is (scalar $graph->nodes(), 4, '4 nodes'); is (scalar $graph->edges(), 3, '3 edges'); is ($graph->is_simple_graph(), 1, 'still simple graph'); my $node5 = Graph::Easy::Node->new( name => 'Potsdam' ); $graph->add_edge( $node2, $node5 ); is (scalar $graph->nodes(), 5, '5 nodes'); is (scalar $graph->edges(), 4, '4 edges'); is ($graph->is_simple_graph(), 1, 'still simple graph'); my $node6 = Graph::Easy::Node->new( name => 'Cottbus' ); $graph->add_edge( $node5, $node6 ); is (scalar $graph->nodes(), 6, '6 nodes'); is (scalar $graph->edges(), 5, '5 edges'); is ($graph->is_simple_graph(), 1, 'still simple graph'); ############################################################################# # attribute tests is ($graph->attribute('background'), 'inherit', 'graph background = undef'); is ($graph->attribute('node', 'background'), 'inherit', 'node background = undef'); is ($graph->attribute('node', 'fill'), 'white', 'node { fill: white }'); is ($graph->attribute('graph', 'border'), 'none', 'graph { border: none; }'); $graph->set_attributes ('graph', { color => 'white', background => 'red' }); is ($graph->attribute('graph', 'background'), 'red', 'now: graph { background: red }'); is ($graph->attribute('graph', 'color'), 'white', 'now: graph { color: white }'); good_css ($graph); ############################################################################# # ID tests is ($graph->id(), '', 'id is empty string'); is ($graph->id('42'), '42', 'id is now 42'); good_css($graph); ############################################################################# # ID tests with sub-classes $graph->set_attributes ('node.cities', { color => '#0000ff' } ); good_css($graph, 'table.graph42 .node_cities', 'table.graph42 .node,table.graph42 .node_anon,table.graph42 .node_cities' ); ############################################################################# # group tests is ($graph->groups(), 0, 'no groups yet'); is ($graph->group('foo'), undef, 'no groups yet'); is ($graph->groups(), 0, 'no groups yet'); my $group = Graph::Easy::Group->new( { name => 'Cities' } ); $graph->add_group($group); is ($graph->group('Cities'), $group, "group 'cities'"); is ($graph->groups(), 1, 'one group'); is ($graph->group('cities'), undef, 'no group'); is ($graph->groups(), 1, 'one group'); is ($graph->as_txt(), <<HERE graph { background: red; color: white; } node.cities { color: #0000ff; } ( Cities ) [ Bonn ] --> [ Berlin ] [ Berlin ] --> [ Frankfurt ] [ Berlin ] --> [ Potsdam ] [ Frankfurt ] --> [ Dresden ] [ Potsdam ] --> [ Cottbus ] HERE , 'with empty group Cities'); $node->add_to_group($group); is ($graph->as_txt(), <<HERE graph { background: red; color: white; } node.cities { color: #0000ff; } ( Cities [ Bonn ] ) [ Bonn ] --> [ Berlin ] [ Berlin ] --> [ Frankfurt ] [ Berlin ] --> [ Potsdam ] [ Frankfurt ] --> [ Dresden ] [ Potsdam ] --> [ Cottbus ] HERE , 'with empty group Cities'); ############################################################################# # title/link/autolink/autotitle/linkbase not in CSS $graph->set_attributes ('node', { link => 123, title => 123, autolink => 'name', autotitle => 'name' } ); $graph->set_attributes ('graph', { linkbase => '123/' } ); good_css ($graph); # check that add_node( 'name' ) works $graph = Graph::Easy->new(); my $bonn = $graph->add_node( 'Bonn' ); is (scalar $graph->nodes(), 1, 'one node'); is ($graph->node('Bonn'), $bonn, 'add_node returned $bonn'); # already in graph, try to add as "name" my $bonn2 = $graph->add_node( 'Bonn' ); is (scalar $graph->nodes(), 1, 'one node'); is ($bonn2, $graph->node('Bonn'), 'add_node returned $bonn'); is ($bonn, $bonn2, 'same node'); # already in graph, try to add as node object my $bonn3 = $graph->add_node( $bonn ); is (scalar $graph->nodes(), 1, 'one node'); is ($bonn3, $graph->node('Bonn'), 'add_node returned $bonn'); is ($bonn, $bonn3, 'same node'); my $bonn5 = Graph::Easy::Node->new('Bonn'); my $bonn4 = $graph->add_node( $bonn5); #make sure that $bonn is not replaced by $bonn5 in graph! is (scalar $graph->nodes(), 1, 'one node'); is ($bonn4, $graph->node('Bonn'), 'add_node returned $bonn'); is ($bonn, $bonn4, 'same node'); ############################################################################# # adding an edge with two plain scalars as names $graph = Graph::Easy->new(); my ($T1,$T2,$edge) = $graph->add_edge( 'Test', 'Test2' ); is (scalar $graph->nodes(), 2, '2 nodes'); is (scalar $graph->edges(), 1, '1 edge'); is ($graph->edge('Test', 'Test2'), $edge, 'edge("A","B") works'); is ($graph->edge($T1,$T2), $edge, 'edge($A,$B) works'); # adding a multi-edge $graph->add_edge( 'Test', 'Test2' ); is (scalar $graph->nodes(), 2, '2 nodes'); is (scalar $graph->edges(), 2, '2 edges'); # this assumes "Test" is created before "Test2" my @N = sort { $a->{id} <=> $b->{id} } $graph->nodes(); my @E = $N[0]->edges_to($N[1]); is (@E, 2, '2 edges from Test to Test2'); # this should work now: my $ascii = $graph->as_ascii(); like ($ascii, qr/Test/, 'Test found in output'); like ($ascii, qr/Test2/, 'Test found in output'); # test that add_edge('Test','Test') does not create two nodes $graph = Graph::Easy->new(); my ($a,$b,$e) = $graph->add_edge( 'Test', 'Test' ); is ($a->{id}, $b->{id}, "one node for ('test','test')"); is ($a, $b, "one object for ('test','test')"); ############################################################################# # is_ascii_html() $ascii = $graph->as_ascii_html(); like ($ascii, qr/<pre>(.|\n)*<\/pre>/, 'as_ascii_html'); ############################################################################# # is_simple_graph() $graph = Graph::Easy->new(); $edge = $graph->add_edge( 'Test', 'Test2' ); is ($graph->is_simple_graph(), 1, 'still simple graph'); $edge = $graph->add_edge( 'Test', 'Test2' ); is ($graph->is_simple_graph(), 0, 'not simple graph'); $edge = $graph->add_edge( 'Test', 'Test2' ); is ($graph->is_simple_graph(), 0, 'not simple graph'); $graph = Graph::Easy->new(); $edge = $graph->add_edge( 'Test', 'Test' ); is ($graph->is_simple_graph(), 1, 'still simple graph'); $edge = $graph->add_edge( 'Test', 'Test2' ); is ($graph->is_simple_graph(), 1, 'still simple graph'); $edge = $graph->add_edge( 'Test', 'Test' ); is ($graph->edges(), 3, '3 edges'); is ($graph->nodes(), 2, '2 nodes'); is ($graph->is_simple_graph(), 0, 'not simple graph'); ############################################################################# # adding nodes with name '0' and '' $graph = Graph::Easy->new(); $node = Graph::Easy::Node->new( { name => '0' } ); $node = $graph->add_node($node); is ($graph->nodes(), '1', 'one node'); is ($graph->{nodes}->{0}, $node, 'got inserted with name 0'); is ($graph->node('0'), $node, 'found node 0 again'); ############################################################################# # renaming nodes ############################################################################# # node is not a reference $graph = Graph::Easy->new(); $node = $graph->rename_node('abc','bcd'); is ($graph->nodes(), '1', 'one node'); is ($graph->{nodes}->{bcd}, $node, 'got inserted with name bcd'); ############################################################################# # node is not yet part of any graph $graph = Graph::Easy->new(); $node = Graph::Easy::Node->new( { name => 'abc' } ); my $new_node = $graph->rename_node($node,'bcd'); is ($graph->nodes(), '1', 'one node'); is ($new_node->{name}, 'bcd', 'got renamed'); is ($graph->{nodes}->{bcd}, $node, 'got inserted with name bcd'); is ($node->{graph}, $graph, 'node is part of this graph'); is ($new_node, $node, 'returned node'); ############################################################################# # node is not part of another graph $graph = Graph::Easy->new(); my $g2 = Graph::Easy->new(); $node = $g2->add_node( 'abc' ); $new_node = $graph->rename_node($node,'bcd'); is ($graph->nodes(), '1', 'one node'); is ($g2->nodes(), '0', 'other graph has now zero'); is ($graph->{nodes}->{bcd}, $node, 'got inserted with name bcd'); is ($node->{graph}, $graph, 'node is part of this graph'); is ($new_node, $node, 'returned node'); ############################################################################# # directed/undirected $graph = Graph::Easy->new(); is ($graph->is_directed(), 1, 'directed graph'); is ($graph->is_undirected(), 0, 'directed graph'); $graph->set_attribute('type','directed'); is ($graph->is_directed(), 1, 'directed graph'); is ($graph->is_undirected(), 0, 'directed graph'); $graph->set_attribute('type','undirected'); is ($graph->is_directed(), 0, 'undirected graph'); is ($graph->is_undirected(), 1, 'undirected graph'); my $ge = Graph::Easy->new( undirected => 1 ); is (ref($ge), 'Graph::Easy'); is ($ge->attribute('type'), 'undirected', 'is undirected'); is ($ge->is_undirected(), 1, 'is undirected'); ############################################################################# # merging nodes $graph = Graph::Easy->new('[A]->[B]->[C]->[D]'); $graph->merge_nodes( 'A', 'B' ); is ($graph->as_txt(), "[ A ] --> [ C ]\n[ C ] --> [ D ]\n", 'merge worked'); $graph->merge_nodes( 'A', 'C', ' ' ); is ($graph->as_txt(), "[ A ] { label: A C; }\n\n[ A ] --> [ D ]\n", 'merge worked'); $graph->merge_nodes( 'A', 'D', ' \n ' ); is ($graph->as_txt(), "[ A ] { label: A C \\n D; }\n\n", 'merge worked'); 1; # all tests done ############################################################################# sub good_css { my $graph = shift; my $css = $graph->css(); foreach my $class (qw/edge node/, ) { like ($css, qr/table\.graph\d* \.$class/, "$class in css"); } like ($css, qr/graph\d* \{/, "graph in css"); foreach my $add (@_) { like ($css, qr/$add/, "$add in css"); } foreach my $attr (qw/link label title linkbase autotitle autolabel/) { unlike ($css, qr/$attr/, "$attr not in css"); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/split.t���������������������������������������������������������������������������0000644�0000764�0000764�00000006061�11675050456�015076� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # parser.t does general parser tests, this one deals only with "[A|B|C]" style # nodes and tests that this feature does work correctly. use Test::More; use strict; BEGIN { plan tests => 20; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy::Parser") or die($@); }; can_ok ("Graph::Easy::Parser", qw/ new from_text from_file reset error _parse_attributes /); ############################################################################# # parser object my $parser = Graph::Easy::Parser->new(); is (ref($parser), 'Graph::Easy::Parser'); is ($parser->error(), '', 'no error yet'); ############################################################################# # split a node and check all relevant fields my $graph = $parser->from_text("[A|B|C]"); is (scalar $graph->nodes(), 3, '3 nodes'); my $A = $graph->node('ABC.0'); is (ref($A), 'Graph::Easy::Node', 'node is node'); is ($A->origin(), undef, 'A is the origin itself'); my $B = $graph->node('ABC.1'); is (ref($B), 'Graph::Easy::Node', 'node is node'); is ($B->origin(), $A, 'A is the origin of B'); is (join(",", $B->offset()), "1,0", 'B is at +1,0'); my $C = $graph->node('ABC.2'); is (ref($C), 'Graph::Easy::Node', 'node is node'); is ($C->origin(), $B, 'B is the origin of C'); is (join(",", $C->offset()), "1,0", 'C is at +1,0 from B'); ############################################################################# # general split tests my $line = 0; foreach (<DATA>) { chomp; next if $_ =~ /^\s*\z/; # skip empty lines next if $_ =~ /^#/; # skip comments die ("Illegal line $line in testdata") unless $_ =~ /^(.*)\|([^\|]*)$/; my ($in,$result) = ($1,$2); my $txt = $in; $txt =~ s/\\n/\n/g; # insert real newlines Graph::Easy::Node->_reset_id(); # to get "#0" for each test my $graph = $parser->from_text($txt); # reuse parser object if (!defined $graph) { fail($parser->error()); next; } my $got = scalar $graph->nodes(); my @edges = $graph->edges(); my $es = 0; foreach my $e (sort { $a->label() cmp $b->label() } @edges) { $es ++ if $e->label() ne ''; } $got .= '+' . $es if $es > 0; for my $n ( sort { $a->{name} cmp $b->{name} } ($graph->nodes(), $graph->edges()) ) { # normalize color output my $b = Graph::Easy->color_as_hex($n->attribute('fill')); my $dx = $n->{dx}||0; my $dy = $n->{dy}||0; $got .= ";" . $n->name() . "," . $n->label() . "=$dx.$dy." . $b; } is ($got, $result, $in); } __DATA__ # split tests with attributes [A|B|C]|3;ABC.0,A=0.0.#ffffff;ABC.1,B=1.0.#ffffff;ABC.2,C=1.0.#ffffff [A|B|C] { fill: red; }|3;ABC.0,A=0.0.#ff0000;ABC.1,B=1.0.#ff0000;ABC.2,C=1.0.#ff0000 [A|B|C] { label: foo; fill: red; }|3;ABC.0,foo=0.0.#ff0000;ABC.1,foo=1.0.#ff0000;ABC.2,foo=1.0.#ff0000 [A| |C]|3;AC.0,A=0.0.#ffffff;AC.1, =1.0.#ffffff;AC.2,C=1.0.#ffffff [A||B|C]|3;ABC.0,A=0.0.#ffffff;ABC.1,B=0.1.#ffffff;ABC.2,C=1.0.#ffffff [A||B||C]|3;ABC.0,A=0.0.#ffffff;ABC.1,B=0.1.#ffffff;ABC.2,C=0.1.#ffffff [A|| |C]|3;AC.0,A=0.0.#ffffff;AC.1, =0.1.#ffffff;AC.2,C=1.0.#ffffff �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/boxart.t��������������������������������������������������������������������������0000644�0000764�0000764�00000004540�11675050456�015242� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use Test::More; use strict; BEGIN { plan tests => 15; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy") or die($@); use_ok ("Graph::Easy::As_ascii") or die($@); }; can_ok ("Graph::Easy", qw/ as_boxart as_boxart_html as_boxart_html_file as_boxart_file /); ############################################################################# binmode STDERR, ':utf8'; # some of our strings are written in utf8 use utf8; my $graph = Graph::Easy->new(); my ($bonn, $berlin, $edge) = $graph->add_edge ('Bonn', 'Berlin'); my $boxart = $graph->as_boxart(); like ($boxart, qr/Bonn/, 'contains Bonn'); like ($boxart, qr/Berlin/, 'contains Berlin'); unlike ($boxart, qr/-/, "doesn't contain '-'"); ############################################################################# # border tests $berlin->set_attribute('border-style', 'dotted'); ############################################################################# # arrow tests my $open = '──>'; my $closed = '──▷'; my $filled = '──▶'; $boxart = $graph->as_boxart(); like ($boxart, qr/$open/, 'contains edge with open arrow'); $edge->set_attribute('arrow-style', 'open'); $boxart = $graph->as_boxart(); like ($boxart, qr/$open/, 'contains edge with open arrow'); $edge->set_attribute('arrow-style', 'closed'); $boxart = $graph->as_boxart(); like ($boxart, qr/$closed/, 'contains edge with closed arrow'); $edge->set_attribute('arrow-style', 'filled'); $boxart = $graph->as_boxart(); like ($boxart, qr/$filled/, 'contains edge with filled arrow'); ############################################################################# # arrow tests with dotted lines $open = "··>"; $closed = '··▷'; $filled = '··▶'; $edge->set_attribute('style', 'dotted'); $edge->del_attribute('arrow-style'); is ($edge->style(), 'dotted', 'edge is now dotted'); $boxart = $graph->as_boxart(); like ($boxart, qr/$open/, 'contains edge with open arrow'); $edge->set_attribute('arrow-style', 'open'); $boxart = $graph->as_boxart(); like ($boxart, qr/$open/, 'contains edge with open arrow'); $edge->set_attribute('arrow-style', 'closed'); $boxart = $graph->as_boxart(); like ($boxart, qr/$closed/, 'contains edge with closed arrow'); $edge->set_attribute('arrow-style', 'filled'); $boxart = $graph->as_boxart(); like ($boxart, qr/$filled/, 'contains edge with filled arrow'); ����������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/parser_dot_html.t�����������������������������������������������������������������0000644�0000764�0000764�00000002670�11675050456�017133� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # test Graph::Easy::Parser::Graphviz with HTML-like labels use Test::More; use strict; use utf8; BEGIN { plan tests => 7; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy::Parser::Graphviz") or die($@); }; can_ok ("Graph::Easy::Parser::Graphviz", qw/ new /); binmode (STDERR, ':utf8') or die ("Cannot do binmode(':utf8') on STDERR: $!"); binmode (STDOUT, ':utf8') or die ("Cannot do binmode(':utf8') on STDOUT: $!"); ############################################################################# # parser object my $c = 'Graph::Easy::Parser::Graphviz'; my $parser = Graph::Easy::Parser::Graphviz->new( debug => 0 ); is (ref($parser), $c); is ($parser->error(), '', 'no error yet'); ############################################################################# # HTML-like labels: my $graph = Graph::Easy::Parser::Graphviz->from_text(<<EOF digraph G { A [ color="dodgerblue4" shape="box" style="" label=<<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0"><TR><TD>Name</TD></TR></TABLE>> ]; B [ color="dodgerblue4" shape="box" style="" label=<<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0"><TR><TD>Name2</TD></TR><TR><TD ALIGN="LEFT" BALIGN="LEFT" PORT="E4">Somewhere<BR/>test1<BR>test</TD></TR></TABLE>> ]; A -> B } EOF ); #print $graph->as_txt(); is (ref($graph), 'Graph::Easy'); is ($graph->nodes(), 3, 'three nodes'); is ($graph->edges(), 1, 'edge did not get lost (bug until v0.60)'); ������������������������������������������������������������������������Graph-Easy-0.73/t/group.t���������������������������������������������������������������������������0000644�0000764�0000764�00000016672�11675050456�015110� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # Test Graph::Easy::Group and Graph::Easy::Group::Cell use Test::More; use strict; BEGIN { plan tests => 72; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy::Group") or die($@); use_ok ("Graph::Easy::Group::Cell") or die($@); use_ok ("Graph::Easy") or die($@); }; can_ok ("Graph::Easy::Group", qw/ new error name add_node add_nodes add_member has_as_successor has_as_predecessor successors predecessors nodes edges _add_cell _del_cell _cells _clear_cells del_node del_edge del_member /); can_ok ("Graph::Easy::Group::Cell", qw/ _set_type class /); ############################################################################# my $group = Graph::Easy::Group->new(); is (ref($group), 'Graph::Easy::Group'); is ($group->error(), '', 'no error yet'); my $graph = Graph::Easy->new(); use_ok ('Graph::Easy::As_txt'); # "insert" into a graph to get default attributes $group->{graph} = $graph; is ($group->as_txt(), "( Group \\#0 )\n\n", 'as_txt (empty group)'); is (scalar $group->nodes(), 0, 'no nodes in group'); is (scalar $group->edges(), 0, 'no edges in group'); is ($group->name(), 'Group #0', 'name()'); my $first = Graph::Easy::Node->new( name => 'first' ); my $second = Graph::Easy::Node->new( name => 'second' ); $group->add_node($first); is (scalar $group->nodes(), 1, 'one node in group'); is ($first->attribute('group'), $group->name(), 'node has group attribute set'); $group->add_nodes($first, $second); is (scalar $group->nodes(), 2, 'two nodes in group'); is ($second->attribute('group'), $group->name(), 'node has group attribute set'); is ($second->{group}, $group, 'add_nodes() worked'); is ($group->as_txt(), <<HERE ( Group \\#0 [ first ] [ second ] ) HERE , 'as_txt (group with two nodes)'); ############################################################################# # attribute nodeclass $group = Graph::Easy::Group->new(); $group->set_attributes ( { 'nodeclass' => 'city', } ); is ($first->class(),'node', 'class is "node"'); $group->add_node($first); is ($first->class(),'node.city', 'class is now "node.city"'); ############################################################################# # Group::Cells my $c = '_cells'; my $cell = Graph::Easy::Group::Cell->new( group => $group, x => 0, y => 0, ); is (scalar keys %{$group->{$c}}, 1, 'one cell'); my $cells = { '0,0' => $cell }; $cell->_set_type( $cells ); is ($cell->class(), 'group ga', 'group ga'); is ($cell->group( $group->{name} ), $group, "group()"); my $cell2 = Graph::Easy::Group::Cell->new( group => $group, x => 1, y => 0 ); is (scalar keys %{$group->{$c}}, 2, 'one more cell'); $cells->{'1,0'} = $cell2; my $cell3 = Graph::Easy::Group::Cell->new( group => $group, x => 0, y => -1 ); is (scalar keys %{$group->{$c}}, 3, 'one more cell'); $cells->{'0,-1'} = $cell3; my $cell4 = Graph::Easy::Group::Cell->new( group => $group, x => 0, y => 1 ); is (scalar keys %{$group->{$c}}, 4, 'one more cell'); $cells->{'0,1'} = $cell4; is ($cell2->group( $group->{name} ), $group, "group()"); $cell->_set_type( $cells ); is ($cell->class(), 'group gl', 'group gl'); ############################################################################# # attributes on cells # The default attributes are returned by attribute(): is ($group->attribute('border-style'), 'dashed', 'group border'); is ($group->attribute('borderstyle'), 'dashed', 'group border'); is ($cell->attribute('border'), '', 'default border on this cell'); is ($cell->attribute('border-style'), 'dashed', 'default border on this cell'); is ($group->default_attribute('border-style'), 'dashed', 'group is dashed'); is ($cell->default_attribute('border'), 'dashed 1px #000000', 'dashed border on this cell'); is ($cell->default_attribute('border-style'), 'dashed', 'dashed border on this cell'); is ($group->default_attribute('fill'), '#a0d0ff', 'fill on group'); is ($group->attribute('fill'), '#a0d0ff', 'fill on group'); is ($cell->default_attribute('fill'), '#a0d0ff', 'fill on group cell'); is ($cell->attribute('fill'), '#a0d0ff', 'fill on group cell'); ############################################################################# # del_cell(); #print join (" ", keys %{$group->{cells}}),"\n"; is (scalar keys %{$group->{$c}}, 4, 'one less'); $group->_del_cell($cell); is (scalar keys %{$group->{$c}}, 3, 'one less'); is ($cell->group(), undef, "no group() on deleted cell"); ############################################################################# # del_node() & del_edge(), when node/edge are in a group (bug until 0.39) $graph = Graph::Easy->new(); $group = $graph->add_group('group'); my ($A,$B,$E) = $graph->add_edge('A','B','E'); for my $m ($A,$B,$E) { $group->add_member($m); } is ($group->nodes(), 2, '2 nodes in group'); is ($group->edges(), 0, '0 edges going from/to group'); is ($group->edges_within(), 1, '1 edge in group'); is ($A->attribute('group'), $group->name(), 'group attribute got added'); $graph->del_node($A); is ($A->attribute('group'), '', 'group attribute got deleted'); is ($group->nodes(), 1, '1 node in group'); is ($group->edges(), 0, '0 edges in group'); ($A,$B,$E) = $graph->add_edge('A','B','E'); $group->add_member($A); $group->add_member($E); is ($group->nodes(), 2, '2 nodes in group'); is ($group->edges(), 0, '0 edges going from/to group'); is ($group->edges_within(), 1, '1 edge in group'); $graph->del_edge($E); is ($group->nodes(), 2, '2 nodes in group'); is ($group->edges(), 0, '0 edges in group'); is ($group->edges_within(), 0, '0 edges in group'); ############################################################################# # successors and predecessors $graph = Graph::Easy->new(); $group = $graph->add_group('group'); my ($g1,$bonn) = $graph->add_edge($group, 'Bonn'); my ($berlin,$g2) = $graph->add_edge('Berlin', $group); is ($group->has_as_successor($bonn), 1, 'group -> bonn'); is ($group->has_as_successor($berlin), 0, '! group -> berlin'); is ($group->has_as_predecessor($berlin), 1, 'berlin -> group'); is ($group->has_as_predecessor($bonn), 0, '! bonn -> group'); is ($bonn->has_as_successor($group), 0, '! group -> bonn'); is ($berlin->has_as_predecessor($group), 0, 'group -> berlin'); is ($bonn->has_as_predecessor($group), 1, 'bonn -> group'); my @suc = $group->successors(); is (scalar @suc, 1, 'one successor'); is ($suc[0], $bonn, 'group => bonn'); ############################################################################# # add_node('Bonn'), add_member('Bonn','Berlin') etc. $graph = Graph::Easy->new(); $group = $graph->add_group('group'); $bonn = $group->add_node('Bonn'); is (ref($bonn), 'Graph::Easy::Node', "add_node('Bonn') works for groups"); ($bonn,$berlin) = $group->add_nodes('Bonn','Berlin'); is (ref($bonn), 'Graph::Easy::Node', "add_nodes('Bonn') works for groups"); is ($bonn->name(), 'Bonn', "add_nodes('Bonn') works for groups"); is (ref($berlin), 'Graph::Easy::Node', "add_nodes('Berlin') works for groups"); is ($berlin->name(), 'Berlin', "add_nodes('Berlin') works for groups"); # add_edge() my $edge = $group->add_edge('Bonn','Kassel'); my $kassel = $graph->node('Kassel'); is (ref($kassel), 'Graph::Easy::Node', "add_edge('Bonn','Kassel') works for groups"); # add_edge_once() $edge = $group->add_edge_once('Bonn','Kassel'); my @edges = $graph->edges('Bonn','Kassel'); is (scalar @edges, 1, 'one edge from Bonn => Kassel'); # add_edge() twice $edge = $group->add_edge('Bonn','Kassel'); @edges = $graph->edges('Bonn','Kassel'); is (scalar @edges, 2, 'two edges from Bonn => Kassel'); ����������������������������������������������������������������������Graph-Easy-0.73/t/messages.t������������������������������������������������������������������������0000644�0000764�0000764�00000001714�11675050456�015552� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use Test::More; use strict; # test catching of error and warnings: BEGIN { plan tests => 10; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy") or die($@); }; can_ok ("Graph::Easy", qw/ catch_messages catch_errors catch_warnings errors warnings /); ############################################################################# # adding edges/nodes my $graph = Graph::Easy->new(); is (ref($graph), 'Graph::Easy'); is ($graph->error(), '', 'no error yet'); $graph->catch_messages(1); $graph->error('foo'); my @errors = $graph->errors(); my @warnings = $graph->warnings(); is (scalar @errors, 1, '1 error'); is (scalar @warnings, 0, '0 warnings'); is ($errors[0], 'foo', '1 error'); $graph->warn('Bar'); @errors = $graph->errors(); @warnings = $graph->warnings(); is (scalar @errors, 1, '1 error'); is (scalar @warnings, 1, '1 warning'); is ($warnings[0], 'Bar', '1 warning'); 1; # all tests done ����������������������������������������������������Graph-Easy-0.73/t/anon.t����������������������������������������������������������������������������0000644�0000764�0000764�00000005753�11675050456�014705� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # test anonymous nodes use Test::More; use strict; BEGIN { plan tests => 31; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy::Node::Anon") or die($@); use_ok ("Graph::Easy") or die($@); use_ok ("Graph::Easy::As_txt") or die($@); require_ok ("Graph::Easy::As_ascii") or die($@); }; can_ok ("Graph::Easy::Node::Anon", qw/ new as_txt as_html error class name successors predecessors width height pos x y class del_attribute set_attribute set_attributes attribute attributes_as_txt as_pure_txt group add_to_group /); ############################################################################# my $node = Graph::Easy::Node::Anon->new(); is (ref($node), 'Graph::Easy::Node::Anon'); is ($node->error(), '', 'no error yet'); is ($node->x(), undef, 'x == undef'); is ($node->y(), undef, 'y == undef'); is ($node->width(), undef, 'w == undef'); is ($node->height(), undef, 'h == undef'); is ($node->label(), ' ', 'label'); is ($node->name(), '#0', 'name'); is ($node->title(), '', 'no title per default'); is (join(",", $node->pos()), "0,0", 'pos = 0,0'); is ($node->{graph}, undef, 'no graph'); is (scalar $node->successors(), undef, 'no outgoing links'); is (scalar $node->predecessors(), undef, 'no incoming links'); is ($node->{graph}, undef, 'successors/predecssors leave graph alone'); $node->_correct_size(); is ($node->width(), 3, 'w == 3'); is ($node->height(), 3, 'h == 3'); ############################################################################# # as_txt/as_html my $graph = Graph::Easy->new(); $graph->add_node($node); is ($node->as_txt(), '[ ]', 'anon as_txt'); is ($node->as_html(), " <td colspan=4 rowspan=4 class='node_anon'></td>\n", 'as_html'); is ($node->as_ascii(), " \n \n ", 'anon as_ascii'); require Graph::Easy::As_graphviz; is ($node->as_graphviz_txt(), '"#0"', 'anon as_graphviz'); ############################################################################# # anon node as_graphviz my $grviz = $graph->as_graphviz(); my $match = quotemeta('"#0" [ color="#ffffff", label=" ", style=filled ]'); like ($grviz, qr/$match/, 'anon node'); ############################################################################# # with border attribute $node->set_attribute('border-style', 'dotted'); is ($node->as_txt(), '[ ] { border: dotted; }', 'anon as_txt'); is ($node->as_html(), " <td colspan=4 rowspan=4 class='node_anon' style=\"border: dotted 1px #000000\"></td>\n", 'as_html'); $grviz = $graph->as_graphviz(); $match = quotemeta('"#0" [ label=" ", style="filled,dotted" ]'); like ($grviz, qr/$match/, 'anon node as graphviz'); ############################################################################# # with fill attribute $node->set_attribute('fill', 'orange'); is ($node->as_txt(), '[ ] { fill: orange; border: dotted; }', 'anon as_txt'); is ($node->as_html(), " <td colspan=4 rowspan=4 class='node_anon' style=\"background: #ffa500; border: dotted 1px #000000\"></td>\n", 'as_html'); ���������������������Graph-Easy-0.73/t/edge.t����������������������������������������������������������������������������0000644�0000764�0000764�00000012426�11675050456�014651� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use Test::More; use strict; BEGIN { plan tests => 43; chdir 't' if -d 't'; use lib '../lib'; use_ok qw/Graph::Easy::Edge/; use_ok qw/Graph::Easy::Edge::Cell/; } can_ok ("Graph::Easy::Edge", qw/ new error label _cells _add_cell _clear_cells _unplace attribute undirected bidirectional has_ports flip set_attribute set_attributes group add_to_group background edge_flow flow port start_port end_port from to start_at to from nodes as_ascii as_txt /); use Graph::Easy::Edge::Cell qw/EDGE_SHORT_E/; use Graph::Easy; ############################################################################# # We need a graph to insert the edge into it, so that the edge gets the # default settings from it. # XXX TODO: should we change the above? my $graph = Graph::Easy->new(); my $edge = Graph::Easy::Edge->new(); $edge->{graph} = $graph; is (ref($edge), 'Graph::Easy::Edge'); is ($edge->error(), '', 'no error yet'); is ($edge->undirected(), undef, 'not undirected'); is ($edge->bidirectional(), undef, 'not bidiriectional'); is ($edge->has_ports(), 0, 'has no port restrictions'); use_ok ('Graph::Easy::As_txt'); is ($edge->as_txt(), ' --> ', 'default is "-->"'); ############################################################################# # different styles $edge = Graph::Easy::Edge->new( style => 'double' ); $edge->{graph} = $graph; is ($edge->as_txt(), ' ==> ', '"==>"'); $edge = Graph::Easy::Edge->new( style => 'dotted' ); $edge->{graph} = $graph; is ($edge->as_txt(), ' ..> ', '"..>"'); $edge = Graph::Easy::Edge->new( style => 'dashed' ); $edge->{graph} = $graph; is ($edge->as_txt(), ' - > ', '"- >"'); $edge = Graph::Easy::Edge->new( style => 'wave' ); $edge->{graph} = $graph; is ($edge->as_txt(), ' ~~> ', '"~~>"'); $edge = Graph::Easy::Edge->new( style => 'dot-dash' ); $edge->{graph} = $graph; is ($edge->as_txt(), ' .-> ', '".->"'); $edge = Graph::Easy::Edge->new( style => 'double-dash' ); $edge->{graph} = $graph; is ($edge->as_txt(), ' = > ', '"= >"'); $edge = Graph::Easy::Edge->new( style => 'dot-dot-dash' ); $edge->{graph} = $graph; is ($edge->as_txt(), ' ..-> ', '"= >"'); $edge = Graph::Easy::Edge->new( style => 'bold' ); $edge->{graph} = $graph; is ($edge->as_txt(), ' --> { style: bold; } ', ' --> { style: bold; }'); ############################################################################# $edge = Graph::Easy::Edge->new( label => 'train' ); $edge->{graph} = $graph; is ($edge->as_txt(), ' -- train --> ', ' -- train -->'); ############################################################################# # cells is (scalar $edge->_cells(), 0, 'no cells'); my $c = Graph::Easy::Edge::Cell->new ( edge => $edge, type => EDGE_SHORT_E, x => 1, y => 1, after => 0, ); is (scalar $edge->_cells(), 1, 'one cell'); my @cells = $edge->_cells(); is ($cells[0], $c, 'added this cell'); my $c_1 = Graph::Easy::Edge::Cell->new ( edge => $edge, type => EDGE_SHORT_E, x => 2, y => 1, after => $c, ); is (scalar $edge->_cells(), 2, 'two cells'); @cells = $edge->_cells(); is ($cells[0], $c, 'first cell stayed'); is ($cells[1], $c_1, 'added after first cell'); $edge->_clear_cells(); is (scalar $edge->_cells(), 0, 'no cells'); ############################################################################# # undirected/bidirectional is ($edge->undirected(2), 1, 'undirected'); is ($edge->undirected(), 1, 'undirected'); is ($edge->undirected(0), 0, 'not undirected'); is ($edge->bidirectional(2), 1, 'bidiriectional'); is ($edge->bidirectional(), 1, 'bidiriectional'); is ($edge->bidirectional(0), 0, 'not bidiriectional'); ############################################################################# # has_ports() $edge->set_attribute('start', 'south'); is ($edge->has_ports(), 1, 'has port restrictions'); $edge->set_attribute('end', 'north'); is ($edge->has_ports(), 1, 'has port restrictions'); $edge->del_attribute('start'); is ($edge->has_ports(), 1, 'has port restrictions'); $edge->del_attribute('end'); is ($edge->has_ports(), 0, 'has no port restrictions'); ############################################################################# # port() $edge->set_attribute('start', 'south'); my @u = $edge->port('start'); is_deeply (\@u, ['south'], "port('start')"); $edge->del_attribute('end'); $edge->del_attribute('start'); ############################################################################# # background() is ($edge->background(), 'inherit', 'background()'); $graph = Graph::Easy->new(); my ($A,$B); ($A,$B,$edge) = $graph->add_edge('A','B'); my $group = $graph->add_group('G'); $group->add_member($edge); my $cell = Graph::Easy::Edge::Cell->new( edge => $edge, graph => $graph ); # default group background is ($cell->background(), '#a0d0ff', 'background() for group member'); $group->set_attribute('background', 'red'); is ($cell->background(), '#a0d0ff', 'background() for group member'); # now has the fill of the group as background $group->set_attribute('fill', 'green'); is ($cell->background(), '#008000', 'background() for group member'); ############################################################################# # flip() my $from = $edge->from(); my $to = $edge->to(); $edge->flip(); is ($from, $edge->to(), 'from/to flipped'); is ($to, $edge->from(), 'from/to flipped'); ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/as_txt.t��������������������������������������������������������������������������0000644�0000764�0000764�00000001237�11675050456�015245� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use Test::More; use strict; BEGIN { plan tests => 4; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy") or die($@); }; can_ok ("Graph::Easy", qw/ as_txt /); ############################################################################# # as_txt use Graph::Easy::Parser; my $parser = Graph::Easy::Parser->new(); my $graph = $parser->from_text( "[A] { link: http://foo.com; color: red; origin: B; offset: 2,1; }" ); is ($parser->error(), '', 'no parsing error' ); is ($graph->as_txt(), <<EOF [ A ] { color: red; link: http://foo.com; offset: 2,1; origin: B; } [ B ] EOF , 'as_txt with offset and origin'); �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/���������������������������������������������������������������������������0000755�0000764�0000764�00000000000�12150110221�015051� 5����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0050.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000316�11675050456�016226� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# override defaults for nodes, edges, groups and graph node { color: brown; } graph { fill: #990066; border: 1px solid black; } group { fill: grey; } edge { color: white; } [ Wismar ] -> [ Neustadt ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0030.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000674�11675050456�016233� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Different edge styles [ Bonn ] <-> [ Berlin ] # bidirectional [ Berlin ] ==> [ Rostock ] # double [ Hamburg ] ..> [ Altona ] # dotted [ Dresden ] - > [ Bautzen ] # dashed [ Schweinfurt ] <=> [ Ulm ] # bidrectional, double [ Magdeburg ] .-> [ Burg ] # dot-dash [ Burg ] -- [ Wolfen ] # single, bidirectional w/o arrows [ Wolfen ] .. [ Coswig ] # dotted, bidirectional w/o arrows [ Magdeburg ] ..> [ Aue ] # dotted, going downwards ��������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0090.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000070�11675050456�016227� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# a chain of nodes [ Bonn ] -> [ Berlin ] -> [ Wismar ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0020.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000062�11675050456�016221� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ Kiel ] -> [ Kassel ] [ Kassel ] -> [ Wismar ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0240.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000770�11675050456�016233� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Clickable links and mouse-over titles: graph { linkbase: http://de.wikipedia.org/wiki/ } node { autolink: label; autotitle: label; } [ Bonn ] [ Berlin ] { title: "Berlin (Capital)" } [ Kassel ] { autolink: none; } [ Ulm ] { link: "Stuttgart"; } [ Lahn ] { label: Ulm; } # link link, title overriden only title [ Bonn ] -> [ Berlin ] -> [ Kassel ] # link overriden [ Bonn ] -> [ Ulm ] # link goes to "Ulm" (label!) [ Ulm ] -> [ Lahn ] ��������Graph-Easy-0.73/t/syntax/0011.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000055�11675050456�016223� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ Berlin\n (West) ] -> [ Berlin\n (Ost) ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0070.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000261�11675050456�016227� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# nodes in a subclass with attributes node.city { color: brown; } # override default color [ Bonn ] { class: city; } -> [ Berlin ] { class: city; } [ Berlin ] -> [ Kassel ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0080.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000204�11675050456�016225� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� node.city { color: brown; } [ Bonn ], [ Berlin ], [ Kassel ] { class: city; } [ Bonn ] -> [ Berlin ] [ Kassel ] -> [ Berlin ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0062.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000132�11675050456�016225� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Override attributes of an edge [ Bonn ] -> { color: blue; label: Autobahn; } [ Berlin ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0100.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000225�11675050456�016221� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� # node lists on the left side [ Ulm ], [ Hahn ], [ Wismar ] => [ Frankfurt ] [ Frankfurt ] -> [ Potsdam ] [ Frankfurt ], [ Lahn ] -> [ Potsdam ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0061.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000154�11675050456�016230� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Override the text of a node with the label attribute [ Bonn ] -> [ Berlin ] -> [ Bonn2 ] { label: Bonn; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0200.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000102�11675050456�016214� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# quoting of () inside node [ Berlin ] -> [ Frankfurt \(Oder\) ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0130.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000151�11675050456�016222� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# all nodes in one group ( German Cities [ Cuxhaven ] -> [ Bremen ] [ Hamburg ], [ Flensburg ] ) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0140.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000174�11675050456�016230� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# some nodes in a group, and one edge coming in ( German Cities [ Augsburg ] -> [ Hof ] ) [ Wien ] -> [ Augsburg ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0250.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000120�11675050456�016221� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# A node-cluster (autosplit into three single nodes) [ Waren | Mirow | Bergen ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0003.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000213�11675050456�016220� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# 2 nodes, one pointing back to itself [ Ruhleben ] -> [ Aschersleben ] [ Aschersleben ] -> [ Ruhleben ] [ Ruhleben ] -> [ Ruhleben ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0160.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000150�11675050456�016224� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ Terminal \#1 ] -> [ Terminal \#2 ] [ Terminal ( \#3 ) \|-\| ] [ Neustadt & Neudorf ] -> [ 3 < 4 ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0171.txt�������������������������������������������������������������������0000644�0000764�0000764�00000001157�11675050456�016236� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# edges with attributes like title/link/label # set the base link graph { linkbase: http%3a//de.wikipedia.org/wiki/; } # if nothing specified, generate a link from the label # or none if no label set edge { autolink: label; } # these edges always link to "Intercity" regardless of the label edge.intercity { link: Intercity; } # autolinks the edge to "Auto" [ Emden ] - Auto -> [ Schortens ] # autolinks this edge to "Bahn" [ Schortens ] --> { label: Bahn; } [ Oldenburg ] # no link (since no label) [ Schortens ] --> [ Cuxhaven ] # links to "Intercity" [ Oldenburg ] - IC -> { class: intercity; } [ Bremerhaven ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0150.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000117�11675050456�016226� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ( German Cities [ Augsburg ] -> [ Hof ] ) [ Wien ] -> [ German Cities ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0040.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000130�11675050456�016217� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# two unconnected parts of a graph [ Kiel ] -> [ Kassel ] [ Koblenz ] -> [ Konstanz ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0021.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000072�11675050456�016223� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� # anonymous, invisible nodes [ ] -> [ Heilbronn ] -> [] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0002.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000127�11675050456�016223� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# 2 nodes, one pointing back to itself [ Bonn ] -> [ Berlin ] [ Bonn ] -> [ Bonn ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0063.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000566�11675050456�016241� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Different node shapes node { shape: ellipse; } node.lilac { background: #deadff; } [ Bergheim ] { shape: diamond; } -> [ Kirchheim ] { shape: triangle; } -> [ Weilheim ] { shape: house; } [ Wilhelmshaven ] { shape: invisible; } -> [ Cuxhaven ] { shape: rounded; } -> [ Zeven ] { class: lilac; } [ Bremerhaven ] { shape: circle; } -> [ Templin ] { shape: triangle; } ������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0230.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000130�11675050456�016220� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Something that follows on the same line [ Ulm ] -> [ Lahn ] [ Lahn ] -> [ Trier ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0102.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000262�11675050456�016224� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� # node lists on both sides [ Ulm ], [ Hahn ], [ Wismar ] => [ Frankfurt ] [ Frankfurt ] => [ Stuttgart ], [ Trier ] [ Potsdam ], [ Cuixhaven ] => [ Bremerhaven ], [ Wismut ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0210.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000363�11675050456�016226� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# gid, "long" edge, comments and color codes graph { gid: 123; color: #308030 } node { color: #f06020 } #this is a comment node.cities { color: #1460b0; fill: lightgrey; } # this too [ Leipzig ] { class: cities } ----> [ Oschatz ] # comment �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0170.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000367�11675050456�016237� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Edges can have labels, too: [ Kempten ] -- Bahn --> [ Schongau ] # normal edge [ Schongau ] -- Fahrrad --> [ Regensburg ] # ditto [ Regensburg ] .. Auto ..> [ Roth ] # dashed [ Regensburg ] == Motorradkolonne ==> [ Straubing ] # double �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0180.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000110�11675050456�016222� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# inserted newlines [ Kempten ] -> [ Schongau ] # normal edge ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0251.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000152�11675050456�016227� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# A node-cluster (autosplit into three single nodes) [ Husum | Schleswig | Flensburg ] { fill: #ddaaff; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0120.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000111�11675050456�016215� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� # [ Neustadt \[an der Weinstrasse\] ] -> # [ Neustadt \[b. Coburg\] ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0010.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000032�11675050456�016215� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ Bonn ] -> [ Berlin ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0252.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000164�11675050456�016233� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# A node-cluster (autosplit into three single nodes) [ Amrum | Sylt | Fehmarn ] { fill: #ddaaff; } -> [ Itzehoe ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0254.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000123�11675050456�016230� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# A node-cluster (autosplit into three single nodes) [ Waren | Mirow | Bergen \| ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0110.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000114�11675050456�016217� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ Bonn ] -> [ Berlin (Capital) ] [ Bonn ] -> [ Wismar ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0060.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000116�11675050456�016225� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# override attributes of only one node [ Bonn ] { color: red; } -> [ Berlin ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0190.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000072�11675050456�016232� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# no spaces [Kempten]{color:red;}->[Thurgau]{color:blue;} ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0000.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000071�11675050456�016217� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� # This is a single-node graph with a comment: [ Bonn ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0131.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000505�11675050456�016226� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Three non-overlapping groups group.nrw { fill: #ffa070; } group.sachsen { fill: #f07070; } ( Bayern [ Augsburg ] ) { fill: #70b070; } [ Augsburg ] --> [ Dortmund ] ( Nordrhein-Westfalen [ Dortmund ] -> [ Olpe ] --> [ Hamm ] ) { class: nrw; } ( Sachsen [ Leipzig ] ) { class: sachsen } [ Hamm ] --> [ Leipzig ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0220.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000232�11675050456�016222� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# different chains of nodes [ Ulm ] -> [ Lahn ] -> [ Bonn ] -> [ Trier ] [ Stein ] -> [ Wahns ] -> [ Lind ] -> [ Burg ] -> [ Buch ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/syntax/0001.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000071�11675050456�016220� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# A node pointing back to itself [ Bonn ] -> [ Bonn ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/chain.t���������������������������������������������������������������������������0000644�0000764�0000764�00000006413�11675050456�015026� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use Test::More; use strict; BEGIN { plan tests => 44; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy::Layout::Chain") or die($@); use_ok ("Graph::Easy") or die($@); }; can_ok ("Graph::Easy::Layout::Chain", qw/ new error length nodes add_node layout /); ############################################################################# # chain tests my $c = 'Graph::Easy::Layout::Chain'; my $graph = Graph::Easy->new(); is (ref($graph), 'Graph::Easy'); is ($graph->error(), '', 'no error yet'); my ($node, $node2) = $graph->add_edge('A','B'); my $chain = Graph::Easy::Layout::Chain->new( start => $node, graph => $graph ); is (ref($chain), $c, 'new() seemed to work'); is ($chain->error(), '', 'no error'); is ($chain->start(), $node, 'start node is $node'); is ($chain->end(), $node, 'end node is $node'); is ($node->{_chain}, $chain, 'chain stored at node'); is ($chain->length(), 1, 'length() is 1'); is ($chain->length($node), 1, 'length($node) is 1'); $chain->add_node($node2); is ($node->{_chain}, $chain, 'chain stored at node'); is ($node2->{_chain}, $chain, 'chain stored at node2'); is ($chain->length(), 2, 'length() is now 2'); is ($chain->start(), $node, 'start node is $node'); is ($chain->end(), $node2, 'end node is $node2'); is ($chain->length($node), 2, 'length($node) is 2'); is ($chain->length($node2), 1, 'length($node2) is 1'); ############################################################################# # merging two chains my ($node3, $node4) = $graph->add_edge('C','D'); my $other = $c->new ( start => $node3, graph => $graph ); is (ref($other), $c, 'new() seemed to work'); is ($other->error(), '', 'no error'); is ($other->length(), 1, 'length() is 1'); is ($other->start(), $node3, 'start node is $node3'); is ($other->end(), $node3, 'end node is $node3'); $other->add_node($node4); is ($other->length(), 2, 'length() is now 2'); is ($other->start(), $node3, 'start node is $node3'); is ($other->end(), $node4, 'end node is $node4'); #diag ("merging chains\n"); $chain->merge($other); is ($other->error(), '', 'no error'); is ($other->length(), 0, 'other length() is still 0'); is ($other->start(), undef, 'start node is $node3'); is ($other->end(), undef, 'end node is $node4'); is ($chain->error(), '', 'no error'); is ($chain->length(), 4, 'chain length() is now 4'); is ($chain->start(), $node, 'start node is $node3'); is ($chain->end(), $node4, 'end node is $node4'); my @nodes = $chain->nodes(); is_deeply (\@nodes, [ $node, $node2, $node3, $node4 ], 'nodes got merged'); ############################################################################# # merging two chains, with offset my ($node5, $node6) = $graph->add_edge('E','F'); $other = $c->new ( start => $node5, graph => $graph ); $other->add_node($node6); # merge $chain into $other, but keep the first 3 nodes of $chain $other->merge($chain, $node3); is ($chain->length(), 4, 'left all four nodes'); is ($other->length(), 4, 'consumed two nodes'); @nodes = $chain->nodes(); is_deeply (\@nodes, [ $node, $node2, $node3, $node4 ], 'nodes got merged'); @nodes = $other->nodes(); is_deeply (\@nodes, [ $node5, $node6, $node3, $node4 ], 'other got two nodes'); for my $node ( @nodes ) { is ($node->{_chain}, $other, 'node got set to new chain'); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/vcg.t�����������������������������������������������������������������������������0000644�0000764�0000764�00000015243�11675050456�014524� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # Some basic VCG tests use Test::More; use strict; BEGIN { plan tests => 52; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy") or die($@); use_ok ("Graph::Easy::Parser") or die($@); }; can_ok ('Graph::Easy', qw/ as_vcg as_vcg_file /); ############################################################################# my $graph = Graph::Easy->new(); my $vcg = $graph->as_vcg(); my $vcg_file = $graph->as_vcg_file(); foreach ($vcg,$vcg_file) { s{(// Generated by Graph::Easy)[^\n]*}{$1}ms; } is ($vcg, $vcg_file, 'as_vcg and as_vcg_file are equal'); ############################################################################# # Parsing my $parser = Graph::Easy::Parser->new( debug => 0 ); $graph = $parser->from_text( <<EOG // test graph: { node: { title: "A" } node: { title: "B" } edge: { sourcename: "A" targetname: "B" } } EOG ); is (ref($graph), 'Graph::Easy', 'Parsing worked'); is (scalar $graph->nodes(), 2, 'two nodes'); my $nodes = ''; for my $n ($graph->nodes()) { $nodes .= "$n->{name}, "; } is ($nodes, "A, B, ", 'two nodes A and B'); is (scalar $graph->edges(), 1, 'one edge'); is ($graph->as_txt(), <<EOF edge { arrowstyle: filled; } graph { flow: south; } node { align: left; } [ A ] --> [ B ] EOF , 'as_txt matches'); ############################################################################# $graph = $parser->from_text( <<EOG // test graph: { edge.color: black node.textcolor: red node: { title: "A" } node.textcolor: blue node: { title: "B" } edge: { sourcename: "A" targetname: "B" } } EOG ); is (ref($graph), 'Graph::Easy', 'Parsing worked'); is (scalar $graph->nodes(), 2, 'two nodes'); $nodes = ''; for my $n ($graph->nodes()) { $nodes .= "$n->{name}, "; } is ($nodes, "A, B, ", 'two nodes A and B'); is (scalar $graph->edges(), 1, 'one edge'); is ($graph->node('A')->attribute('color'), 'red', 'textcolor red for A'); is ($graph->node('B')->attribute('color'), 'blue', 'textcolor blue for B'); ############################################################################# $graph = $parser->from_text( <<EOG graph: {title: "red vs. black" colorentry 42: 128 128 128 node: { title: "A" color: 42 } node: { title: "B" color: 1 } edge: { sourcename: "A" targetname: "B" } } EOG ); is ($parser->error(), '', 'no error'); is (ref($graph), 'Graph::Easy', 'Parsing worked'); is (scalar $graph->nodes(), 2, 'two nodes'); is ($graph->label(), 'red vs. black', 'title => label'); my $A = $graph->node('A'); is ($A->attribute('fill'), 'rgb(128,128,128)', 'A is grey'); ############################################################################# # custom attributes from parsed text # XXX TODO: this doesn't work: "energetic attraction: 0.7" $graph = $parser->from_text( <<EOG graph: {title: "red vs. black" colorentry 42: 128 128 128 ignore_singles: 1 node: { title: "A" color: 42 fontname: "serif" focus: 2 } node: { title: "B" color: 1 focus: 1 } edge: { sourcename: "A" targetname: "B" class: 1 anchor: foo } } EOG ); is ($parser->error(), '', 'no error'); is (ref($graph), 'Graph::Easy', 'Parsing worked'); is (scalar $graph->nodes(), 2, 'two nodes'); is ($graph->label(), 'red vs. black', 'title => label'); is ($graph->attribute('x-vcg-ignore_singles'), '1', 'graph has x-ignore_singles'); $A = $graph->node('A'); is ($A->attribute('font'), 'serif', 'fontname => font'); is ($A->attribute('x-vcg-focus'), '2', 'A has x-vcg-focus'); my $B = $graph->node('B'); is ($B->attribute('x-vcg-focus'), '1', 'B has x-vcg-focus'); my $E = $graph->edge('A','B'); is ($E->attribute('x-vcg-anchor'), 'foo', 'B->B has x-vcg-anchor'); ############################################################################# # attributes (custom and normal) $vcg = $graph->as_vcg(); unlike ($vcg, qr/x-/, 'no custom attributs were output'); like ($vcg, qr/fontname/, 'no custom attributs were output'); unlike ($vcg, qr/font:/, 'font => fontname'); ############################################################################# # Parsing multi-line labels and \fiXXX in strings # test that both "0x0c" and "\\f" are supported: $graph = $parser->from_text( <<EOG // test graph: { node: { title: "A" label: " i065" } node: { title: "\\fi066" label: "foo bar baz" } edge: { sourcename: "A" targetname: "B" } } EOG ); is ($parser->error(), '', 'no error'); is (ref($graph), 'Graph::Easy', 'Parsing worked'); is (scalar $graph->nodes(), 2, 'two nodes'); is ($graph->node('B')->label(), 'foo\nbar\nbaz', 'label of B'); # unquoted from \f064 to A is ($graph->node('A')->label(), 'A', 'label of A'); ############################################################################# # classname attribute $graph = $parser->from_text( <<EOG // test graph: { infoname1: "test" infoname2: "test" classname 1: "classa" classname 2: "classB" node: { title: "A" } node: { title: "B" } edge: { sourcename: "A" targetname: "B" class:1 } edge: { sourcename: "B" targetname: "A" class:2 } } EOG ); is ($parser->error(), '', 'no error'); is (ref($graph), 'Graph::Easy', 'Parsing worked'); is (scalar $graph->nodes(), 2, 'two nodes'); is (scalar $graph->edges(), 2, 'two edges'); my $e = $graph->edge('A','B'); is (ref($e), 'Graph::Easy::Edge', "got edge from A to B"); is ($e->class(), 'edge.classa', 'classname 1 worked'); $e = $graph->edge('B','A'); is (ref($e), 'Graph::Easy::Edge', "got edge from B to A"); is ($e->class(), 'edge.classb', 'classname 2 worked'); ############################################################################# # flow => orientation $graph = Graph::Easy->new('graph { flow: right; } [A]->[B]'); $graph->set_attribute('node','align','right'); $vcg = $graph->as_vcg(); like ($vcg, qr/orientation: "left_to_right"/, 'flow => orientation'); like ($vcg, qr/node.textmode: "right_justify"/, 'node align => node.textmode'); ############################################################################# # class attributes $graph = $parser->from_text( <<EOG graph: { node.color: red edge.color: green node: { title: "A" } node: { title: "B" } edge: { source: "A" target: "B" } } EOG ); $vcg = $graph->as_vcg(); like ($vcg, qr/edge.*color: "green"/, 'edge color survived'); like ($vcg, qr/node.*color: "red"/, 'node color survived'); ############################################################################# # node shapes: circle, trapeze etc. $graph = $parser->from_text( <<EOG graph: { node.shape: circle node: { title: "A" } node: { title: "B" shape: trapeze } node: { title: "C" invisible: yes } edge: { source: "A" target: "B" } } EOG ); $vcg = $graph->as_vcg(); like ($vcg, qr/node.*shape: "circle".*A/, 'A is circle'); like ($vcg, qr/node.*shape: "trapeze".*B/, 'B is trapeze'); like ($vcg, qr/node.*invisible: "yes".*C/, 'C is invisible'); �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/fb.t������������������������������������������������������������������������������0000644�0000764�0000764�00000011014�11675050456�014324� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # test printing into a framebuffer use Test::More; use strict; BEGIN { plan tests => 36; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy::Node") or die($@); use_ok ("Graph::Easy") or die($@); use_ok ("Graph::Easy::As_ascii") or die($@); }; can_ok ("Graph::Easy::Node", qw/ as_ascii _printfb _printfb_ver _draw_label _framebuffer _aligned_label /); ############################################################################# # general framebuffer tests my $node = Graph::Easy::Node->new(); is (ref($node), 'Graph::Easy::Node'); is ($node->error(), '', 'no error yet'); my $fb = $node->_framebuffer(2,3); is (join ("::", @$fb), " :: :: ", 'framebuffer set up'); $node->_printfb( $fb, 0,0, '+'); is (join ("::", @$fb), "+ :: :: ", 'print +'); $node->_printfb( $fb, 1,0, '+'); is (join ("::", @$fb), "++:: :: ", 'print +'); $node->_printfb( $fb, 1,2, '+'); is (join ("::", @$fb), "++:: :: +", 'print +'); $node->_printfb( $fb, 0,0, '--'); is (join ("::", @$fb), "--:: :: +", 'print --'); $node->_printfb( $fb, 0,1, "''"); is (join ("::", @$fb), "--::'':: +", "print ''"); ############################################################################# # multiline printing $fb = $node->_framebuffer(2,5); $node->_printfb( $fb, 0,3, "+", "+"); is (join ("::", @$fb), " :: :: ::+ ::+ ", 'print "+\n+"'); $node->_printfb( $fb, 0,2, "|", "|"); is (join ("::", @$fb), " :: ::| ::| ::+ ", 'print "\|\n\|"'); $fb = $node->_framebuffer(4,5); is (join ("::", @$fb), " :: :: :: :: ", 'new fb set up'); $node->_printfb( $fb, 1,1, "01", "234"); is (join ("::", @$fb), " :: 01 :: 234:: :: ", 'new fb set up'); ############################################################################# # _draw_border() tests $fb = $node->_framebuffer(12,6); $node->{w} = 12; $node->{h} = 6; $node->_draw_border( $fb, 'solid', 'solid', 'solid', 'solid'); is (join ("::", @$fb), '+----------+::| |::| |::| |::| |::+----------+', 'solid border'); $fb = $node->_framebuffer(8,4); $node->{w} = 8; $node->{h} = 4; my @expect = ( ' \n \n \n ', '+------+\n| |\n| |\n+------+', '........\n: :\n: :\n:......:', '+ - - -+\n\' \'\n\' \'\n+ - - -+', '+-.-.-.+\n! !\n! !\n+-.-.-.+', '+.-..-.+\n| |\n: :\n+.-..-.+', '########\n# #\n# #\n########', '#======#\nH H\nH H\n#======#', '# = = =#\n" "\n" "\n# = = =#', '+~~~~~~+\n{ {\n} }\n+~~~~~~+', ); my $i = 0; for my $style (qw/ none solid dotted dashed dot-dash dot-dot-dash bold double double-dash wave/) { $node->_draw_border( $fb, $style, $style, $style, $style); is (join ('\n', @$fb), $expect[$i], "$style border"); $i++; } ############################################################################# # _draw_border() tests with different styles $fb = $node->_framebuffer(8,4); $node->{w} = 8; $node->{h} = 4; $node->_draw_border( $fb, 'solid', 'dotted', 'solid', 'solid'); is (join ("::", @$fb), '+------+::| |::| |:::......:', 'solid border, except bottom, which is dotted'); ############################################################################# # label alignments $node->set_attribute('label', 'left\r right\l left\c center\n normal'); my ($lines,$aligns) = $node->_aligned_label(); is_deeply ( $lines, [ 'left', 'right', 'left', 'center', 'normal' ], 'lines are ok'); is_deeply ( $aligns, [ 'c', 'r', 'l', 'c', 'c', ], 'aligns is ok'); # empty lines at the are thrown away $node->set_attribute('label', 'left\r right\l left\c center\n normal\c'); ($lines,$aligns) = $node->_aligned_label(); is_deeply ( $lines, [ 'left', 'right', 'left', 'center', 'normal' ], 'lines are ok'); is_deeply ( $aligns, [ 'c', 'r', 'l', 'c', 'c', ], 'aligns is ok'); # start with alignment $node->set_attribute('label', '\rleft\r right\l left\c center\n normal\c'); ($lines,$aligns) = $node->_aligned_label(); is_deeply ( $lines, [ '', 'left', 'right', 'left', 'center', 'normal' ], 'lines are ok'); is_deeply ( $aligns, [ 'c', 'r', 'r', 'l', 'c', 'c', ], 'aligns is ok'); # start with alignment $node->set_attribute('label', '\r\l\rleft\r right\l left\c center\n normal\c'); ($lines,$aligns) = $node->_aligned_label(); is_deeply ( $lines, [ '','','','left', 'right', 'left', 'center', 'normal' ], 'lines are ok'); is_deeply ( $aligns, [ 'c','r','l','r', 'r', 'l', 'c', 'c', ], 'aligns is ok'); ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/attributes.t����������������������������������������������������������������������0000644�0000764�0000764�00000030313�11675050456�016126� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # Test the attribute system, especially getting, setting attributes # on objects and classes: use Test::More; use strict; BEGIN { plan tests => 123; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy::Attributes") or die($@); use_ok ("Graph::Easy") or die($@); }; can_ok ("Graph::Easy", qw/ color_as_hex color_name color_value _remap_attributes valid_attribute get_custom_attributes raw_attributes custom_attributes /); can_ok ("Graph::Easy::Node", qw/ get_custom_attributes raw_attributes set_attribute get_attribute custom_attributes /); can_ok ("Graph::Easy::Edge", qw/ get_custom_attributes raw_attributes set_attribute get_attribute custom_attributes /); can_ok ("Graph::Easy::Group", qw/ get_custom_attributes raw_attributes set_attribute get_attribute custom_attributes /); ############################################################################# # color_as_hex: my $att = 'Graph::Easy'; is ($att->color_as_hex( 'red' ), '#ff0000', 'color red'); is ($att->color_as_hex( '#0000ff' ), '#0000ff', 'color #0000ff'); is ($att->color_as_hex( '#f0c' ), '#ff00cc', 'color #f0c'); is ($att->color_as_hex( 'rgb(128,255,0)' ), '#80ff00', 'color rgb(128,255,0)'); is ($att->color_as_hex('lavender'), '#e6e6fa', 'color lavender'); is ($att->color_as_hex('lavenderblush'), '#fff0f5', 'color lavenderblush'); is ($att->color_as_hex('lavenderbush'), undef, 'color lavenderbush does not exist'); ############################################################################# # color_name: is ($att->color_name('red'), 'red', 'red => red'); is ($att->color_name('#ff0000'), 'red', '#ff0000 => red'); is ($att->color_name('#ffffff'), 'white', '#ffffff => white'); is ($att->color_name('#808080'), 'gray', '#808080 => gray'); ############################################################################# # color scheme support: is ($att->color_name('grey', 'x11'), 'grey', 'grey => grey'); is ($att->color_name('#c0c0c0','x11'), 'gray', '#c0c0c0 => gray'); is ($att->color_name('#ffffff','x11'), 'white', '#ffffff => white'); is ($att->color_name('grey23','x11'), 'grey23', 'grey23 => grey23'); # 1 => '#ca0020', 2 => '#f4a582', 3 => '#bababa', 4 => '#404040', is ($att->color_name('1','rdgy4'), '1', '1 => 1 under rdgy4'); ############################################################################# # color_value: is ($att->color_value('red'), '#ff0000', 'red => #ff0000'); is ($att->color_value('grey'), '#808080', 'grey => #808080'); is ($att->color_value('grey','x11'), '#c0c0c0', 'grey => #c0c0c0 under x11'); is ($att->color_value('grey23','x11'), '#3b3b3b', 'grey23 => #3b3b3b under x11'); # 1 => '#ca0020', 2 => '#f4a582', 3 => '#bababa', 4 => '#404040', is ($att->color_value('1','rdgy4'), '#ca0020', '1 => #ca0020 under rdgy4'); is ($att->color_value('4','rdgy4'), '#404040', '4 => #404040 under rdgy4'); ############################################################################# # valid_attribute: $att = Graph::Easy->new(); $att->no_fatal_errors(1); my $new_value = $att->valid_attribute( 'color', 'redbrownish' ); is ($new_value, undef, 'color redbrownish is not valid'); $new_value = $att->valid_attribute( 'fill', 'redbrownish' ); is ($new_value, undef, 'fill redbrownish is not valid'); $new_value = $att->valid_attribute( 'border-shape', 'double' ); is (ref($new_value), 'ARRAY', 'border-shape is not valied'); # no class name: 'all' will be tested for my $name ( 'red','w3c/red','x11/red', 'chocolate4', 'rgb(1,2,3)', 'rgb(10%,1%,2%)', 'rgb(8,1%,0.2)', 'w3c/grey', ) { for my $class ( undef, 'node', 'node.subclass') { my $new_value = $att->valid_attribute( 'color', $name, $class ); is ($new_value, $name, "color $name is valid"); } } ############################################################################# # fallback to color scheme 'x11' $new_value = $att->valid_attribute( 'color', 'chocolate4' ); is ($new_value, 'chocolate4', 'color chocolate4 is valid'); ############################################################################# # valid_attribute for graph only: $new_value = $att->valid_attribute( 'gid', '123', 'graph' ); is ($new_value, '123', 'gid 123 is valid for graph'); $new_value = $att->valid_attribute( 'gid', '123', 'node' ); is (ref($new_value), 'ARRAY', 'gid is invalid for nodes'); $new_value = $att->valid_attribute( 'gid', '123', 'edge' ); is (ref($new_value), 'ARRAY', 'gid is invalid for edges'); $new_value = $att->valid_attribute( 'output', 'html', 'graph' ); is ($new_value, 'html', 'output "html" is valid for graph'); $new_value = $att->valid_attribute( 'output', 'html', 'node' ); is (ref($new_value), 'ARRAY', 'output is invalid for nodes'); $new_value = $att->valid_attribute( 'output', 'html', 'edge' ); is (ref($new_value), 'ARRAY', 'output is invalid for edges'); ############################################################################# # setting attributes on graphs, nodes and edges my $graph = Graph::Easy->new(); $graph->no_fatal_errors(1); my ($n,$m,$e) = $graph->add_edge('A','B'); $n->set_attribute('color','red'); is ($graph->error(),'','no error'); $graph->error(''); # reset potential error for next test $n->set_attribute('shape','point'); is ($graph->error(),'','no error'); $graph->error(''); # reset potential error for next test $graph->set_attribute('graph', 'shape', 'point'); is ($graph->error(),"Error in attribute: 'shape' is not a valid attribute name for a graph", 'shape is not a valid attribute'); $graph->error(''); # reset potential error for next test $e->no_fatal_errors(1); $e->set_attribute('shape','point'); is ($graph->error(),"Error in attribute: 'shape' is not a valid attribute name for a edge", 'shape is not a valid attribute'); $graph->error(''); # reset potential error for next test ############################################################################# # Setting an attribute on the graph directly is the same as setting it on # the class 'graph': $graph->set_attribute('graph', 'flow', 'south'); is ($graph->attribute('flow'), 'south', 'flow was set to south'); $graph->set_attribute('flow', 'west'); is ($graph->attribute('flow'), 'west', 'flow was set to south'); is ($graph->attribute('label-pos'), 'top', 'label-pos defaults to top'); is ($graph->attribute('labelpos'), 'top', 'label-pos defaults to top'); $graph->set_attribute('graph', 'label-pos', 'bottom'); is ($graph->attribute('label-pos'), 'bottom', 'label-pos was set to bottom'); is ($graph->attribute('labelpos'), 'bottom', 'label-pos was set to bottom'); $graph->del_attribute('label-pos'); is ($graph->attribute('label-pos'), 'top', 'label-pos defaults to top'); is ($graph->attribute('labelpos'), 'top', 'label-pos defaults to top'); $graph->set_attribute('graph', 'labelpos', 'bottom'); is ($graph->attribute('label-pos'), 'bottom', 'label-pos was set to bottom'); is ($graph->attribute('labelpos'), 'bottom', 'label-pos was set to bottom'); ############################################################################# # text-style attribute for my $class (qw/edge graph node group/) { $graph->set_attribute($class, 'text-style', 'none'); is ($graph->error(), '', "could set text-style on $class"); $graph->error(''); # reset potential error for next test $graph->set_attribute($class, 'text-style', 'bold'); is ($graph->error(), '', "could set text-style on $class"); $graph->error(''); # reset potential error for next test $graph->set_attribute($class, 'text-style', 'bold underline'); is ($graph->error(), '', "could set text-style on $class"); $graph->error(''); # reset potential error for next test $graph->set_attribute($class, 'text-style', 'bold underline overline italic'); is ($graph->error(), '', "could set text-style on $class"); $graph->error(''); # reset potential error for next test } $graph->set_attribute('graph', 'text-style', 'bold underline overline italic'); my $styles = $graph->text_styles(); is (join(',', sort keys %$styles), 'bold,italic,overline,underline', 'text_styles()'); my $node = $graph->add_node('one'); $node->set_attribute('text-style', 'bold underline overline italic'); $styles = $node->text_styles(); is (join(',', sort keys %$styles), 'bold,italic,overline,underline', 'text_styles() on node'); ############################################################################# # border-style vs. borderstyle $graph = Graph::Easy->new(); $graph->no_fatal_errors(1); ($n,$m,$e) = $graph->add_edge('A','B'); is ($n->attribute('border-style'),'solid', 'border-style is solid'); is ($n->attribute('borderstyle'),'solid', 'borderstyle is solid'); $n->set_attribute('border-style','dashed'); is ($n->attribute('border-style'),'dashed', 'border-style is now dashed'); is ($n->attribute('borderstyle'),'dashed', 'border-style is now dashed'); ############################################################################# # inheritance of values ('inherit') $graph = Graph::Easy->new(); ($n,$m,$e) = $graph->add_edge('A','B'); $graph->set_attribute('node', 'color', 'red'); $graph->set_attribute('color', 'green'); $n->set_attribute('color', 'inherit'); $n->set_attribute('class', 'foo'); is ($n->attribute('class'), 'foo', 'get_attribute("class") works'); # N inherits from class "node" is ($n->attribute('color'),'red', 'inherited red from class "node"'); is ($m->attribute('color'),'red', 'inherited red from class "node"'); $graph->set_attribute('node', 'color', 'inherit'); is ($n->attribute('color'),'green', 'inherited green from graph'); is ($m->attribute('color'),'green', 'inherited green from graph'); $m->set_attribute('color', 'blue'); is ($m->attribute('color'),'blue', 'got blue'); ############################################################################# # raw_attribute() and get_raw_attributes() $graph = Graph::Easy->new(); ($n,$m,$e) = $graph->add_edge('A','B'); $graph->set_attribute('node', 'color', 'red'); $graph->set_attribute('color', 'green'); $n->set_attribute('color', 'inherit'); $n->set_attribute('class', 'foo'); $m->set_attribute('color', 'blue'); # N inherits from class "node" is ($n->raw_attribute('fill'), undef, 'attribute fill not set'); is ($n->raw_attribute('color'), 'red', 'attribute color set to inherit, so we inherit red'); is ($graph->raw_attribute('fill'), undef, 'attribute fill not set on graph'); is ($graph->raw_attribute('color'), 'green', 'attribute color set to green on graph'); is ($m->raw_attribute('color'), 'blue', 'attribute color set to blue on node B'); is ($m->raw_attribute('fill'), undef, 'attribute fill not set on node m'); my $str = _att_to_str($n->raw_attributes()); is ($str, 'color=>red;', 'node A has only color set'); $str = _att_to_str($m->raw_attributes()); is ($str, 'color=>blue;', 'node B has only color set'); $str = _att_to_str($graph->raw_attributes()); is ($str, 'color=>green;', 'graph has only color set'); $str = _att_to_str($e->raw_attributes()); is ($str, '', 'edge has no attributes set'); ############################################################################# # virtual attribute 'class' $graph = Graph::Easy->new(); ($n,$m,$e) = $graph->add_edge('Bonn','Berlin'); is ($graph->attribute('class'), '', 'class graph'); is ($n->attribute('class'), '', 'class node'); is ($e->attribute('class'), '', 'class edge'); $n->set_attribute('class', 'anon'); is ($n->attribute('class'), 'anon', 'class anon for node Bonn'); $e->set_attribute('class', 'foo'); is ($e->attribute('class'), 'foo', 'class foo for edge'); ############################################################################# # attribute 'link' $graph = Graph::Easy->new(); ($n,$m,$e) = $graph->add_edge('Bonn','Berlin'); $n->set_attribute('autolink','name'); # default linkbase + autolink from name is ($n->link(), '/wiki/index.php/Bonn', "link() for 'Bonn'"); is ($graph->link(), '', "no link on graph"); $graph->set_attribute('autolink','name'); # graph doesn't have a name => no link is ($graph->link(), '', "link() is 'Bonn'"); $graph->set_attribute('link','Berlin'); # default linkbase + link is ($graph->link(), '/wiki/index.php/Berlin', "link() for graph"); 1; ############################################################################# sub _att_to_str { my $out = shift; my $str = ''; for my $k (sort keys %$out) { $str .= $k . '=>' . $out->{$k} . ';'; } $str; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/������������������������������������������������������������������������������0000755�0000764�0000764�00000000000�12150110221�014342� 5����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_selfloop_flip.txt�����������������������������������������������������������0000644�0000764�0000764�00000000130�12150106410�020161� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Adenau ] --> [ Monschau ] [ Nideggen ] --> [ Monschau ] [ Monschau ] --> [ Monschau ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_bug_basename.txt������������������������������������������������������������0000644�0000764�0000764�00000000132�11675050456�017762� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ a | b | c ] { basename: pv; } [ u ] --> [ pv.0 ] [ u ] --> [ pv.1 ] [ u ] --> [ pv.2 ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_nodes_5_edges.txt�����������������������������������������������������������0000644�0000764�0000764�00000000250�12150106410�020032� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Default Page ] --> [ Sign Up ] [ Default Page ] .-> [ Main Page ] [ Default Page ] = > [ Main Page ] [ Default Page ] - > [ Main Page ] [ Sign Up ] --> [ Main Page ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_cluster.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000074�11675050456�017037� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Husum | Schleswig | Flensburg ] { background: #ddaaff; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_label_align.txt�������������������������������������������������������������0000644�0000764�0000764�00000000117�12150106410�017561� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Long Node Label\l left\r right\c center ] -- A\r long\n edge label --> [ B ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_selfloop_flow_down.txt������������������������������������������������������0000644�0000764�0000764�00000000152�12150106410�021231� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { flow: 180; } [ Start ] --> [ Main ] [ Main ] -- Until not done --> [ Main ] [ Main ] --> [ End ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_att.txt���������������������������������������������������������������������0000644�0000764�0000764�00000000211�12150106410�016115� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������node { title: 100%; } [ 1 ] { label: $a = %229%22%3b\nmy $b = 1%3b; } [ 3 ] { label: $a = %22%3b%25; } [ 1 ] --> [ 2 ] [ 3 ] --> [ 4 ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_flow_chain.txt��������������������������������������������������������������0000644�0000764�0000764�00000000134�12150106410�017442� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bonn ] --> [ Cottbus ] [ Bonn ] --> { flow: forward; } [ Berlin ] [ Cottbus ] --> [ Ulm ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/8_optimize_bend.txt�����������������������������������������������������������0000644�0000764�0000764�00000000635�12150106411�020174� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������node { shape: circle; } [ compare ] { background: #c1b2ff; shape: rect; } [ main ] { shape: rect; } [ make_string ] { label: make a\n string; } [ main ] ..> [ init ] [ main ] --> [ parse ] [ main ] -- Testlabel --> [ cleanup ] [ main ] - 100 times - > { color: red; } [ printf ] [ init ] --> [ make_string ] [ parse ] --> [ execute ] [ execute ] --> { color: red; } [ compare ] [ execute ] --> [ make_string ] ���������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/6_chained.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000253�12150106411�016731� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bonn ] --> [ Ulm ] [ Bonn ] --> [ Berlin ] [ Bonn ] --> [ Koblenz ] [ Berlin ] --> [ Kassel ] [ Ulm ] --> [ Bautzen ] [ Ulm ] --> [ Koblenz ] [ Bautzen ] --> [ Berlin ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_comma.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000060�12150106410�016423� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ 1 ] --> [ 4 ] [ 2 ] --> [ 4 ] [ 3 ] --> [ 4 ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_collapse.txt����������������������������������������������������������������0000644�0000764�0000764�00000000217�12150106410�017135� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������node.i { label: S P A C Y; } [ A ] { label: S P A C E; } [ B ] { class: i; } [ A ] -- S P A C E --> [ B ] [ A B C ] -- A B C D --> [ X Z Y ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/9_flow_south.txt��������������������������������������������������������������0000644�0000764�0000764�00000000266�12150106411�017536� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { start: south; } graph { flow: down; } [ A ] --> [ A1 ] [ A ] --> [ A2 ] [ A ] --> [ A3 ] [ A ] --> [ A4 ] [ A ] --> [ A5 ] [ A ] --> [ A6 ] [ A ] --> [ A7 ] [ A ] --> [ A8 ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_nodes_edge.txt��������������������������������������������������������������0000644�0000764�0000764�00000000123�12150106410�017423� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ test ] { shape: edge; } [ A ] -- [ test ] [ C ] --> [ test ] [ test ] --> [ B ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_wrap.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000325�11675050456�016325� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { textwrap: 6; } [ Small city near a beautyfull river ] { align: right; textwrap: 10; } [ Frankfurt Oder\n Flughafen-Terminal ] -- Drive a car to the destination --> [ Small city near a beautyfull river ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/��������������������������������������������������������������������������0000755�0000764�0000764�00000000000�12150110221�015130� 5����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/2_square_bracket_in_attr.txt����������������������������������������������0000644�0000764�0000764�00000000176�12150107334�022644� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: G; } [ a ] { label: ] ; } [ a ] --> [ b ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/4_loose.txt���������������������������������������������������������������0000644�0000764�0000764�00000000255�11675050456�017267� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: unnamed; type: undirected; } [ A ] -- [ B ] [ B ] -- [ C ] [ C ] -- [ D ] [ D ] -- [ A ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/4_html_like.txt�����������������������������������������������������������0000644�0000764�0000764�00000000272�11675050456�020115� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: G; } [ first ] { label: ; } [ fourth ] { label: a; } [ second ] { label: ; } [ third ] { label: ; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/2_ignore.txt��������������������������������������������������������������0000644�0000764�0000764�00000000157�12150107334�017413� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: G; } [ node1 ] --> [ node2 ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/5_scopes_uni.txt����������������������������������������������������������0000644�0000764�0000764�00000000267�12150107334�020304� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: GRAPH_0; } [ a ] -- [ b ] [ a ] -- [ c ] [ a ] -- [ d ] [ b ] -- [ o ] [ c ] -- [ o ] [ d ] -- [ o ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/2_nospace.txt�������������������������������������������������������������0000644�0000764�0000764�00000000163�12150107334�017555� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: mike; } [ mike ] --> [ michael ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/6_group_align.txt���������������������������������������������������������0000644�0000764�0000764�00000000272�11675050456�020455� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( Center: [ E ] [ F ] ) { align: center; } ( Left: [ A ] [ B ] ) { align: left; } ( Right: [ C ] [ D ] ) { align: right; } [ A ] --> [ B ] [ C ] --> [ D ] [ E ] --> [ F ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/3_graph_label_long.txt����������������������������������������������������0000644�0000764�0000764�00000000305�12150107334�021403� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; label: Deutsche Städte am Rhein; labelpos: bottom; title: GRAPH_0; } [ Köln ] --> [ Bonn ] [ Bonn ] --> [ Koblenz ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/4_uppercase.txt�����������������������������������������������������������0000644�0000764�0000764�00000000532�11675050456�020133� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: graph; } group { align: center; fill: inherit; } [ a ] { border: dotted black; } [ b ] { border: dotted black; } [ c ] { border: dotted black; } [ d ] { border: dotted black; } ( cluster_me [ a ] [ b ] ) [ a ] -- foo --> [ b ] [ c ] -- foo --> [ d ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/7_record.txt��������������������������������������������������������������0000644�0000764�0000764�00000000345�11675050456�017427� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { align: left; colorscheme: x11; flow: south; label: Left aligned label; title: gaph; } [ <f1> Aa \| <f2> Bb \| Cc ] { shape: rect; } [ AA | BB | CC ] { basename: a; } [ A | B | C ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/2_setlinewidth.txt��������������������������������������������������������0000644�0000764�0000764�00000000274�11675050456�020650� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: unnamed; } [ Berlin ] { shape: rect; border: broad; } [ Bonn ] { border: bold; } [ Bonn ] --> [ Berlin ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/3_ids.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000164�12150107334�016706� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: G; } [ 123 ] [ abc ] --> [ test ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/0_empty.txt���������������������������������������������������������������0000644�0000764�0000764�00000000135�11675050456�017275� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: unnamed; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/9_stacking.txt������������������������������������������������������������0000644�0000764�0000764�00000000352�12150107335�017740� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: east; title: G; } [ 0 ] --> [ 11 ] [ 11 ] --> [ 1 ] [ 1 ] --> [ 10 ] [ 1 ] --> [ 2 ] [ 10 ] --> [ 6 ] [ 2 ] --> [ 3 ] [ 3 ] --> [ 8 ] [ 8 ] --> [ 5 ] [ 5 ] --> [ 6 ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/10_numbers.txt������������������������������������������������������������0000644�0000764�0000764�00000000313�12150107335�017655� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: G; } [ -100 ] --> [ 123.1230 ] [ .1 ] --> [ -1. ] [ .99 ] --> [ 0. ] [ 0.99 ] --> [ 12.88 ] [ 00019.1001 ] --> [ 1000. ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/9_back.txt����������������������������������������������������������������0000644�0000764�0000764�00000000352�12150107334�017034� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: east; title: G; } [ 0 ] --> [ 11 ] [ 5 ] --> [ 6 ] [ 5 ] --> [ 8 ] [ 11 ] --> [ 1 ] [ 1 ] --> [ 10 ] [ 1 ] --> [ 2 ] [ 10 ] --> [ 6 ] [ 2 ] --> [ 3 ] [ 3 ] --> [ 8 ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/3_colors.txt��������������������������������������������������������������0000644�0000764�0000764�00000000535�11675050456�017447� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: G; } group { align: center; fill: inherit; } [ A ] { color: red; } [ B ] { color: #ffff99; } [ C ] { color: #ff0000; } ( cluster_1 [ A ] [ B ] ) { border: dashed #ff0000; } [ A ] --> { color: #ff0000ff; } [ B ] [ B ] --> { color: hsv(1.0,1.0,1.0); } [ C ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/3_empty_record.txt��������������������������������������������������������0000644�0000764�0000764�00000000216�11675050456�020636� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: G; } [ Compositor| |Compose() ] { basename: Compositor; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/2_ports.txt���������������������������������������������������������������0000644�0000764�0000764�00000000217�12150107334�017274� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: ports; } [ north ] --> { end: west; start: east; } [ south ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/2_no_spaces.txt�����������������������������������������������������������0000644�0000764�0000764�00000000210�12150107334�020070� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: unnamed; type: undirected; } [ bonn ] -- [ berlin ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/2_bool.txt����������������������������������������������������������������0000644�0000764�0000764�00000000157�12150107334�017063� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: G; } [ Bonn ] --> [ Berlin ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/5_scopes.txt��������������������������������������������������������������0000644�0000764�0000764�00000000275�12150107334�017430� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: GRAPH_0; } [ a ] --> [ b ] [ a ] --> [ c ] [ a ] --> [ d ] [ b ] --> [ u ] [ c ] --> [ u ] [ d ] --> [ u ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/2_group_labelloc.txt������������������������������������������������������0000644�0000764�0000764�00000000447�11675050456�021140� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: GRAPH_0; } group { align: center; fill: inherit; } ( cluster0 [ Wasserkuppe ] [ Zugspitze ] ) { align: left; label: Bergtour:; labelpos: bottom; border: dashed black; } [ Zugspitze ] --> [ Wasserkuppe ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/9_tree.txt����������������������������������������������������������������0000644�0000764�0000764�00000000406�11675050456�017110� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: G; } [ | A| ] { basename: node0; } [ | B| ] { basename: node1; } [ | C| ] { basename: node2; } [ node0.0 ] --> [ node1.1 ] [ node0.2 ] --> [ node2.2 ] [ node1.0 ] --> [ node2.1 ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/5_scope_atr.txt�����������������������������������������������������������0000644�0000764�0000764�00000000334�12150107334�020107� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: GRAPH_0; } [ b ] { shape: rounded; } [ c ] { shape: none; } [ d ] { shape: none; } [ a ] [ b ] --> [ u ] [ c ] --> [ u ] [ d ] --> [ u ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/6_comments.txt������������������������������������������������������������0000644�0000764�0000764�00000000347�12150107334�017762� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: GRAPH_0; } [ a ] --> [ b ] [ a ] --> [ c ] [ a ] --> [ d ] [ my node /* not a comment */ ] --> [ b ] [ b ] --> [ u ] [ c ] --> [ u ] [ d ] --> [ u ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/2_comment_inside_attr.txt�������������������������������������������������0000644�0000764�0000764�00000000174�12150107334�022156� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: G; } [ a ] { label: a; } [ a ] --> [ b ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/3_output_lone.txt���������������������������������������������������������0000644�0000764�0000764�00000000175�12150107334�020506� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; output: html; title: G; } [ a ] --> [ b ] [ c ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/5_scopes_chain.txt��������������������������������������������������������0000644�0000764�0000764�00000000275�12150107334�020572� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: GRAPH_0; } [ a ] --> [ b ] [ a ] --> [ c ] [ a ] --> [ d ] [ b ] --> [ o ] [ c ] --> [ o ] [ d ] --> [ o ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/3_empty_record_LR.txt�����������������������������������������������������0000644�0000764�0000764�00000000220�11675050456�021226� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: east; title: G; } [ Compositor|| || Compose() ] { basename: Compositor; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/16_split.txt��������������������������������������������������������������0000644�0000764�0000764�00000000464�11675050456�017366� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: SPLIT; } [ nil| | | 0 ] { basename: 0; } [ nil| | | 1 ] { basename: 1; } [ nil| | | 2 ] { basename: 2; } [ nil| | | 3 ] { basename: 3; } [ 0.0 ] --> [ 1.3 ] [ 0.3 ] --> [ 1.0 ] [ 2.0 ] --> [ 3.3 ] [ 2.3 ] --> [ 3.0 ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/4_record.txt��������������������������������������������������������������0000644�0000764�0000764�00000000301�11675050456�017414� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: G; } [ test | split ] { basename: node1; } [ record | test ] { basename: node2; } [ node1.0 ] --> [ node2.1 ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/4_strings.txt�������������������������������������������������������������0000644�0000764�0000764�00000000456�11675050456�017642� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: graph0; } [ Cottbus ] { label: Cottbus; } [ berlin ] { label: Berlin Ost; } [ bonn ] { label: Bonn am Rhein; } [ bonntest ] { label: Bonn (Rhei n); } [ bonn ] -- train (ICE) --> [ berlin ] [ bonntest ] --> [ Cottbus ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/4_cluster_labeljust.txt���������������������������������������������������0000644�0000764�0000764�00000000557�11675050456�021701� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: G; } group { align: center; fill: inherit; } ( cluster_0 [ aaaaaaaaaaaaaaaaaaa ] [ b ] ) { align: right; label: Right aligned; } ( cluster_1 [ c ] [ ddddddddddddddd ] ) { align: left; label: Left aligned; } [ aaaaaaaaaaaaaaaaaaa ] --> [ b ] [ c ] --> [ ddddddddddddddd ] �������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/2_linewidth.txt�����������������������������������������������������������0000644�0000764�0000764�00000000401�11675050456�020124� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: G; x-dot-overlap: false; x-dot-ratio: fill; x-dot-size: 11,8.5; } [ 123 ] { fontsize: 10px; shape: none; } [ foo ] { fontsize: 10px; shape: none; } [ 123 ] --> [ foo ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/2_strict.txt��������������������������������������������������������������0000644�0000764�0000764�00000000166�12150107334�017440� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: G; } [ Wolfsbüttel ] --> [ Köln ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/2_graph_label_bottom.txt��������������������������������������������������0000644�0000764�0000764�00000000250�12150107334�021746� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; label: Deutsche Städte; labelpos: bottom; title: GRAPH_0; } [ Köln ] --> [ Stralsund ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/3_invis.txt���������������������������������������������������������������0000644�0000764�0000764�00000000302�11675050456�017266� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: G; } [ a ] { shape: rect; } [ b ] { shape: invisible; } [ c ] { shape: rect; } [ a ] --> [ b ] [ b ] --> [ c ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/9_edge_styles.txt���������������������������������������������������������0000644�0000764�0000764�00000002174�12150107335�020450� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: G; x-dot-clusterrank: local; x-dot-margin: 0; x-dot-nodesep: 0.2; x-dot-ranksep: 0.2; x-dot-ratio: auto; } [ A ] { color: lightsalmon; font: Helvetica; fontsize: 14px; shape: rect; } [ B ] { color: lightsalmon; font: Helvetica; fontsize: 14px; shape: rect; } [ C ] { color: lightsalmon; font: Helvetica; fontsize: 14px; shape: rect; } [ D ] { color: lightsalmon; font: Helvetica; fontsize: 14px; shape: rect; } [ E ] { color: lightsalmon; font: Helvetica; fontsize: 14px; shape: rect; } [ F ] { color: lightsalmon; font: Helvetica; fontsize: 14px; shape: rect; } [ G ] { color: lightsalmon; font: Helvetica; fontsize: 14px; shape: rect; } [ H ] { color: lightsalmon; font: Helvetica; fontsize: 14px; shape: rect; } [ I ] { color: lightsalmon; font: Helvetica; fontsize: 14px; shape: rect; } [ A ] --> { style: bold; } [ B ] [ A ] --> { style: bold; } [ F ] [ B ] --> { style: bold; } [ C ] [ F ] --> { style: bold; } [ G ] [ C ] --> { style: bold; } [ D ] [ G ] --> { style: bold; } [ H ] [ D ] --> { style: bold; } [ E ] [ H ] --> { style: bold; } [ I ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/4_compass.txt�������������������������������������������������������������0000644�0000764�0000764�00000000375�12150106110�017570� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: unnamed; } [ berlin N | w ] { basename: berlin; } [ bonn N | S ] { basename: bonn; } [ bonn.1 ] --> { end: west; start: south; } [ berlin.0 ] [ bonn.1 ] --> [ berlin.1 ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/6_2_cluster.txt�����������������������������������������������������������0000644�0000764�0000764�00000000600�12150107334�020027� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; title: G; } group { align: center; fill: inherit; } ( cluster_first [ a ] [ b ] ) { label: first; } ( cluster_second [ p ] [ q ] [ r ] [ s ] ) { label: second; } [ a ] --> [ b ] [ b ] --> [ q ] [ q ] --> [ r ] [ r ] --> { style: bold; } [ a ] [ r ] --> [ s ] [ s ] --> [ p ] [ p ] --> [ q ] ��������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/dot/3_node_label.txt����������������������������������������������������������0000644�0000764�0000764�00000000332�11675050456�020225� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { colorscheme: x11; flow: south; label: \G; title: Graphname; } [ 0 ] { label: \G \N; } [ 1 ] { label: \G \N; } [ 2 ] { label: Name: \N \G; } [ 0 ] -- \E \G \T \H --> [ 1 ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/8_invisible.txt���������������������������������������������������������������0000644�0000764�0000764�00000001061�12150106411�017322� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { color: blue; labelcolor: green; style: invisible; } graph { background: oldlace; label: My sample graph; border: solid black; } [ Five ] { color: yellow; fill: maroon; } [ One ] { color: white; fill: seagreen; } [ Two ] { shape: triangle; } [ Five ] - Test label - > { color: red; labelcolor: darkslategrey; } [ Seven ] [ Five ] --> [ Seven ] [ Five ] <==> [ Three ] [ Five ] --> [ Eight ] [ One ] ==> [ Three ] [ One ] .. Test\n label ..> [ Four ] [ One ] -- label --> [ Two ] [ Seven ] -- [ Eight ] [ Three ] <.. Test label ..> [ Six ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_group_labelpos.txt����������������������������������������������������������0000644�0000764�0000764�00000000151�11675050456�020366� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( Bergtour: [ Wasserkuppe ] [ Zugspitze ] ) { labelpos: bottom; } [ Zugspitze ] --> [ Wasserkuppe ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_endless_loop_2.txt����������������������������������������������������������0000644�0000764�0000764�00000000071�12150106410�020240� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ 4 ] { offset: 1,0; origin: 3; } [ 1 ] --> [ 2 ] [ 3 ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/6_multicell_offset.txt��������������������������������������������������������0000644�0000764�0000764�00000000423�11675050456�020717� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ B ] { offset: 2,0; origin: Wide; } [ C ] { offset: -2,0; origin: Wide; } [ D ] { offset: 0,2; origin: High; } [ E ] { offset: 0,-2; origin: High; } [ High ] { size: 1,4; } [ Wide ] { size: 4,1; } [ High ] --> [ D ] [ High ] --> [ E ] [ Wide ] --> [ B ] [ Wide ] --> [ C ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/6_group_align.txt�������������������������������������������������������������0000644�0000764�0000764�00000000272�11675050456�017667� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( Center: [ E ] [ F ] ) { align: center; } ( Left: [ A ] [ B ] ) { align: left; } ( Right: [ C ] [ D ] ) { align: right; } [ A ] --> [ B ] [ C ] --> [ D ] [ E ] --> [ F ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/25_autosplit_empty.txt��������������������������������������������������������0000644�0000764�0000764�00000000343�12150106410�020660� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ | D | ] { class: empty; } [ | F | ] { class: empty; } [ |G| ] { class: empty; } [ | C | ] [ C.2 ] --> [ A1 ] [ D.2 ] --> [ A2 ] [ | E | ] [ E.2 ] --> [ A3 ] [ F.2 ] --> [ A4 ] [ G.2 ] --> [ A5 ] [ |H| | ] [ H.3 ] --> [ A6 ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/8_align.txt�������������������������������������������������������������������0000644�0000764�0000764�00000001314�12150106411�016431� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { align: right; } graph { fontsize: 2em; label: My Graph; } node.left { align: left; fontsize: 1.5em; } [ Left\n align\n no border ] { align: left; border: none; } [ Left\naligned ] { class: left; } [ Right\nAligned ] { align: right; } [ Rightmost ] { align: right; } [ center\n align\n no border ] { align: center; border: none; } ( Nodes: [ Left\naligned ] [ Right\nAligned ] ) { align: right; } [ B ] --> [ Left\n align\n no border ] [ Center\n aligned text ] -- aligned\n left --> { align: left; } [ Rightmost ] [ Right\nAligned ] -- label\n text --> { align: right; } [ Left\naligned ] [ Left\n align\n no border ] --> [ center\n align\n no border ] [ center\n align\n no border ] --> [ A ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_2x2nodes.txt����������������������������������������������������������������0000644�0000764�0000764�00000000065�12150106410�017000� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Frankfurt ] --> [ Hof ] [ Hamburg ] --> [ Altona ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_minlen.txt������������������������������������������������������������������0000644�0000764�0000764�00000000176�12150106410�016621� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Berlin ] --> { minlen: 4; } [ Cottbus ] [ Berlin ] --> { minlen: 4; } [ Leipzig ] [ Berlin ] --> { minlen: 4; } [ Potsdam ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_invisible.txt���������������������������������������������������������������0000644�0000764�0000764�00000000365�12150106410�017323� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Borna ] { pointstyle: invisible; shape: point; } [ You don't see me! ] { shape: invisible; } [ Bischofswerda ] --> [ You don't see me! ] [ You don't see me! ] --> [ Bischofswerda ] [ You don't see me! ] --> [ Borna ] [ Borna ] --> [ Bremen ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_autolabel.txt���������������������������������������������������������������0000644�0000764�0000764�00000000270�12150106410�017300� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { autolabel: name, 20; autotitle: name; } [ Bonn ] { label: Bonn (ehemalige Bundeshauptstadt); } [ Bonn ] -- Acme Travels Incorporated --> [ Frankfurt (Main) / Flughafen ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_joint_short.txt�������������������������������������������������������������0000644�0000764�0000764�00000000250�11675050456�017714� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ B ] { offset: 2,0; origin: A; } [ C ] { offset: 2,-2; origin: B; } [ A ] --> { end: south, 0; start: south; } [ C ] [ B ] --> { end: south, 0; start: south; } [ C ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_flow.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000212�12150106410�016275� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Passau ] --> { flow: down; } [ Siegen ] [ Siegen ] --> { flow: left; } [ Aschaffenburg ] [ Siegen ] --> { flow: right; } [ Regensburg ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_lists.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000057�12150106410�016472� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bonn ] --> [ Berlin ] [ Ulm ] --> [ Berlin ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_list_attr.txt���������������������������������������������������������������0000644�0000764�0000764�00000000075�11675050456�017363� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Berlin ] { border: dashed; } [ Bonn ] { border: dashed; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/5_tree_joint.txt��������������������������������������������������������������0000644�0000764�0000764�00000000445�11675050456�017524� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A1 ] { offset: 2,1; origin: A; } [ A2 ] { offset: 2,2; origin: A; } [ A3 ] { offset: 2,3; origin: A; } [ A4 ] { offset: 2,4; origin: A; } [ A ] --> { start: south, 0; } [ A1 ] [ A ] --> { start: south, 0; } [ A2 ] [ A ] --> { start: south, 0; } [ A3 ] [ A ] --> { start: south, 0; } [ A4 ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_edge_cross.txt��������������������������������������������������������������0000644�0000764�0000764�00000000154�12150106410�017450� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ E1 ] --> [ E2 ] [ E1 ] --> [ S1 ] [ E1 ] --> [ S2 ] [ E2 ] --> [ S1 ] [ E2 ] --> [ S2 ] [ S1 ] --> [ S2 ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3nodes.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000052�12150106410�016300� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ One ] ==> [ Two ] [ Two ] - > [ Three ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_newlines.txt����������������������������������������������������������������0000644�0000764�0000764�00000000123�11675050456�017174� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Berlin\n (O) ] { border: dotted black; } [ Berlin\n (W) ] --> [ Berlin\n (O) ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_autosplit_escaped.txt�������������������������������������������������������0000644�0000764�0000764�00000000017�11675050456�021062� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A \| B | C ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_invisible_left.txt����������������������������������������������������������0000644�0000764�0000764�00000000023�12150106410�020322� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ ] --> [ Berlin ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_list_attr.txt���������������������������������������������������������������0000644�0000764�0000764�00000000251�11675050456�017360� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Berlin ] { border: dashed; } [ Bonn ] { border: dashed; } [ Frankfurt ] { border: dot-dash; } [ Berlin ] -- test --> [ Frankfurt ] [ Bonn ] -- test --> [ Frankfurt ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/5_rounded.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000454�12150106411�017000� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������node { shape: rounded; } [ Pankow\n (Berlin) ] { border: double; } [ Parsing ] { border: dashed; } [ Pullach ] { border: dotted; } [ Pullheim ] { border: bold; } [ Pullach ] --> [ Parsing ] [ Parsing ] --> [ Pankow\n (Berlin) ] [ Pankow\n (Berlin) ] --> [ Pullheim ] [ Pullheim ] --> [ Paderborn ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/8_flow.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000470�12150106411�016310� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ South ] { flow: south; } [ East ] --> [ South ] [ South ] --> [ West ] [ West ] --> { flow: west; } [ South Too ] [ South Too ] --> { flow: down; } [ right (west) ] [ right (west) ] --> { flow: right; } [ left (south) ] [ left (south) ] --> { flow: left; } [ east (default) ] [ east (default) ] --> [ final ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_edge_types.txt��������������������������������������������������������������0000644�0000764�0000764�00000000116�12150106410�017461� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bingen ] <--> [ Mainz ] [ Bischofswerde ] -- test-text --> [ Finsterwalde ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/10_repair.txt�����������������������������������������������������������������0000644�0000764�0000764�00000001125�12150106410�016671� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( DMZ1: [ 1Backend ] [ 1Database ] [ 1Proxy ] [ 1Server ] ) ( DMZ: [ Backend ] [ Database ] [ Proxy ] [ Server ] ) [ 1Proxy ] --> [ 1Check ] [ 1Proxy ] --> [ 1Check ] [ 1Proxy ] --> { flow: south; } [ 1Database ] [ 1Proxy ] --> { flow: south; } [ 1Database ] [ 1Proxy ] --> [ 1Server ] [ 1Proxy ] --> [ 1Server ] [ Proxy ] --> [ Check ] [ Proxy ] --> { flow: south; } [ Database ] [ Proxy ] --> [ Server ] [ 1Check ] --> [ 1Backend ] [ 1Check ] --> [ 1Backend ] [ 1Database ] --> [ 1Backend ] [ 1Database ] --> [ 1Backend ] [ Check ] --> [ Backend ] [ Database ] --> [ Backend ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_joining.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000220�12150106410�016761� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A ] --> { start: south,0; } [ B ] [ A ] --> { start: north,0; } [ B ] [ A ] --> { start: south,0; } [ C ] [ A ] --> { start: north,0; } [ C ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_label.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000113�11675050456�016426� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������node { label: A; } [ C ] { link: http://bloodgate.com; } [ B ] --> [ C ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_selfloop_flow_down.txt������������������������������������������������������0000644�0000764�0000764�00000000171�12150106410�021231� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { flow: 180; } [ Start ] --> [ Main ] [ Main ] -- Until not done --> [ Main ] [ Main ] -- Until done --> [ Main ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/0_empty_group.txt�������������������������������������������������������������0000644�0000764�0000764�00000000032�11675050456�017717� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( Group without a name ) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/8_endless_loop.txt������������������������������������������������������������0000644�0000764�0000764�00000000102�12150106411�020017� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ 1 ] --> [ 2 ] [ 1 ] --> [ 3 ] [ 3|4 ] [ 5|6|7 ] [ 2 ] --> [ 3 ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/18_multiples.txt��������������������������������������������������������������0000644�0000764�0000764�00000001073�12150106410�017437� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������node { background: yellow; } [ 1Bonn ] --> [ 1Ulm ] [ 1Bonn ] ..> [ 1Berlin ] [ 1Bonn ] .-> [ 1Koblenz ] [ 2Bonn ] --> [ 2Ulm ] [ 2Bonn ] ..> [ 2Berlin ] [ 2Bonn ] .-> [ 2Koblenz ] [ Bonn ] --> [ Ulm ] [ Bonn ] ..> [ Berlin ] [ Bonn ] .-> [ Koblenz ] [ 1Berlin ] --> [ 1Kassel ] [ 1Ulm ] --> [ 1Bautzen ] [ 1Ulm ] --> [ 1Koblenz ] [ 2Berlin ] --> [ 2Kassel ] [ 2Ulm ] --> [ 2Bautzen ] [ 2Ulm ] --> [ 2Koblenz ] [ Berlin ] --> [ Kassel ] [ Ulm ] --> [ Bautzen ] [ Ulm ] --> [ Koblenz ] [ 1Bautzen ] --> [ 1Berlin ] [ 2Bautzen ] --> [ 2Berlin ] [ Bautzen ] --> [ Berlin ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/5_group_split.txt�������������������������������������������������������������0000644�0000764�0000764�00000000353�12150106411�017705� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Frankfurt ] { flow: down; } [ Ulm ] { offset: 6,2; origin: Bonn; } ( test: [ Berlin ] [ Bonn ] [ Frankfurt ] [ Hagen ] ) [ Bonn ] --> [ Berlin ] [ Bonn ] --> [ Ulm ] [ Berlin ] --> [ Frankfurt ] [ Frankfurt ] --> [ Hagen ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/10borders.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000656�11675050456�016743� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bold ] { border: bold; } [ Dashed ] { border: dashed; } [ Dotted ] { border: dotted; } [ Double ] { border: double; } [ dot-dash ] { border: dot-dash; } [ dot-dot-dash ] { border: dot-dot-dash; } [ double-dash ] { border: double-dash; } [ none ] { border: none; } [ wave ] { border: wave; } [ Dashed ] ==> [ none ] [ Solid ] ..> [ Dotted ] [ dot-dash ] - > [ Bold ] [ dot-dot-dash ] .-> [ wave ] [ double-dash ] ~~> [ Double ] ����������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/5_group_repair.txt������������������������������������������������������������0000644�0000764�0000764�00000000324�12150106411�020032� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( DMZ: [ Backend ] [ Database ] [ Proxy ] [ Server ] ) [ Proxy ] --> [ Check ] [ Proxy ] --> { flow: south; } [ Database ] [ Proxy ] --> [ Server ] [ Check ] --> [ Backend ] [ Database ] --> [ Backend ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4nodes.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000116�12150106411�016303� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bonn ] --> [ Berlin ] [ Bonn ] --> [ Frankfurt ] [ Berlin ] ..> [ Cottbus ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/9_cross.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000354�12150106411�016474� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A ] { offset: 4,0; origin: U; } [ B|I ] { offset: 32,0; origin: A; } [ C | D||E ] { offset: 0,1; origin: A; } [ U ] --> [ A ] [ U ] --> { start: north; } [ I ] [ A ] --> [ BI.1 ] [ A ] --> [ BI.0 ] [ I ] --> [ Z ] [ BI.1 ] --> [ I ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_invisible_right.txt���������������������������������������������������������0000644�0000764�0000764�00000000021�12150106410�020503� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bonn ] --> [ ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_cross.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000214�11675050456�016504� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ B ] { offset: 40,0; origin: A; } [ C ] { offset: 20,-20; origin: A; } [ D ] { offset: 0,40; origin: C; } [ A ] --> [ B ] [ C ] - > [ D ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/5_arrow_styles.txt������������������������������������������������������������0000644�0000764�0000764�00000000560�11675050456�020115� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: none; } [ Hagnau ] { flow: down; } [ Unteruhlingen ] { border: dotted; } [ Hagnau ] <--> [ Oberuhlingen ] [ Hagnau ] <--> [ Uhlingen ] [ Mainau ] --> [ Unteruhlingen ] [ Mainau ] -- { arrowstyle: closed; } [ Unteruhlingen ] [ Oberuhlingen ] <--> { arrowstyle: closed; } [ Unteruhlingen ] [ Uhlingen ] <--> { arrowstyle: filled; } [ Oberuhlingen ] ������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/8_basename.txt����������������������������������������������������������������0000644�0000764�0000764�00000000114�11675050456�017131� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A|B|C ] { basename: A; } [ 1 ] --> [ A.2 ] [ 2 ] --> [ ABC.2 ] [ A|B|C ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_joint_bug_flags.txt���������������������������������������������������������0000644�0000764�0000764�00000000374�11675050456�020516� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ C ] { offset: 0,2; origin: B; } [ D ] { offset: 0,2; origin: C; } [ E ] { offset: -2,-2; origin: B; } [ B ] <--> { end: south, 0; start: west; } [ E ] [ C ] <--> { end: south, 0; start: west; } [ E ] [ D ] <--> { end: south, 0; start: west; } [ E ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_cross_split_hor.txt���������������������������������������������������������0000644�0000764�0000764�00000000264�11675050456�020574� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ B ] { offset: 0,4; origin: A; } [ C ] { offset: -2,2; origin: A; } [ D ] { offset: 2,2; origin: A; } ( Cross: [ A ] [ B ] [ C ] [ D ] ) [ A ] --> [ B ] [ C ] --> [ D ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/6_ranks.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000207�12150106411�016453� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ D ] { rank: 0; } [ F ] { rank: 0; } [ A ] --> [ B ] [ D ] --> [ E ] [ B ] --> [ C ] [ E ] --> { end: north; } [ F ] [ C ] --> [ D ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/5_flow.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000174�12150106411�016306� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bonn ] --> [ Cottbus ] [ Bonn ] --> { flow: forward; } [ Berlin ] [ Cottbus ] --> [ Moselkern ] [ Moselkern ] --> [ Ulm ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_cross_inv.txt���������������������������������������������������������������0000644�0000764�0000764�00000000242�11675050456�017361� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ B ] { offset: 40,0; origin: A; } [ C ] { offset: 20,-20; origin: A; } [ D ] { offset: 0,40; origin: C; } [ A ] --> { style: invisible; } [ B ] [ C ] - > [ D ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_dot_dot_dash.txt������������������������������������������������������������0000644�0000764�0000764�00000000222�11675050456�020003� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ B ] { border: dot-dash; } [ Test\n Test\n Test test test\n test ] { border: dot-dot-dash; } [ Test\n Test\n Test test test\n test ] ..-> [ B ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_bug_joint_2.txt�������������������������������������������������������������0000644�0000764�0000764�00000000430�11675050456�017554� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: none; } [ C ] { offset: 0,2; origin: B; } [ D ] { offset: 0,2; origin: C; } [ E ] { offset: -2,-2; origin: B; } [ B ] <--> { end: south, 0; start: west; } [ E ] [ C ] <--> { end: south, 0; start: west; } [ E ] [ D ] <--> { end: south, 0; start: west; } [ E ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/7_star.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000234�12150106411�016307� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Dachau ] --> [ Berlin ] [ Dachau ] --> [ Chemnitz ] [ Dachau ] --> [ Frankfurt ] [ Dachau ] --> [ Kiel ] [ Dachau ] --> [ Ulm ] [ Dachau ] --> [ Weimar ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/9_chain.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000341�12150106411�016421� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bonn ] --> [ Berlin ] [ Berlin ] --> [ Koeln ] [ Berlin ] --> [ Cottbus ] [ Koeln ] --> [ Frankfurt ] [ Frankfurt ] --> [ Hannover ] [ Frankfurt ] --> [ Chemnitz ] [ Frankfurt ] --> [ Dresden ] [ Hannover ] --> [ Hamburg ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/6_joint.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000277�12150106411�016467� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Hagen ] --> { end: back, 0; } [ Bonn ] [ Hamburg ] --> { end: back, 0; } [ Bonn ] [ Hanau ] --> { end: back, 0; } [ Bonn ] [ Hannover ] --> { end: back, 0; } [ Bonn ] [ Bonn ] --> [ Prag ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/8_chain.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000306�12150106411�016421� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bonn ] --> [ Berlin ] [ Berlin ] --> [ Koeln ] [ Koeln ] --> [ Frankfurt ] [ Frankfurt ] --> [ Hannover ] [ Frankfurt ] --> [ Chemnitz ] [ Frankfurt ] --> [ Dresden ] [ Hannover ] --> [ Hamburg ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_edge_start.txt��������������������������������������������������������������0000644�0000764�0000764�00000000104�12150106410�017446� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A ] --> { start: front; } [ B ] [ A ] --> { start: front; } [ C ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_group.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000102�11675050456�016501� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( Test\n group [ Berlin ] [ Bonn ] ) [ Bonn ] --> [ Berlin ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_colors.txt������������������������������������������������������������������0000644�0000764�0000764�00000000456�11675050456�016663� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { color: 3; colorscheme: paired12; fill: rgb(0.1,100,10%); } [ Colors ] { color: hsl(300,1.0,1.0); fill: w3c/grey; } [ Preserve ] { color: cornflowerblue; } [ The ] { color: #ff00ff; } [ Preserve ] --> { color: rgb(33,44,55); } [ The ] [ The ] --> { color: hsv(1.0,1.0,0.5); } [ Colors ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_lists.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000144�12150106410�016470� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bonn ] --> [ Berlin ] [ Bonn ] --> [ Frankfurt ] [ Ulm ] --> [ Berlin ] [ Ulm ] --> [ Frankfurt ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_class.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000144�11675050456�016460� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������node.second { border: double; } [ A ] { class: second; } [ B ] { class: second; } [ A ] --> [ B ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/7_cluster.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000040�11675050456�017034� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A | BCD | E | F || G | | H ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/6_chain_10_edges.txt����������������������������������������������������������0000644�0000764�0000764�00000000371�12150106411�020070� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bonn ] --> [ Ulm ] [ Bonn ] --> [ Berlin ] [ Bonn ] --> [ Koblenz ] [ Berlin ] --> [ Kassel ] [ Koblenz ] --> [ Berlin ] [ Ulm ] --> [ Bautzen ] [ Ulm ] --> [ Bautzen ] [ Ulm ] --> [ Koblenz ] [ Bautzen ] --> [ Berlin ] [ Bautzen ] --> [ Berlin ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/1_empty_group.txt�������������������������������������������������������������0000644�0000764�0000764�00000000057�11675050456�017727� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( Group ) { background: yellow; } [ Outside ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_dot.txt���������������������������������������������������������������������0000644�0000764�0000764�00000000103�12150106410�016111� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { label: // digraph G {; } [ Kummersbach ] --> [ Düsburg ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_autosplit_offset.txt��������������������������������������������������������0000644�0000764�0000764�00000000140�12150106410�020720� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ 1 ] { size: 1,2; } [ 2|3 ] { offset: 2,1; origin: 1; } [ 1 ] --> [ 23.1 ] [ 23.1 ] --> [ 3 ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_autosplit_empty.txt���������������������������������������������������������0000644�0000764�0000764�00000000035�11675050456�020616� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ 1 ] --> [ 23.1 ] [ 2| |3 ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_split_bug.txt���������������������������������������������������������������0000644�0000764�0000764�00000000033�11675050456�017340� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Some \[\] || Autosplit ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_joint.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000303�12150106410�016452� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ B ] { offset: 2,0; origin: A; size: 2,1; } [ C ] { offset: 2,-2; origin: B; } [ A ] --> { end: south, 0; start: south; } [ C ] [ B ] --> { end: south, 0; start: south; } [ C ] [ B ] --> [ U ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_split_attribute.txt���������������������������������������������������������0000644�0000764�0000764�00000000153�11675050456�020572� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������node { color: silver; } [ Some || Autosplit || Node ] { basename: u; color: red|blue; border: dashed|; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/6_nested_groups.txt�����������������������������������������������������������0000644�0000764�0000764�00000000251�11675050456�020237� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( Inner 1 [ A ] [ B ] ) { group: Outer; } ( Inner 2 [ C ] [ D ] ) { group: Outer; } ( Outer [ E ] [ F ] ) [ A ] --> [ B ] [ C ] --> [ D ] [ E ] --> [ F ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_invisible_both.txt����������������������������������������������������������0000644�0000764�0000764�00000000042�12150106410�020326� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ ] --> [ Bonn ] [ Bonn ] --> [ ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_selfloop_flow_left.txt������������������������������������������������������0000644�0000764�0000764�00000000152�12150106410�021214� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { flow: 270; } [ Start ] --> [ Main ] [ Main ] -- Until not done --> [ Main ] [ Main ] --> [ End ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/5_offsets_2.txt���������������������������������������������������������������0000644�0000764�0000764�00000000327�12150106411�017231� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A ] { offset: 2,1; origin: B; } [ B ] { offset: 1,1; origin: C; } [ C ] { offset: 1,1; origin: D; } [ A ] --> [ B ] [ E ] --> [ D ] [ B ] --> [ A ] [ B ] --> [ C ] [ B ] --> [ C ] [ D ] --> [ C ] [ D ] --> [ C ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/5_long_edge_labels.txt��������������������������������������������������������0000644�0000764�0000764�00000000230�12150106411�020575� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A ] --> [ B ] [ A ] -- very long edge label --> [ E ] [ B ] --> [ C ] [ E ] -- another very long edge label --> [ A ] [ C ] --> [ D ] [ D ] --> [ E ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_graph_label.txt�������������������������������������������������������������0000644�0000764�0000764�00000000115�12150106410�017566� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { label: My Graph; labelpos: top; } [ Regensburg ] --> [ Passau ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_cluster_2.txt���������������������������������������������������������������0000644�0000764�0000764�00000000154�11675050456�017256� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Altona ] { size: 3,2; } [ Hamburg ] { size: 2,2; } [ Hamburg ] --> [ Altona ] [ Hamburg ] --> [ Altona ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_edges.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000050�12150106410�016413� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ One ] --> [ Two ] [ One ] --> [ Two ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/8_labels.txt������������������������������������������������������������������0000644�0000764�0000764�00000001035�12150106411�016601� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { color: blue; labelcolor: green; } graph { background: oldlace; label: My sample graph; border: solid black; } [ Five ] { color: yellow; fill: maroon; } [ One ] { color: white; fill: seagreen; } [ Two ] { shape: triangle; } [ Five ] - Test label - > { color: red; labelcolor: darkslategrey; } [ Seven ] [ Five ] --> [ Seven ] [ Five ] <==> [ Three ] [ Five ] --> [ Eight ] [ One ] ==> [ Three ] [ One ] .. Test\n label ..> [ Four ] [ One ] -- label --> [ Two ] [ Seven ] -- [ Eight ] [ Three ] <.. Test label ..> [ Six ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4groups.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000151�11675050456�016533� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( German Cities [ Bremen ] [ Cuxhaven ] [ Flensburg ] [ Hamburg ] ) [ Cuxhaven ] --> [ Bremen ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/6_fanout.txt������������������������������������������������������������������0000644�0000764�0000764�00000000562�11675050456�016657� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A ] { label: Hello World!; } [ B ] { label: Hello World!; } [ C ] { label: Hello World!; } [ D ] { label: Hello World!; } [ middle ] { fill: #ccccff; label: /dev/fanout; } [ Hello World ] --> [ middle ] [ middle ] --> { start: front,0; } [ A ] [ middle ] --> { start: front,0; } [ B ] [ middle ] --> { start: front,0; } [ C ] [ middle ] --> { start: front,0; } [ D ] ����������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_endless_loop.txt������������������������������������������������������������0000644�0000764�0000764�00000000075�11675050456�020046� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ 2 ] { fill: red; } [ 3|4 ] { fill: red; } [ 1 ] --> [ 2 ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/gdl/��������������������������������������������������������������������������0000755�0000764�0000764�00000000000�12150110221�015110� 5����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/gdl/2_bottom_to_top.txt�������������������������������������������������������0000644�0000764�0000764�00000000133�12150107022�020764� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { flow: north; } node { align: left; } [ A ] --> [ B ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/gdl/1_color_code.txt����������������������������������������������������������0000644�0000764�0000764�00000000350�11675050456�020227� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { flow: south; label: Graph of sub_105D40F; } node { align: left; } [ 0 ] { label: sub_105D40F:\nmov edi, esi\npush ebp\njz short loc_105D27B; rank: 0; x-vcg-vertical_order: 0; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/gdl/2_right_to_left.txt�������������������������������������������������������0000644�0000764�0000764�00000000132�12150107023�020725� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { flow: west; } node { align: left; } [ A ] --> [ B ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/gdl/2_left_to_right.txt�������������������������������������������������������0000644�0000764�0000764�00000000132�12150107023�020725� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { flow: east; } node { align: left; } [ A ] --> [ B ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/gdl/2_top_to_bottom.txt�������������������������������������������������������0000644�0000764�0000764�00000000133�12150107023�020765� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrowstyle: filled; } graph { flow: south; } node { align: left; } [ A ] --> [ B ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/0_empty_groups.txt������������������������������������������������������������0000644�0000764�0000764�00000000017�11675050456�020105� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( ) ( ) ( ) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_group_no_border.txt���������������������������������������������������������0000644�0000764�0000764�00000000124�11675050456�020536� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( Test\n group [ Berlin ] [ Bonn ] ) { border: none; } [ Bonn ] --> [ Berlin ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_nodes_5_edges.txt�����������������������������������������������������������0000644�0000764�0000764�00000000247�12150106410�020041� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Default Page ] --> [ Sign Up ] [ Default Page ] --> [ Test Search ] [ Default Page ] ..> [ Main Page ] [ Sign Up ] --> [ Main Page ] [ Test Search ] --> [ Sign Up ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_cache_bug.txt���������������������������������������������������������������0000644�0000764�0000764�00000000210�12150106410�017223� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A ] { flow: east; } [ B ] { offset: 2,2; origin: A; } ( G [ A ] ) [ A ] -- C --> { end: north; start: east; } [ B ] [ B ] --> [ ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/6_empty_row.txt���������������������������������������������������������������0000644�0000764�0000764�00000000060�11675050456�017401� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A|| ||B ] [ AB.0 ] --> [ CD.0 ] [ C|| ||D ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_group_align_center.txt������������������������������������������������������0000644�0000764�0000764�00000000642�12150106410�021202� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { align: right; } graph { fontsize: 2em; label: My Graph; } node.left { align: left; fontsize: 1.5em; } [ Left\naligned ] { class: left; } [ Right\nAligned ] { align: right; } ( Nodes: [ Center\n aligned ] [ Left\naligned ] [ Right\nAligned ] ) { align: center; border: none; } [ Right\nAligned ] -- label\n text --> { align: right; } [ Left\naligned ] [ Left\naligned ] --> [ Center\n aligned ] ����������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_list_attr.txt���������������������������������������������������������������0000644�0000764�0000764�00000000462�11675050456�017365� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Berlin ] { border: dashed; } [ Bonn ] { border: dashed; } [ Frankfurt ] { border: dot-dot-dash; } [ Frankfurt (Oder) ] { border: dot-dot-dash; } [ Berlin ] -- test --> [ Frankfurt ] [ Berlin ] -- test --> [ Frankfurt (Oder) ] [ Bonn ] -- test --> [ Frankfurt ] [ Bonn ] -- test --> [ Frankfurt (Oder) ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_edge_labels.txt�������������������������������������������������������������0000644�0000764�0000764�00000000202�12150106410�017553� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bonn ] == train ==> [ Berlin ] [ Bonn ] -- car --> [ Potsdam ] [ Berlin ] .. bus ..> [ Potsdam ] [ Berlin ] .- bike .-> [ Ulm ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_selfloop.txt����������������������������������������������������������������0000644�0000764�0000764�00000000245�12150106410�017155� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { flow: 180; } [ Start ] --> [ Main ] [ Main ] -- Until not done --> [ Main ] [ Main ] -- Until not done --> [ Main ] [ Main ] -- Until not done --> [ Main ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4groups_class.txt�������������������������������������������������������������0000644�0000764�0000764�00000000257�11675050456�017727� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������node.city { color: #801010; } ( German Cities [ Bremen ] [ Cuxhaven ] [ Flensburg ] [ Hamburg ] ) { nodeclass: city; border: dot-dash; } [ Cuxhaven ] --> [ Bremen ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_nodes_6_edges.txt�����������������������������������������������������������0000644�0000764�0000764�00000000312�12150106410�020033� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Default Page ] --> [ Sign Up ] [ Default Page ] --> [ Test Search ] [ Default Page ] --> [ Main Page ] [ Default Page ] --> [ Main Page ] [ Sign Up ] --> [ Main Page ] [ Test Search ] --> [ Sign Up ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/5_multicell.txt���������������������������������������������������������������0000644�0000764�0000764�00000000513�11675050456�017350� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { flow: up; } [ Client ] { offset: 0,2; origin: Proxy; } [ Client1 ] { offset: 0,-2; origin: Proxy; } [ Client2 ] { offset: 2,0; origin: Proxy; } [ Client3 ] { offset: -2,0; origin: Proxy; } [ Proxy ] { size: 3,2; } [ Client ] <--> [ Proxy ] [ Client1 ] <--> [ Proxy ] [ Client2 ] <--> [ Proxy ] [ Client3 ] <--> [ Proxy ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_empty_group.txt�������������������������������������������������������������0000644�0000764�0000764�00000000053�11675050456�017725� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( [ A ] [ B ] ) [ A ] --> [ B ] [ C ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_autosplit_hang.txt����������������������������������������������������������0000644�0000764�0000764�00000000033�11675050456�020372� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ 1 ] --> [ 23.0 ] [ 2|3 ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_cluster_3.txt���������������������������������������������������������������0000644�0000764�0000764�00000000207�11675050456�017256� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Altona ] { size: 3,2; } [ Hamburg ] { size: 2,2; } [ Hamburg ] --> [ Altona ] [ Hamburg ] --> [ Altona ] [ Hamburg ] --> [ Altona ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_corrupt.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000142�11675050456�017050� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A ] { label: AB; } [ B ] { label: ABCB; } [ C ] { label: AB; } [ A ] --> [ B ] [ B ] --> [ C ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_node_edge.txt���������������������������������������������������������������0000644�0000764�0000764�00000000113�12150106410�017237� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ test ] { shape: edge; } [ A ] -- [ test ] [ test ] -- [ ] [ ] --> [ B ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_inherit.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000332�11675050456�017015� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { flow: down; } node { align: left; } node.right { align: right; } [ Left ] { class: none; } [ Lefty, too ] { class: none; } [ Right ] { class: right; } [ Left ] --> [ Lefty, too ] [ Lefty, too ] --> [ Right ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_edge_repair.txt�������������������������������������������������������������0000644�0000764�0000764�00000000170�12150106410�017576� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( Router: [ Input ] [ Output ] ) [ Input ] --> [ Output ] [ Output ] ==> { end: north; start: south; } [ Network ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_zeros.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000102�11675050456�016507� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ 10 ] { label: 0; } [ 0 ] -- 0 --> [ 10 ] [ 10 ] -- 0 --> [ 0 ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/7_tree.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000320�12150106411�016271� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { start: south, 0; } [ A1 ] { offset: 2,1; origin: A; } [ A23 ] { offset: 2,1; origin: A2; } [ A ] --> [ A2 ] [ A ] --> [ A1 ] [ A ] --> [ A3 ] [ A2 ] --> [ A21 ] [ A2 ] --> [ A22 ] [ A2 ] --> [ A23 ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_classes.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000555�11675050456�017016� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge.blue { color: blue; } edge.green { color: blue; } edge.red { color: red; } group { color: blue; } group.blue { color: blue; } group.green { color: blue; } group.red { color: red; } node.blue { color: blue; } node.green { color: blue; } node.red { color: red; } [ Black ] { class: green; } [ Red ] { class: red; } [ Red ] -- red --> { class: red; } [ Black ] ���������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_edge_labels_from_class.txt��������������������������������������������������0000644�0000764�0000764�00000000143�12150106410�021766� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { label: MyLabel; } edge.yes { label: Yes; } [ A ] --> { class: yes; } [ B ] [ B ] --> [ C ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_flow.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000110�12150106410�016270� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { flow: 90; } [ Left ] --> { end: left; start: left; } [ Right ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_long_labels.txt�������������������������������������������������������������0000644�0000764�0000764�00000000072�12150106410�017611� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ My\n long\n node\n name ] -- A\n long\n label --> [ B ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_flow.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000164�12150106410�016302� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { flow: down; } [ Enschede ] { flow: left; } [ Enschede ] --> [ Bielefeld ] [ Bielefeld ] --> [ Wolfsburg ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2nodes.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000024�12150106410�016276� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ One ] ==> [ Two ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_selfloop.txt����������������������������������������������������������������0000644�0000764�0000764�00000000124�12150106410�017152� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Start ] --> [ Main ] [ Main ] -- Until not done --> [ Main ] [ Main ] --> [ End ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_autosplit_class.txt���������������������������������������������������������0000644�0000764�0000764�00000000157�11675050456�020572� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������node { border: double; } node.b { border: dotted; } node.c { border: dashed; } [ C|D ] { class: c; } [ A|B ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/5_offsets.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000247�12150106411�017011� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A ] { offset: 2,1; origin: B; } [ B ] { offset: 1,1; origin: C; } [ C ] { offset: 1,1; origin: D; } [ A ] --> [ B ] [ B ] --> [ C ] [ C ] --> [ D ] [ D ] --> [ E ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_selfloop_flow_up.txt��������������������������������������������������������0000644�0000764�0000764�00000000150�12150106410�020704� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { flow: 0; } [ Start ] --> [ Main ] [ Main ] -- Until not done --> [ Main ] [ Main ] --> [ End ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_group_multicell.txt���������������������������������������������������������0000644�0000764�0000764�00000000253�11675050456�020562� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Frankfurt a. Main\n (Flughafen) ] { size: 2,2; } ( Some group: [ Berlin ] [ Frankfurt a. Main\n (Flughafen) ] ) [ Frankfurt a. Main\n (Flughafen) ] --> [ Berlin ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_cluster.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000121�11675050456�017027� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Altona ] { size: 3,2; } [ Hamburg ] { size: 2,2; } [ Hamburg ] --> [ Altona ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_autosplit_shape.txt���������������������������������������������������������0000644�0000764�0000764�00000000065�11675050456�020563� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A|B ] { shape: |none; } [ C|D ] { shape: none|; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/6_autosplit_class.txt���������������������������������������������������������0000644�0000764�0000764�00000000222�12150106411�020543� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������node.red { color: red; columns: 2; label: D; } [ A|B|C ] { basename: A; class: red; } [ 1 ] --> [ A.0 ] [ 2 ] --> [ 3 ] [ 2 ] --> [ A.0 ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_cross_split.txt�������������������������������������������������������������0000644�0000764�0000764�00000000264�11675050456�017724� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ B ] { offset: 4,0; origin: A; } [ C ] { offset: 2,-2; origin: A; } [ D ] { offset: 2,2; origin: A; } ( Cross: [ A ] [ B ] [ C ] [ D ] ) [ A ] --> [ B ] [ C ] --> [ D ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/1_undirected_loop.txt���������������������������������������������������������0000644�0000764�0000764�00000000074�11675050456�020533� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A ] -- [ A ] [ A ] -- [ A ] [ A ] -- [ A ] [ A ] -- [ A ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/8_points.txt������������������������������������������������������������������0000644�0000764�0000764�00000000524�11675050456�016677� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������node { shape: point; } [ B ] { pointstyle: square; } [ C ] { pointstyle: dot; } [ D ] { pointstyle: circle; } [ E ] { pointstyle: diamond; } [ F ] { pointstyle: cross; } [ G ] { shape: invisible; } [ H ] { pointstyle: star; } [ A ] --> [ B ] [ B ] --> [ C ] [ C ] --> [ D ] [ D ] --> [ E ] [ E ] --> [ F ] [ F ] --> [ G ] [ G ] --> [ H ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/6_multicell.txt���������������������������������������������������������������0000644�0000764�0000764�00000000316�12150106411�017330� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A ] { size: 2,2; } [ B ] { rows: 2; } [ C ] { size: 3,1; } [ A ] --> [ B ] [ A ] --> [ B ] [ A ] --> [ F ] [ A ] --> [ G ] [ B ] --> [ C ] [ B ] --> [ C ] [ B ] --> [ C ] [ C ] --> [ D ] [ D ] --> [ C ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/6_split_join_loop.txt���������������������������������������������������������0000644�0000764�0000764�00000000276�12150106411�020546� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A ] --> [ B ] [ B ] --> [ C ] [ C ] --> { start: front, 0; } [ X ] [ C ] --> { start: front, 0; } [ Y ] [ X ] --> { end: back, 0; } [ I ] [ Y ] --> { end: back, 0; } [ I ] [ I ] --> [ A ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/5_joint_bug2.txt��������������������������������������������������������������0000644�0000764�0000764�00000000564�11675050456�017426� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A ] { flow: south; size: 5,1; } [ B ] { offset: 0,2; origin: A; } [ C ] { offset: 2,0; origin: B; } [ D ] { offset: 2,0; origin: C; } [ E ] { offset: 2,0; origin: A; } [ A ] <--> [ B ] [ A ] <--> [ C ] [ A ] <--> [ D ] [ B ] <--> { end: south, 0; start: south; } [ E ] [ C ] <--> { end: south, 0; start: south; } [ E ] [ D ] <--> { end: south, 0; start: south; } [ E ] ��������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/5_joint_label.txt�������������������������������������������������������������0000644�0000764�0000764�00000000334�12150106411�017617� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Aachen ] -- travel --> { end: back, 0; } [ Zittau ] [ Buxtehude ] -- travel --> { end: back, 0; } [ Zittau ] [ Cottbus ] -- travel --> { end: back, 0; } [ Zittau ] [ Dachau ] -- travel --> { end: back, 0; } [ Zittau ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/3_joint.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000131�12150106410�016450� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Mannheim ] --> { end: back,0; } [ Weimar ] [ Potsdam ] --> { end: back,0; } [ Weimar ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/4_near.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000112�12150106410�016252� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bonn ] --> [ Berlin ] [ Essen ] --> [ Berlin ] [ Berlin ] --> [ Koeln ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/2_autosplit_empty.txt���������������������������������������������������������0000644�0000764�0000764�00000000026�11675050456�020614� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Siegen | Siegburg ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/txt/5_joint.txt�������������������������������������������������������������������0000644�0000764�0000764�00000001030�12150106411�016452� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Aachen ] { offset: 0,2; origin: Hannover; } [ Berlin ] { offset: 2,0; origin: Aachen; } [ Cuxhaven ] { offset: 4,0; origin: Aachen; } [ Hannover ] { flow: down; } [ Zwickau ] { offset: 2,-2; origin: Hannover; } [ Hannover ] --> { start: south; } [ Aachen ] [ Hannover ] --> { start: south; } [ Berlin ] [ Hannover ] --> { start: south; } [ Cuxhaven ] [ Aachen ] --> { end: south, 0; start: south; } [ Zwickau ] [ Berlin ] --> { end: south, 0; start: south; } [ Zwickau ] [ Cuxhaven ] --> { end: south, 0; start: south; } [ Zwickau ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/pod.t�����������������������������������������������������������������������������0000644�0000764�0000764�00000002710�11675050456�014522� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use Test::More; use strict; my $tests; BEGIN { $tests = 23; plan tests => $tests; chdir 't' if -d 't'; use lib '../lib'; }; SKIP: { skip( 'Test::Pod not installed on this system', $tests ) unless do { eval "use Test::Pod;"; $@ ? 0 : 1; }; pod_file_ok( '../lib/Graph/Easy.pm' ); pod_file_ok( '../lib/Graph/Easy/Base.pm' ); pod_file_ok( '../lib/Graph/Easy/Layout.pm' ); pod_file_ok( '../lib/Graph/Easy/Layout/Chain.pm' ); pod_file_ok( '../lib/Graph/Easy/Layout/Scout.pm' ); pod_file_ok( '../lib/Graph/Easy/Layout/Path.pm' ); pod_file_ok( '../lib/Graph/Easy/Layout/Grid.pm' ); pod_file_ok( '../lib/Graph/Easy/Layout/Repair.pm' ); pod_file_ok( '../lib/Graph/Easy/Node.pm' ); pod_file_ok( '../lib/Graph/Easy/Edge.pm' ); pod_file_ok( '../lib/Graph/Easy/Group.pm' ); pod_file_ok( '../lib/Graph/Easy/Parser.pm' ); pod_file_ok( '../lib/Graph/Easy/Parser/Graphviz.pm' ); pod_file_ok( '../lib/Graph/Easy/Parser/VCG.pm' ); pod_file_ok( '../lib/Graph/Easy/Attributes.pm' ); pod_file_ok( '../lib/Graph/Easy/As_ascii.pm' ); pod_file_ok( '../lib/Graph/Easy/As_txt.pm' ); pod_file_ok( '../lib/Graph/Easy/As_graphviz.pm' ); pod_file_ok( '../lib/Graph/Easy/Edge/Cell.pm' ); pod_file_ok( '../lib/Graph/Easy/Node/Anon.pm' ); pod_file_ok( '../lib/Graph/Easy/Node/Cell.pm' ); pod_file_ok( '../lib/Graph/Easy/Group/Cell.pm' ); pod_file_ok( '../lib/Graph/Easy/Group/Anon.pm' ); } ��������������������������������������������������������Graph-Easy-0.73/t/gv.t������������������������������������������������������������������������������0000644�0000764�0000764�00000010613�12150106072�014336� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use Test::More; use strict; use File::Spec; # test graphviz (dot) file input => ASCII output # and back to as_txt() again BEGIN { plan tests => 140; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy") or die($@); use_ok ("Graph::Easy::Parser") or die($@); use_ok ("Graph::Easy::Parser::Graphviz") or die($@); }; my @warnings; ############################################################################# # override the warn method to catch warnigs { no warnings 'redefine'; package Graph::Easy::Base; sub warn { my ($self,$msg) = @_; push @warnings, $msg; } } ############################################################################# # parser object my $def_parser = Graph::Easy::Parser->new(debug => 0); is (ref($def_parser), 'Graph::Easy::Parser'); is ($def_parser->error(), '', 'no error yet'); my $dot_parser = Graph::Easy::Parser::Graphviz->new(debug => 0); is (ref($dot_parser), 'Graph::Easy::Parser::Graphviz'); is ($dot_parser->error(), '', 'no error yet'); my $dir = File::Spec->catdir('in','dot'); opendir DIR, $dir or die ("Cannot read dir '$dir': $!"); my @files = readdir(DIR); closedir(DIR); opendir DIR, 'dot' or die ("Cannot read dir 'dot': $!"); push @files, readdir(DIR); closedir(DIR); binmode (STDERR, ':utf8') or die ("Cannot do binmode(':utf8') on STDERR: $!"); binmode (STDOUT, ':utf8') or die ("Cannot do binmode(':utf8') on STDOUT: $!"); eval { require Test::Differences; }; foreach my $f (sort { $a =~ /^(\d+)/; my $a1 = $1 || '1'; $b =~ /^(\d+)/; my $b1 = $1 || '1'; $a1 <=> $b1 || $a cmp $b; } @files) { my $file = File::Spec->catfile($dir,$f); my $parser = $def_parser; if (!-f $file) { $file = File::Spec->catfile('dot',$f); next unless -f $file; # only files # for files in t/dot, we need to use the Graphviz parser as they # look like Graph::Easy text to the normal parser, which then fails $parser = $dot_parser; } next unless $f =~ /\.dot/; # ignore anything else print "# at $f\n"; my $txt = readfile($file); $parser->reset(); my $graph = $parser->from_text($txt); # reuse parser object $f =~ /^(\d+)/; my $nodes = $1; if (!defined $graph) { fail ("Graphviz input was invalid: " . $parser->error()); next; } is (scalar $graph->nodes(), $nodes, "$nodes nodes"); # for slow testing machines $graph->timeout(20); my $ascii = $graph->as_ascii(); my $of = $f; $of =~ s/\.dot/\.txt/; my $out_path = File::Spec->catfile('out','dot',$of); my $out = readfile($out_path); $out =~ s/(^|\n)#[^# ]{2}.*\n//g; # remove comments $out =~ s/\n\n\z/\n/mg; # remove empty lines # print "txt: $txt\n"; # print "ascii: $ascii\n"; # print "should: $out\n"; if (! is ($ascii, $out, "from $f")) { if ($ENV{__SHLOMIF__UPDATE_ME}) { require IO::All; IO::All->new->file($out_path)->utf8->print($ascii); } if (defined $Test::Differences::VERSION) { Test::Differences::eq_or_diff ($ascii, $out); } else { fail ("Test::Differences not installed"); } } # if the txt output differes, read it in my $f_txt = File::Spec->catfile('txt','dot',$of); if (-f $f_txt) { $txt = readfile($f_txt); } $graph->debug(1); if (! is ($graph->as_txt(), $txt, "$f as_txt")) { if ($ENV{__SHLOMIF__UPDATE_ME}) { require IO::All; IO::All->new->file($f_txt)->utf8->print($graph->as_txt()); } if (defined $Test::Differences::VERSION) { Test::Differences::eq_or_diff ($graph->as_txt(), $txt); } else { fail ("Test::Differences not installed"); } } # print a debug output my $debug = $ascii; $debug =~ s/\n/\n# /g; print "# Generated:\n#\n# $debug\n"; } # check that only the expected warnings were generated use Data::Dumper; print STDERR Dumper(\@warnings) unless is (scalar @warnings, 6, 'Got exactly 6 warnings'); my $i = 0; for my $name (qw/bar foo pname fname bar brabble/) { like ($warnings[$i], qr/Ignoring unknown attribute '$name' for class/, "Got warning about $name"); $i++; } 1; sub readfile { my ($file) = @_; open my $FILE, $file or die ("Cannot read file $file: $!"); binmode ($FILE, ':utf8') or die ("Cannot do binmode(':utf8') on $FILE: $!"); local $/ = undef; # slurp mode my $doc = <$FILE>; close $FILE; $doc; } ���������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/gdl.t�����������������������������������������������������������������������������0000644�0000764�0000764�00000005643�12150104131�014471� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use Test::More; use strict; use File::Spec; # test GDL (Graph Description Language) file input => ASCII output # and back to as_txt() again BEGIN { plan tests => 20; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy") or die($@); use_ok ("Graph::Easy::Parser") or die($@); use_ok ("Graph::Easy::Parser::VCG") or die($@); }; ############################################################################# # parser object my $def_parser = Graph::Easy::Parser->new(debug => 0); is (ref($def_parser), 'Graph::Easy::Parser'); is ($def_parser->error(), '', 'no error yet'); my $dir = File::Spec->catdir('in','gdl'); opendir DIR, $dir or die ("Cannot read dir $dir: $!"); my @files = readdir(DIR); closedir(DIR); binmode (STDERR, ':utf8') or die ("Cannot do binmode(':utf8') on STDERR: $!"); binmode (STDOUT, ':utf8') or die ("Cannot do binmode(':utf8') on STDOUT: $!"); eval { require Test::Differences; }; foreach my $f (sort { $a =~ /^(\d+)/; my $a1 = $1 || '1'; $b =~ /^(\d+)/; my $b1 = $1 || '1'; $a1 <=> $b1 || $a cmp $b; } @files) { my $file = File::Spec->catfile($dir,$f); my $parser = $def_parser; next unless $f =~ /\.gdl/; # ignore anything else print "# at $f\n"; my $txt = readfile($file); $parser->reset(); my $graph = $parser->from_text($txt); # reuse parser object $f =~ /^(\d+)/; my $nodes = $1; if (!defined $graph) { fail ("GDL input was invalid: " . $parser->error()); next; } is (scalar $graph->nodes(), $nodes, "$nodes nodes"); # for slow testing machines $graph->timeout(20); my $ascii = $graph->as_ascii(); my $of = $f; $of =~ s/\.gdl/\.txt/; my $out = readfile(File::Spec->catfile('out','gdl',$of)); $out =~ s/(^|\n)#[^# ]{2}.*\n//g; # remove comments $out =~ s/\n\n\z/\n/mg; # remove empty lines # print "txt: $txt\n"; # print "ascii: $ascii\n"; # print "should: $out\n"; if (! is ($ascii, $out, "from $f")) { if (defined $Test::Differences::VERSION) { Test::Differences::eq_or_diff ($ascii, $out); } else { fail ("Test::Differences not installed"); } } # if the txt output differes, read it in my $f_txt = File::Spec->catfile('txt','gdl',$of); if (-f $f_txt) { $txt = readfile($f_txt); } $graph->debug(1); if (! is ($graph->as_txt(), $txt, "$f as_txt")) { if (defined $Test::Differences::VERSION) { Test::Differences::eq_or_diff ($graph->as_txt(), $txt); } else { fail ("Test::Differences not installed"); } } # print a debug output my $debug = $ascii; $debug =~ s/\n/\n# /g; print "# Generated:\n#\n# $debug\n"; } 1; sub readfile { my ($file) = @_; open my $FILE, $file or die ("Cannot read file $file: $!"); binmode ($FILE, ':utf8') or die ("Cannot do binmode(':utf8') on $FILE: $!"); local $/ = undef; # slurp mode my $doc = <$FILE>; close $FILE; $doc; } ���������������������������������������������������������������������������������������������Graph-Easy-0.73/t/graph-maker.t���������������������������������������������������������������������0000644�0000764�0000764�00000003041�11675050456�016134� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # test interface being compatible to Graph.pm so that Graph::Maker works: use Test::More; use strict; BEGIN { plan tests => 15; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy") or die($@); }; can_ok ('Graph::Easy', qw/ new add_vertex add_vertices has_edge vertices add_path add_cycle /); ############################################################################# my $graph = Graph::Easy->new(); is (ref($graph), 'Graph::Easy'); is ($graph->error(), '', 'no error yet'); $graph->add_vertex('A'); my $A = $graph->node('A'); is (scalar $graph->vertices(), 1, '1 vertex'); my @nodes = $graph->vertices(); is ($nodes[0], $A, '1 vertex'); my $edge = $graph->add_edge ('A', 'B'); is ($graph->has_edge('A','B'), 1, 'has_edge()'); is ($graph->has_edge($A,'B'), 1, 'has_edge()'); is ($graph->has_edge('C','B'), 0, 'has_edge()'); $graph->add_vertices('A','B','C'); is (scalar $graph->vertices(), 3, '3 vertices'); $graph->set_vertex_attribute('A','fill','#deadff'); my $atr = $graph->get_vertex_attribute('A','fill'); is ($atr, $A->attribute('fill'), 'attribute got set'); ############################################################################# ## add_cycle and add_path # $graph = Graph::Easy->new(); $graph->add_path('A','B','C'); is (scalar $graph->vertices(), 3, '3 vertices'); is (scalar $graph->edges(), 2, '2 vertices'); $graph = Graph::Easy->new(); $graph->add_cycle('A','B','C'); is (scalar $graph->vertices(), 3, '3 vertices'); is (scalar $graph->edges(), 3, '3 vertices'); �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/heap.t����������������������������������������������������������������������������0000644�0000764�0000764�00000005536�11675050456�014666� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # Test the Heap structure for A* use Test::More; use strict; BEGIN { plan tests => 72; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy::Layout") or die($@); use_ok ("Graph::Easy") or die($@); }; can_ok ("Graph::Easy::Heap", qw/ add extract_top elements delete /); my $heap = Graph::Easy::Heap->new(); ############################################################################# # heap tests is (ref($heap), 'Graph::Easy::Heap', 'new() worked'); is ($heap->elements(), 0, '0 elements'); # add some elements (some of them with the same weight) $heap->add( [ 1, '', 0,0] ); is ($heap->elements(), 1, '1 elements'); $heap->add( [ 1, '', 1,0] ); is ($heap->elements(), 2, '2 elements'); $heap->add( [ 2, '', 2,0] ); is ($heap->elements(), 3, '3 elements'); $heap->add( [ 2, '', 3,0] ); is ($heap->elements(), 4, '4 elements'); $heap->add( [ 2, '', 4,0] ); is ($heap->elements(), 5, '5 elements'); $heap->add( [ 2, '', 5,0] ); is ($heap->elements(), 6, '6 elements'); $heap->add( [ 3, '', 6,0] ); is ($heap->elements(), 7, '7 elements'); # extract them again for (my $i = 0; $i < 7; $i++) { my $e = $heap->extract_top(); is ($e->[2], $i, "elem $i extracted"); } ############################################################################# # add some elements (some of them with the same weight) $heap->add( [ 1, '', 0,0] ); is ($heap->elements(), 1, '1 elements'); $heap->add( [ 1, '', 1,0] ); is ($heap->elements(), 2, '2 elements'); $heap->add( [ 2, '', 2,0] ); is ($heap->elements(), 3, '3 elements'); $heap->add( [ 2, '', 3,0] ); is ($heap->elements(), 4, '4 elements'); $heap->add( [ 2, '', 4,0] ); is ($heap->elements(), 5, '5 elements'); $heap->add( [ 2, '', 5,0] ); is ($heap->elements(), 6, '6 elements'); $heap->add( [ 3, '', 7,0] ); is ($heap->elements(), 7, '7 elements'); # supposed to end at the end of the row of "2" $heap->add( [ 2, '', 6,0] ); is ($heap->elements(), 8, '8 elements'); # extract them again for (my $i = 0; $i < 8; $i++) { my $e = $heap->extract_top(); is ($e->[2], $i, "elem $i extracted"); } is ($heap->elements(), 0, '0 elements'); ############################################################################# # overflow the simple algorithm (more than 16) and use binary search for add for (my $i = 0; $i < 8; $i++) { $heap->add( [ 1, '', $i,0] ); } is ($heap->elements(), 8, '8 elements'); for (my $i = 0; $i < 7; $i++) { $heap->add( [ 2, '', $i+8,0] ); } is ($heap->elements(), 15, '15 elements'); for (my $i = 0; $i < 16; $i++) { $heap->add( [ 3, '', $i+8+8,0] ); } is ($heap->elements(), 31, '31 elements'); # supposed to end at the end of the row of "2" $heap->add( [ 2, '', 15,0] ); is ($heap->elements(), 32, '32 elements'); # extract them again for (my $i = 0; $i < 32; $i++) { my $e = $heap->extract_top(); is ($e->[2], $i, "elem $i extracted"); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/layout_r.t������������������������������������������������������������������������0000644�0000764�0000764�00000001432�11675050456�015576� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # test the recursive layouter use Test::More; use strict; BEGIN { plan tests => 3; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy") or die($@); use_ok ("Graph::Easy::As_txt") or die($@); require_ok ("Graph::Easy::As_ascii") or die($@); }; ############################################################################# # laying out a group of nodes my $g = Graph::Easy->new(); my $gr = $g->add_group('Am Rhein:'); my ($a,$b,$e) = $g->add_edge('St. Goarshausen','St. Goar', 'Ferry'); $gr->add_node($a); $gr->add_node($b); #$g->{debug} = 1; # this is only called for the graph itself, so force it beforehand $g->_edges_into_groups(); $gr->_layout(); #use Data::Dumper; #print STDERR Dumper($gr->{cells}); #print $g->as_ascii(); ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/cluster.t�������������������������������������������������������������������������0000644�0000764�0000764�00000002656�11675050456�015432� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use Test::More; use strict; BEGIN { plan tests => 12; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy") or die($@); }; ############################################################################# # basic tests my $graph = Graph::Easy->new(); my ($first, $second, $edge) = $graph->add_edge('first', 'second'); $second->set_attribute('origin', $first->{name}); is (join(",", $second->offset()), '0,0', 'offset is 0,0'); is ($second->origin(), $first, 'origin is $first'); ############################################################################# # graph tests # node placement (clustered) $graph = Graph::Easy->new(); $first = $graph->add_node('A'); $second = $graph->add_node('B'); $second->relative_to($first, 1,0); is (scalar $graph->nodes(), 2, 'two nodes'); my $cells = { }; my $parent = { cells => $cells }; is ($first->_do_place(1,1,$parent), 1, 'node can be placed'); is ($cells->{"1,1"}, $first, 'first was really placed'); is ($cells->{"2,1"}, $second, 'second node was placed, too'); is (scalar keys %$cells, 2, 'two nodes placed'); # 1,0 and 2,0 are blocked, so 0,0+1,0; 1,0+2,0 and 2,0+3,0 are blocked, too: is ($first->_do_place(0,1,$parent), 0, 'node cannot be placed again'); is ($first->_do_place(1,1,$parent), 0, 'node cannot be placed again'); is ($first->_do_place(2,1,$parent), 0, 'node cannot be placed again'); is (scalar keys %$cells, 2, 'two nodes placed'); ����������������������������������������������������������������������������������Graph-Easy-0.73/t/in/�������������������������������������������������������������������������������0000755�0000764�0000764�00000000000�12150110221�014131� 5����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_selfloop_flip.txt������������������������������������������������������������0000644�0000764�0000764�00000000125�11675050456�017777� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Adenau ] -> [ Monschau ] [ Monschau ] -> [ Monschau ] [ Nideggen ] -> [ Monschau ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_bug_basename.txt�������������������������������������������������������������0000644�0000764�0000764�00000000230�11675050456�017550� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ u ] --> [ a | b | c ] # putting the attributes on an extra line failed prior to v0.55 due to # the basename being wrong: { basename: pv; } [ pv.0 ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_nodes_5_edges.txt������������������������������������������������������������0000644�0000764�0000764�00000000251�11675050456�017645� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Default Page ] --> [ Sign Up ] [ Sign Up ] --> [ Main Page ] [ Default Page ] .-> [ Main Page ] [ Default Page ] = > [ Main Page ] [ Default Page ] - > [ Main Page ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_cluster.txt������������������������������������������������������������������0000644�0000764�0000764�00000000160�11675050456�016622� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# A node-cluster (autosplit into three single nodes) [ Husum | Schleswig | Flensburg ] { background: #ddaaff; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_label_align.txt��������������������������������������������������������������0000644�0000764�0000764�00000000117�11675050456�017373� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Long Node Label\l left\r right\c center ] -- A\r long\n edge label --> [ B ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_selfloop_flow_down.txt�������������������������������������������������������0000644�0000764�0000764�00000000152�11675050456�021043� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { flow: 180; } [ Start ] --> [ Main ] [ Main ] -- Until not done --> [ Main ] [ Main ] --> [ End ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_att.txt����������������������������������������������������������������������0000644�0000764�0000764�00000000213�11675050456�015731� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������node { title: 100%25; } [ 1 ] { label: $a = %229%22%3b\nmy $b = 1%3b; } [ 3 ] { label: $a = %22%3b%25; } [ 1 ] --> [ 2 ] [ 3 ] --> [ 4 ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_flow_chain.txt���������������������������������������������������������������0000644�0000764�0000764�00000000123�11675050456�017252� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ Bonn ] -> { flow: forward; } [ Berlin ] [ Bonn ] -> [ Cottbus ] --> [ Ulm ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/8_optimize_bend.txt������������������������������������������������������������0000644�0000764�0000764�00000000765�11675050456�020011� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Example from Graphviz extension # http://www.wickle.com/wiki/index.php/Graphviz_extension node { shape: circle; } [ main ] { shape: rect; } # this is a comment -> [ parse ] -> [ execute ] -> [ make_string ] { label: make a\n string; } [ main ] ..> [ init ] [ main ] -- Testlabel --> [ cleanup ] [ init ] -> [ make_string ] [ main ] == 100 times ==> { color: red; style: dashed; } [ printf ] # dashed red edge! [ execute ] -> { color: red; } [ compare ] { shape: rect; background: #c1b2ff; } �����������Graph-Easy-0.73/t/in/6_chained.txt������������������������������������������������������������������0000644�0000764�0000764�00000000205�11675050456�016537� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bonn ] -> [ Berlin ] -> [ Kassel ] [ Bonn ] -> [ Koblenz ] [ Bonn ] -> [ Ulm ] -> [ Koblenz ] [ Ulm ] -> [ Bautzen ] -> [ Berlin ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_comma.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000036�11675050456�016240� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ 1 ], [ 2 ], [ 3 ] -> [ 4 ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_collapse.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000251�11675050456�016745� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A B C ] - A B C D -> [ X Z Y ] node.i { label: "S P A C Y"; } [ A ] { label: "S P A C E"; } --> { label: "S P A C E"; } [ B ] { class: i; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/9_flow_south.txt���������������������������������������������������������������0000644�0000764�0000764�00000000145�11675050456�017343� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { flow: down; } edge { start: south; } [A] --> [A1], [A2], [A3], [A4], [A5], [A6], [A7], [A8] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_nodes_edge.txt���������������������������������������������������������������0000644�0000764�0000764�00000000123�11675050456�017235� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ test ] { shape: edge; } [ A ] -- [ test ] [ C ] --> [ test ] [ test ] --> [ B ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_wrap.txt���������������������������������������������������������������������0000644�0000764�0000764�00000000265�11675050456�016117� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { textwrap: 6; } [ Frankfurt Oder\n Flughafen-Terminal ] -- Drive a car to the destination --> [ Small city near a beautyfull river ] { text-wrap: 10; align: right; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/���������������������������������������������������������������������������0000755�0000764�0000764�00000000000�12150110221�014717� 5����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/2_ignore.dot���������������������������������������������������������������0000644�0000764�0000764�00000000211�11675050456�017155� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������strict digraph G { node [ pname=123, foo=9, bar=baz ] node1 [ fname=o ] node2 [ bar=zup ] node1 -> node2 [ brabble=bramble ] } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/3_invis.dot����������������������������������������������������������������0000644�0000764�0000764�00000000130�11675050456�017023� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph G { node [ style=invis ] { node [ style=filled ] a; c; } a -> b -> c; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/7_record.dot���������������������������������������������������������������0000644�0000764�0000764�00000000315�11675050456�017162� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph gaph { graph [ labeljust=l, label="Left aligned label" ] "<f1> Aa | <f2> Bb | Cc" [ shape=rect ]; node [ shape=record ] a [ label = "<f1> AA | <f2> BB | CC" ] "<f1> A | <f2> B | C"; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/3_empty_record_LR.dot������������������������������������������������������0000644�0000764�0000764�00000000136�11675050456�020772� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph G { rankdir=LR; "Compositor" [shape="record", label="{Compositor||Compose()}"] } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/2_bool.dot�����������������������������������������������������������������0000644�0000764�0000764�00000000445�11675050456�016636� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph G { node [ center compound concentrate constraint decorate diredgeconstraints fixedsize headclip labelfloat landscape mosek nojustify normalize overlap pack pin regular remincross root splines tailclip truecolor ] "Bonn" -> "Berlin" } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/2_linewidth.dot������������������������������������������������������������0000644�0000764�0000764�00000000223�11675050456�017664� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph G {size="11,8.5"; ratio=fill; overlap=false; node[fontsize=10,shape=plaintext];edge[dir=none,style="setlinewidth(0.1)"]; "123"->"foo"[]; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/9_tree.dot�����������������������������������������������������������������0000644�0000764�0000764�00000000363�11675050456�016650� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph G { node [ shape=record ]; node0 [label = "<f0> |<f1> A|<f2> "]; node1 [label = "<f0> |<f1> B|<f2> "]; node2 [label = "<f0> |<f1> C|<f2> "]; "node0":f0 -> "node1":f1; "node1":f0 -> "node2":f1; "node0":f2 -> "node2":f2; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/4_html_like.dot������������������������������������������������������������0000644�0000764�0000764�00000000412�11675050456�017647� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# these are all invalid HTML-like labels. The should not result # in errors and the "<>" should be stripped, leaving only the inner # text as the label text: digraph G { first [ label = <> ] second [ label =<> ] third [ label =< > ] fourth [ label = <a> ] } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/10_numbers.dot�������������������������������������������������������������0000644�0000764�0000764�00000000220�11675050456�017424� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph G { // test parsing of numbers as node names 0.99 -> 12.88 -100 -> 123.1230 00019.1001 -> 1000. .99 -> 0. .1 -> -1. } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/3_output_lone.dot����������������������������������������������������������0000644�0000764�0000764�00000000126�11675050456�020255� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph G { output = html; c:w; /* a node named "c" with port "w" */ a -> b; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/2_comment_inside_attr.dot��������������������������������������������������0000644�0000764�0000764�00000000116�11675050456�021725� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph G { a [ label = "a" /* color=red */ ] a -> b; } /* digraph */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/2_graph_label_bottom.dot���������������������������������������������������0000644�0000764�0000764�00000000136�11675050456�021524� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph GRAPH_0 { graph [ labelloc=b, label="Deutsche Städte" ] "Köln" -> "Stralsund" } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/5_scopes_uni.dot�����������������������������������������������������������0000644�0000764�0000764�00000000052�11675050456�020047� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph GRAPH_0 { a -- { b c d } -- o } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/5_scope_atr.dot������������������������������������������������������������0000644�0000764�0000764�00000000147�11675050456�017664� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph GRAPH_0 { b [ style=rounded ] a -> { node [ shape=plaintext ] b c d } { b c d } -> u } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/2_no_spaces.dot������������������������������������������������������������0000644�0000764�0000764�00000000033�11675050456�017646� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������strict graph{bonn--berlin} �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/3_ids.dot������������������������������������������������������������������0000644�0000764�0000764�00000000041�11675050456�016453� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph G { 123abc -> test } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/4_uppercase.dot������������������������������������������������������������0000644�0000764�0000764�00000000161�11675050456�017667� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������STRICT DIGRAPH "graph" { EDGE [ label=foo ] NODE [ style=dotted ] SUBGRAPH cluster_me { a -> b } c -> d } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/3_empty_record.dot���������������������������������������������������������0000644�0000764�0000764�00000000120�11675050456�020366� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph G { "Compositor" [shape="record", label="{Compositor||Compose()}"] } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/4_record.dot���������������������������������������������������������������0000644�0000764�0000764�00000000236�11675050456�017161� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph G { node1 [ label = "<a1> test | <a2> split", shape=record ] node2 [ label = "<b1> record | <b2> test", shape=record ] node1:a1 -> node2:b2 } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/2_strict.dot���������������������������������������������������������������0000644�0000764�0000764�00000000055�11675050456�017210� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������strict digraph G { Wolfsbüttel -> Köln } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/6_2_cluster.dot������������������������������������������������������������0000644�0000764�0000764�00000000264�11675050456�017610� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph G { subgraph cluster_first { a -> b; label=first; } subgraph cluster_second { p -> q -> r -> s label=second; s -> p; } r -> a [ style=bold ]; b -> q; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/3_graph_label_long.dot�����������������������������������������������������0000644�0000764�0000764�00000000157�11675050456�021163� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph GRAPH_0 { graph [ labelloc=b, label="Deutsche Städte am Rhein" ] "Köln" -> "Bonn" -> "Koblenz" } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/5_scopes_chain.dot���������������������������������������������������������0000644�0000764�0000764�00000000052�11675050456�020336� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph GRAPH_0 { a -> { b c d } -> o } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/16_split.dot���������������������������������������������������������������0000644�0000764�0000764�00000000454�11675050456�017123� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph SPLIT { 0 [ shape=record, label="<f1>nil| | | <f2> 0" ] 1 [ shape=record, label="<f1>nil| | | <f2> 1" ] 2 [ shape=record, label="<f1>nil| | | <f2> 2" ] 3 [ shape=record, label="<f1>nil| | | <f2> 3" ] "0":f1 -> "1":f2; "0":"f2" -> "1":"f1"; 2:"f1" -> 3:"f2"; 2:f2 -> 3:f1; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/2_group_labelloc.dot�������������������������������������������������������0000644�0000764�0000764�00000000247�11675050456�020674� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph GRAPH_0 { subgraph "cluster0" { label="Bergtour:"; style="filled,dashed"; labelloc=bottom; labeljust=l; Zugspitze -> Wasserkuppe } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/9_stacking.dot�������������������������������������������������������������0000644�0000764�0000764�00000000211�11675050456�017504� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph G { graph [rankdir=LR]; "11" -> "1" "1" -> "2" "2" -> "3" "1" -> "10" "3" -> "8" "8" -> "5" "5" -> "6" "10" -> "6" "0" -> "11" } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/2_ports.dot����������������������������������������������������������������0000644�0000764�0000764�00000000103�11675050456�017041� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph ports { north -> south [ headport=west tailport=east ] } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/4_cluster_labeljust.dot����������������������������������������������������0000644�0000764�0000764�00000000325�11675050456�021430� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph G { subgraph cluster_0 { aaaaaaaaaaaaaaaaaaa -> b; labeljust=r; label="Right aligned"; } subgraph cluster_1 { c -> ddddddddddddddd; labeljust=l; label="Left aligned"; } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/4_strings.dot��������������������������������������������������������������0000644�0000764�0000764�00000000753�11675050456�017400� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������strict digraph graph0 { // test continuation lines (ending in \) berlin [ label="Berlin\ Ost" ] bonn [ label="Bonn\ am\ Rhein" ] // test double quoted string continuation "foo" + "bar" in attributes bonn -> berlin [ label="train" + " (ICE)" ] // and nodes "bonn" + "test" [ label="Bonn" + " (Rhei \ n)" ] // test more than one concat, more than one space plus empty parts "Cottbus" [ label= "Cot" + "" + "tb" +"" +"" + "us" ] "bonntest" -> "Cottbus" } ���������������������Graph-Easy-0.73/t/in/dot/6_group_align.dot����������������������������������������������������������0000644�0000764�0000764�00000000203�11675050456�020205� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( Left: [ A ] -> [ B ]) { align: left; } ( Right: [ C ] -> [ D ]) { align: right; } ( Center: [ E ] -> [ F ]) { align: center; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/5_scopes.dot���������������������������������������������������������������0000644�0000764�0000764�00000000067�11675050456�017202� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph GRAPH_0 { a -> { b c d } { b c d } -> u } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/2_setlinewidth.dot���������������������������������������������������������0000644�0000764�0000764�00000000167�11675050456�020407� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph { "Bonn" [ style="setlinewidth(4)" ] "Berlin" [ style="filled, setlinewidth(5)" ] "Bonn" -> "Berlin" } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/3_colors.dot���������������������������������������������������������������0000644�0000764�0000764�00000000646�11675050456�017210� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Differen ways to write colors, all of them should result in "red", # except "/accent4/4": digraph G { subgraph cluster_1 { pencolor="#ff 00 00"; # "//" means empty color scheme and is equivalen to "" node [ color=red fontcolor="//red" ] A -> B [ color="#ff0000ff" ] } C [ color="0 1.0 1.0" fontcolor="/x11/red" ] B -> C [ color="1.0,1.0,1.0" ] // results in "#ffff99" B [ color="/accent4/4" ] } ������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/0_empty.dot����������������������������������������������������������������0000644�0000764�0000764�00000000075�11675050456�017036� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������strict/*comment*/digraph/*comment*/{ // test comments } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/4_compass.dot��������������������������������������������������������������0000644�0000764�0000764�00000000415�11675050456�017347� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Test compass directions and port names // Note: This graph does not have a name, as the ID is optional digraph { bonn [ label="<N> bonn N | <S> S" shape=record] berlin [ label="<N> berlin N | <w> w" shape=record] bonn:S -> berlin:w bonn:s -> berlin:N:w } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/2_nospace.dot��������������������������������������������������������������0000644�0000764�0000764�00000000042�11675050456�017324� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph mike{ mike -> michael } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/9_edge_styles.dot����������������������������������������������������������0000644�0000764�0000764�00000001227�11675050456�020220� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# dot -Tpng 9_edge_styles.dot -o 9_edge_styles.png // various edge styles and misc. attributes digraph G { node [style=filled, color=lightsalmon, fontsize=14, fontname="Helvetica"]; edge [style="setlinewidth(3)"]; margin="0"; ratio=auto; nodesep=0.2; ranksep=0.2; clusterrank=local; A -> B [ style=solid ] B -> C [ style=invis ] C -> D [ style=dotted ] D -> E [ style=dashed ] // bold-dash A -> F [ style="setlinewidth(3), dashed" ] // setlinewidth(3) => bold F -> G [ style="setlinewidth(3)" ] // setlinewidth(5) => broad G -> H [ style="setlinewidth(5)" ] // setlinewidth(11) => wide H -> I [ style="setlinewidth(11)" ] } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/9_back.dot�����������������������������������������������������������������0000644�0000764�0000764�00000000227�11675050456�016610� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph G { graph [rankdir=LR]; "11" -> "1" "1" -> "2" "2" -> "3" "1" -> "10" "3" -> "8" "8" -> "5" [ dir=back; ] "5" -> "6" "10" -> "6" "0" -> "11" } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/3_node_label.dot�����������������������������������������������������������0000644�0000764�0000764�00000000204�11675050456�017761� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph Graphname { graph [ label="\G" ] node [ label="\G \N" ] 0 -> 1 [ label="\E \G \T \H" ] 2 [ label="Name: \N \G" ] } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/6_comments.dot�������������������������������������������������������������0000644�0000764�0000764�00000000422�11675050456�017527� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* this is a * multi line * comment */ /* comment */ digraph /* one comment */ GRAPH_0 /* another */ /* comment */ { // comment // comment a -> /* comment */ { b c /* comment */ d } { b /* comment */ c d } /* comment */ -> u " my node /* not a comment */ " -> b } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/dot/2_square_bracket_in_attr.dot�����������������������������������������������0000644�0000764�0000764�00000000062�11675050456�022411� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������digraph G { a [ label = < ] > ] a -> b; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/8_invisible.txt����������������������������������������������������������������0000644�0000764�0000764�00000001027�11675050456�017135� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { border: 1px solid black; background: oldlace; label: My sample graph; } edge { label-color: green; color: blue; style: invisible; } [ One ] { fill: seagreen; color: white; } -- label --> [ Two ] { shape: triangle; } [ One ] => [ Three ] [ Five ] { fill: maroon; color: yellow; } <=> [ Three ] [ One ] .. Test\n label ..> [ Four ] [ Three ] <.. Test label ..> [ Six ] [ Five ] - Test label - > { label-color: darkslategrey; color: red; } [ Seven ] [ Seven ] -- [ Eight ] [ Five ] --> [ Eight ] [ Five ] --> [ Seven ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_group_labelpos.txt�����������������������������������������������������������0000644�0000764�0000764�00000000112�11675050456�020152� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( Bergtour: [ Zugspitze ] --> [ Wasserkuppe ] ) { labelpos: bottom; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_endless_loop_2.txt�����������������������������������������������������������0000644�0000764�0000764�00000000071�11675050456�020052� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ 1 ] -> [ 2 ] [ 3 ] [ 4 ] { origin: 3; offset: 1,0; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/6_multicell_offset.txt���������������������������������������������������������0000644�0000764�0000764�00000000346�11675050456�020512� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ Wide ] { size: 4,1; } -> [ B ] { offset: 2,0; origin: Wide; } [ Wide ] -> [ C ] { offset: -2,0; origin: Wide; } [ High ] { size: 1,4; } -> [ D ] { offset: 0,2; origin: High; } [ High ] -> [ E ] { offset: 0,-2; origin: High; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/6_group_align.txt��������������������������������������������������������������0000644�0000764�0000764�00000000203�11675050456�017450� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( Left: [ A ] -> [ B ]) { align: left; } ( Right: [ C ] -> [ D ]) { align: right; } ( Center: [ E ] -> [ F ]) { align: center; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/25_autosplit_empty.txt���������������������������������������������������������0000644�0000764�0000764�00000000251�11675050456�020470� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ | C | ] [ | D | ] [ | E | ] [ | F | ] [|G|] [ |H| |] [ C.2 ] -> [ A1 ] [ D.2 ] -> [ A2 ] [ E.2 ] -> [ A3 ] [ F.2 ] -> [ A4 ] [ G.2 ] -> [ A5 ] [ H.3 ] -> [ A6 ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/8_align.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000777�11675050456�016256� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { label: My Graph; font-size: 2em; } node.left { align: left; font-size: 1.5em; } edge { align: right; } ( Nodes: [ Right\nAligned ] { align: right; } -- label\n text --> { align: right; } [ Left\naligned ] { class: left; }) { align: right; } [ Center\n aligned text] -- aligned\n left --> { align: left; } [ Rightmost ] { align: right; } [ B ] --> [ Left\n align\n no border ] { border: none; align: left; } --> [ center\n align\n no border ] { border: none; align: center; } --> [ A ] �Graph-Easy-0.73/t/in/4_2x2nodes.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000105�11675050456�016605� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ Hamburg ] --> [ Altona ] [ Frankfurt ] --> [ Hof ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_minlen.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000105�11675050456�016423� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ Berlin ] --> { minlen: 4; } [ Potsdam ], [ Cottbus ], [ Leipzig ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/1node.txt����������������������������������������������������������������������0000644�0000764�0000764�00000000010�11675050456�015717� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ One ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_invisible.txt����������������������������������������������������������������0000644�0000764�0000764�00000000266�11675050456�017135� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bischofswerda ] -> [ You don't see me! ] { shape: invisible; } -> [ Borna ] { shape: point; point-style: invisible; } -> [ Bremen ] [ You don't see me! ] -> [ Bischofswerda ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_autolabel.txt����������������������������������������������������������������0000644�0000764�0000764�00000000253�11675050456�017113� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { autolabel: name, 20; autotitle: name; } [ Bonn ] { label: Bonn (ehemalige Bundeshauptstadt); } -- Acme Travels Incorporated --> [ Frankfurt (Main) / Flughafen ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_joint_short.txt��������������������������������������������������������������0000644�0000764�0000764�00000000162�11675050456�017505� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ B ] { offset: 2,0; origin: A; }, [ A ] --> { start: south; end: south, 0; } [ C ] { offset: 2,-2; origin: B; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_flow.txt���������������������������������������������������������������������0000644�0000764�0000764�00000000202�11675050456�016106� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Passau ] -> { flow: down; } [ Siegen ] -> { flow: right; } [ Regensburg ] [ Siegen ] -> { flow: left; } [ Aschaffenburg ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_lists.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000040�11675050456�016274� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bonn ], [ Ulm ] -> [ Berlin ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_list_attr.txt����������������������������������������������������������������0000644�0000764�0000764�00000000111�11675050456�017141� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bonn ] { border-style: dotted; }, [ Berlin ] { border-style: dashed; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/5_tree_joint.txt���������������������������������������������������������������0000644�0000764�0000764�00000000420�11675050456�017304� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ A ] [ A ] -> { start: south, 0; } [ A1 ] { origin: A; offset: 2,1; } [ A ] -> { start: south, 0; } [ A2 ] { origin: A; offset: 2,2; } [ A ] -> { start: south, 0; } [ A3 ] { origin: A; offset: 2,3; } [ A ] -> { start: south, 0; } [ A4 ] { origin: A; offset: 2,4; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_edge_cross.txt���������������������������������������������������������������0000644�0000764�0000764�00000000124�11675050456�017257� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[E1] --> [S1] [E1] --> [S2] [E1] --> [E2] [E2] --> [S1] [E2] --> [S2] [S1] --> [S2] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3nodes.txt���������������������������������������������������������������������0000644�0000764�0000764�00000000052�11675050456�016112� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ One ] ==> [ Two ] [ Two ] - > [ Three ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_newlines.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000106�11675050456�016764� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ Berlin\n (W) ] -> [ Berlin\n (O) ] { border: dotted 1px black; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_autosplit_escaped.txt��������������������������������������������������������0000644�0000764�0000764�00000000020�11675050456�020643� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ A \| B | C ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_invisible_left.txt�����������������������������������������������������������0000644�0000764�0000764�00000000022�11675050456�020133� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[] --> [ Berlin ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_list_attr.txt����������������������������������������������������������������0000644�0000764�0000764�00000000177�11675050456�017156� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bonn ] { border-style: dotted; }, [ Berlin ] { border-style: dashed; } -- test --> [ Frankfurt ] { border-style: dot-dash; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_bend_bug.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000232�11675050456�016707� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Berlin ] { offset: 0,2; origin: Bonn; } [ Frankfurt ] { offset: 2,2; origin: Bonn; } [ Ulm ] { offset: 2,0; origin: Bonn; } [ Bonn ] --> [ Frankfurt ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/5_rounded.txt������������������������������������������������������������������0000644�0000764�0000764�00000000307�11675050456�016606� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������node { shape: rounded; } [ Pullach ] { border: dotted; } -> [ Parsing ] { border: dashed; } -> [ Pankow\n (Berlin) ] { border: double; } -> [ Pullheim ] { border: bold; } -> [ Paderborn ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/8_flow.txt���������������������������������������������������������������������0000644�0000764�0000764�00000000435�11675050456�016122� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ East ] -> [ South ] { flow: south; } -> [ West ] -> { flow: west; } [ South Too ] --> { flow: down; } [ right (west) ] --> { flow: right; } [ left (south) ] --> { flow: left; } [ east (default) ] -> [ final ] #[ east (default) ] -> [ second ] #[ east (default) ] -> [ third ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_edge_types.txt���������������������������������������������������������������0000644�0000764�0000764�00000000116�11675050456�017273� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bingen ] <--> [ Mainz ] [ Bischofswerde ] -- test-text --> [ Finsterwalde ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/10_repair.txt������������������������������������������������������������������0000644�0000764�0000764�00000001126�11675050456�016504� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( DMZ: [ Backend ] [ Database ] [ Proxy ] [ Server ] ) [ Proxy ] --> [ Check ] [ Proxy ] --> { flow: south; } [ Database ] [ Proxy ] --> [ Server ] [ Check ] --> [ Backend ] [ Database ] --> [ Backend ] ( DMZ1: [ 1Backend ] [ 1Database ] [ 1Proxy ] [ 1Server ] ) [ 1Proxy ] --> [ 1Check ] [ 1Proxy ] --> { flow: south; } [ 1Database ] [ 1Proxy ] --> [ 1Server ] [ 1Check ] --> [ 1Backend ] [ 1Database ] --> [ 1Backend ] [ 1Proxy ] --> [ 1Check ] [ 1Proxy ] --> { flow: south; } [ 1Database ] [ 1Proxy ] --> [ 1Server ] [ 1Check ] --> [ 1Backend ] [ 1Database ] --> [ 1Backend ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_joining.txt������������������������������������������������������������������0000644�0000764�0000764�00000000220�11675050456�016573� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A ] --> { start: south,0; } [ B ] [ A ] --> { start: north,0; } [ B ] [ A ] --> { start: south,0; } [ C ] [ A ] --> { start: north,0; } [ C ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_label.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000103�11675050456�016214� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������node { label: A; } [ B ] -> [ C ] { link: http://bloodgate.com; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_selfloop_flow_down.txt�������������������������������������������������������0000644�0000764�0000764�00000000171�11675050456�021043� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { flow: 180; } [ Start ] --> [ Main ] [ Main ] -- Until not done --> [ Main ] [ Main ] -- Until done --> [ Main ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/0_empty_group.txt��������������������������������������������������������������0000644�0000764�0000764�00000000027�11675050456�017512� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������(Group without a name) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/8_endless_loop.txt�������������������������������������������������������������0000644�0000764�0000764�00000000074�11675050456�017640� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ 1 ], [ 2 ] -> [ 3 ] [ 1 ] -> [ 2 ] [ 3|4 ] [ 5|6|7 ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/18_multiples.txt���������������������������������������������������������������0000644�0000764�0000764�00000000732�11675050456�017252� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������node { background: yellow; } [ Bonn ] ..> [ Berlin ] -> [ Kassel ] [ Bonn ] .-> [ Koblenz ] [ Bonn ] -> [ Ulm ] -> [ Koblenz ] [ Ulm ] -> [ Bautzen ] -> [ Berlin ] [ 1Bonn ] ..> [ 1Berlin ] -> [ 1Kassel ] [ 1Bonn ] .-> [ 1Koblenz ] [ 1Bonn ] -> [ 1Ulm ] -> [ 1Koblenz ] [ 1Ulm ] -> [ 1Bautzen ] -> [ 1Berlin ] [ 2Bonn ] ..> [ 2Berlin ] -> [ 2Kassel ] [ 2Bonn ] .-> [ 2Koblenz ] [ 2Bonn ] -> [ 2Ulm ] -> [ 2Koblenz ] [ 2Ulm ] -> [ 2Bautzen ] -> [ 2Berlin ] ��������������������������������������Graph-Easy-0.73/t/in/5_group_split.txt��������������������������������������������������������������0000644�0000764�0000764�00000000210�11675050456�017506� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ( test: [ Bonn ] -> [ Berlin ] -> [ Frankfurt ] { flow: down; } -> [ Hagen ] ) [ Bonn ] -> [ Ulm ] { origin: Bonn; offset: 6,2; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/10borders.txt������������������������������������������������������������������0000644�0000764�0000764�00000000540�11675050456�016522� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Different border styles [ Solid ] ..> [ Dotted ] { border: dotted; } [ Dashed ] { border: dashed; } ==> [ none ] { border: none; } [ dot-dash ] { border: dot-dash; } - > [ Bold ] { border: bold; } [ dot-dot-dash ] { border: dot-dot-dash; } .-> [ wave ] { border: wave; } [ double-dash ] { border: double-dash; } ~~> [ Double ] { border: double; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/5_group_repair.txt�������������������������������������������������������������0000644�0000764�0000764�00000000324�11675050456�017643� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( DMZ: [ Backend ] [ Database ] [ Proxy ] [ Server ] ) [ Proxy ] --> [ Check ] [ Proxy ] --> { flow: south; } [ Database ] [ Proxy ] --> [ Server ] [ Check ] --> [ Backend ] [ Database ] --> [ Backend ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4nodes.txt���������������������������������������������������������������������0000644�0000764�0000764�00000000116�11675050456�016114� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bonn ] --> [ Berlin ] [ Bonn ] --> [ Frankfurt ] [ Berlin ] ..> [ Cottbus ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/9_cross.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000335�11675050456�016304� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ U ] --> [ A ] { origin: U; offset: 4,0; } --> [ B|I] { origin: A; offset: 32, 0; } [ BI.1] --> [ I ] -> [ Z ] [ C | D||E ] { origin: A; offset: 0,1; } #[ CDE.0 ] --> [ BI.0 ] [ U ] --> { start: north; } [ I ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_invisible_right.txt����������������������������������������������������������0000644�0000764�0000764�00000000021�11675050456�020315� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bonn ] --> [ ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_cross.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000247�11675050456�016301� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A ] --> { style: solid; } [ B ] { origin: A; offset: 40,0; } [ C ] { offset: 20,-20; origin: A; } --> { style: dashed; } [ D ] { origin: C; offset: 0,40; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/5_arrow_styles.txt�������������������������������������������������������������0000644�0000764�0000764�00000000751�11675050456�017706� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrow-style: none; } # the arrow is not drawn [ Mainau ] --> [ Unteruhlingen ] { border-style: dotted; } # there is no arrow, so the style is ignored [ Mainau ] -- { arrow-style: closed; } [ Unteruhlingen ] # these override the class default: [ Oberuhlingen ] <--> { arrow-style: closed; } [ Unteruhlingen ] [ Uhlingen ] <--> { arrow-style: filled; } [ Oberuhlingen ] # arrows are again not rendered [ Hagnau ] { flow: down; } <--> [ Uhlingen ] [ Hagnau ] <--> [ Oberuhlingen ] �����������������������Graph-Easy-0.73/t/in/8_basename.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000111�11675050456�016715� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A|B|C ] { basename: A } [ 1 ] -> [ A.2 ] [ A|B|C ] [ 2 ] -> [ ABC.2 ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_joint_bug_flags.txt����������������������������������������������������������0000644�0000764�0000764�00000000246�11675050456�020303� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ B ], [ C ], [ D ] <--> { start: west; end: south, 0; } [ E ] { origin: B; offset: -2,-2; } [ C ] { origin: B; offset: 0,2; } [ D ] { origin: C; offset: 0,2; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_cross_split_hor.txt����������������������������������������������������������0000644�0000764�0000764�00000000223�11675050456�020356� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ( Cross: [A],[B],[C],[D] ) [ A ] --> [ B ] { offset: 0,4; origin: A; } [ C ] { offset: -2,2; origin: A; } --> [ D ] { offset: 2,2; origin: A; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/1_selfloop_2.txt���������������������������������������������������������������0000644�0000764�0000764�00000000240�11675050456�017202� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Main ] -- Until not done --> [ Main ] [ Main ] -- Until not done --> [ Main ] [ Main ] -- Until not done --> [ Main ] [ Main ] -- Until not done --> [ Main ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/6_ranks.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000151�11675050456�016262� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A ] --> [ B ] --> [ C ] --> [ D ] { rank: 0; } --> [ E ] --> { end: north; } [ F ] { rank: 0; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/1_selfloop.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000024�11675050456�016761� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ One ] --> [ One ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/5_flow.txt���������������������������������������������������������������������0000644�0000764�0000764�00000000145�11675050456�016115� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ Bonn ] -> { flow: forward; } [ Berlin ] [ Bonn ] -> [ Cottbus ] --> [ Moselkern ] --> [ Ulm ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_cross_inv.txt����������������������������������������������������������������0000644�0000764�0000764�00000000253�11675050456�017152� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A ] --> { style: invisible; } [ B ] { origin: A; offset: 40,0; } [ C ] { offset: 20,-20; origin: A; } --> { style: dashed; } [ D ] { origin: C; offset: 0,40; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_dot_dot_dash.txt�������������������������������������������������������������0000644�0000764�0000764�00000000143�11675050456�017574� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Test\n Test\n Test test test\n test ] { border: dot-dot-dash; } ..-> [ B ] { border: dot-dash; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_bug_joint_2.txt��������������������������������������������������������������0000644�0000764�0000764�00000000302�11675050456�017341� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { arrow-style: none; } [ B ], [ C ], [ D ] <--> { start: west; end: south, 0; } [ E ] { origin: B; offset: -2,-2; } [ C ] { origin: B; offset: 0,2; } [ D ] { origin: C; offset: 0,2; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/7_star.txt���������������������������������������������������������������������0000644�0000764�0000764�00000000131�11675050456�016114� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ Dachau ] -> [ Berlin ], [ Ulm ], [ Frankfurt ], [ Chemnitz ], [ Weimar ], [ Kiel ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/9_chain.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000550�11675050456�016234� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# a chain with branches. Despite the branches defined first, # the output should create one chain with branches going up/down [ Bonn ] --> [ Berlin ] [ Berlin ] --> [ Koeln ] [ Berlin ] --> [ Cottbus ] [ Koeln ] --> [ Frankfurt ] [ Frankfurt ] --> [ Chemnitz ] [ Frankfurt ] --> [ Dresden ] [ Frankfurt ] --> [ Hannover ] [ Hannover ] --> [ Hamburg ] ��������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/6_joint.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000137�11675050456�016273� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ Hanau ], [ Hagen ], [ Hannover ], [ Hamburg ] --> { end: back, 0; } [ Bonn ] --> [ Prag ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/8_chain.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000514�11675050456�016233� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# a chain with branches. Despite the branches defined first, # the output should create one chain with branches going up/down [ Bonn ] --> [ Berlin ] [ Berlin ] --> [ Koeln ] [ Koeln ] --> [ Frankfurt ] [ Frankfurt ] --> [ Chemnitz ] [ Frankfurt ] --> [ Dresden ] [ Frankfurt ] --> [ Hannover ] [ Hannover ] --> [ Hamburg ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_edge_start.txt���������������������������������������������������������������0000644�0000764�0000764�00000000052�11675050456�017262� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ A ] --> { start: front; } [ B ], [ C ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_group.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000053�11675050456�016275� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( Test\n group [ Bonn ] -> [ Berlin ] ) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_colors.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000543�11675050456�016447� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { fill: rgb(0.1, 100, 10%); color: 3; # mention color "3" before colorscheme colorscheme: paired12; } [ Colors ] { fill: w3c/grey; } [ Preserve ] { color: cornflowerblue; } [ The ] { color: #ff00ff; } [ Preserve ] --> { color: rgb(33,44,55); } [ The ] [ The ] --> { color: hsv(1.0,1.0,0.5); } [ Colors ] { color: hsl(300, 1.0, 1.0); } �������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_lists.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000060�11675050456�016277� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bonn ], [ Ulm ] -> [ Berlin ], [ Frankfurt ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_class.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000127�11675050456�016250� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������node.second { border: double; } [ A ] { class: SECOND; } --> [ B ] { class: Second; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/7_cluster.txt������������������������������������������������������������������0000644�0000764�0000764�00000000066�11675050456�016633� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# 6-node cluster [ A | BCD | E | F || G | | H ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/6_chain_10_edges.txt�����������������������������������������������������������0000644�0000764�0000764�00000000271�11675050456�017700� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bonn ] -> [ Berlin ] -> [ Kassel ] [ Bonn ] -> [ Koblenz ] -> [ Berlin ] [ Bonn ] -> [ Ulm ] -> [ Koblenz ] [ Ulm ] -> [ Bautzen ] -> [ Berlin ] [ Ulm ] -> [ Bautzen ] -> [ Berlin ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/1_empty_group.txt��������������������������������������������������������������0000644�0000764�0000764�00000000057�11675050456�017516� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( Group ) { background: yellow; } [ Outside ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_dot.txt����������������������������������������������������������������������0000644�0000764�0000764�00000000240�11675050456�015725� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� # the following should not confuse the parser #digraph G { # a -> b #} # neither should this graph { label: // digraph G { } [ Kummersbach ] -> [ Düsburg ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_autosplit_offset.txt���������������������������������������������������������0000644�0000764�0000764�00000000126�11675050456�020536� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ 1 ] { size: 1,2; } [ 2|3 ] { origin: 1; offset: 2,1; } [ 1 ] -> [ 23.1 ] --> [ 3 ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_autosplit_empty.txt����������������������������������������������������������0000644�0000764�0000764�00000000037�11675050456�020407� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ 1 ] -> [ 23.1 ] [ 2| |3 ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_split_bug.txt����������������������������������������������������������������0000644�0000764�0000764�00000000036�11675050456�017132� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Some \[\] || Autosplit ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_joint.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000216�11675050456�016267� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ B ] { offset: 2,0; origin: A; size: 2,1; }, [ A ] --> { start: south; end: south, 0; } [ C ] { offset: 2,-2; origin: B; } [ B ] --> [ U ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_split_attribute.txt����������������������������������������������������������0000644�0000764�0000764�00000000204�11675050456�020356� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� node { color: silver; } [ Some || Autosplit || Node ] { basename: u; border: dashed|; color: red|blue; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/6_nested_groups.txt������������������������������������������������������������0000644�0000764�0000764�00000000141�11675050456�020024� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( Outer ( Inner 1 [ A ] -> [ B ] ) ( Inner 2 [ C ] -> [ D ] ) [ E ] -> [ F ] ) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_invisible_both.txt�����������������������������������������������������������0000644�0000764�0000764�00000000030�11675050456�020135� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[] --> [ Bonn ] --> [ ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_selfloop_flow_left.txt�������������������������������������������������������0000644�0000764�0000764�00000000152�11675050456�021026� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { flow: 270; } [ Start ] --> [ Main ] [ Main ] -- Until not done --> [ Main ] [ Main ] --> [ End ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/1_selfloop_label.txt�����������������������������������������������������������0000644�0000764�0000764�00000000063�11675050456�020123� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Freiburg ] -- Alle Jahre Wieder --> [ Freiburg ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/5_offsets_2.txt����������������������������������������������������������������0000644�0000764�0000764�00000000333�11675050456�017037� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A ] { origin: B; offset: 2,1; } [ B ] { origin: C; offset: 1,1; } [ C ] { origin: D; offset: 1,1; } [ D ] [ E ] --> [ D ] --> [ C ] [ B ] --> [ A ] [ D ] --> [ C ] [ B ] --> [ C ] [ B ] --> [ C ] [ A ] --> [ B ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/5_long_edge_labels.txt���������������������������������������������������������0000644�0000764�0000764�00000000177�11675050456�020420� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ A ] -> [ B ] -> [ C ] -> [ D ] -> [ E ] [ A ] -- very long edge label --> [ E ] -- another very long edge label --> [ A ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_graph_label.txt��������������������������������������������������������������0000644�0000764�0000764�00000000112�11675050456�017375� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { label: My Graph; label-pos: top; } [ Regensburg ] --> [ Passau ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_cluster_2.txt����������������������������������������������������������������0000644�0000764�0000764�00000000136�11675050456�017045� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Hamburg ] { size: 2,2; } --> [ Altona ] { rows: 2; columns: 3; } [ Hamburg ] --> [ Altona ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_bend_bug.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000163�11675050456�016711� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Berlin ] { offset: 2,0; origin: Bonn; } [ Frankfurt ] { offset: 2,2; origin: Bonn; } [ Bonn ] --> [ Frankfurt ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_edges.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000050�11675050456�016225� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ One ] --> [ Two ] [ One ] --> [ Two ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/8_labels.txt�������������������������������������������������������������������0000644�0000764�0000764�00000001005�11675050456�016407� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { border: 1px solid black; background: oldlace; label: My sample graph; } edge { label-color: green; color: blue; } [ One ] { fill: seagreen; color: white; } -- label --> [ Two ] { shape: triangle; } [ One ] => [ Three ] [ Five ] { fill: maroon; color: yellow; } <=> [ Three ] [ One ] .. Test\n label ..> [ Four ] [ Three ] <.. Test label ..> [ Six ] [ Five ] - Test label - > { label-color: darkslategrey; color: red; } [ Seven ] [ Seven ] -- [ Eight ] [ Five ] --> [ Eight ] [ Five ] --> [ Seven ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4groups.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000117�11675050456�016324� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ( German Cities [ Cuxhaven ] -> [ Bremen ] [ Hamburg ], [ Flensburg ] ) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/6_fanout.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000224�11675050456�016441� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Hello World ] --> [ middle ] { label: /dev/fanout; fill: #ccccff; } --> { start: front,0; } [ A ], [ B ], [ C ], [ D ] { label: Hello World!; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_endless_loop.txt�������������������������������������������������������������0000644�0000764�0000764�00000000070�11675050456�017630� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ 1 ] -> [ 2 ] { fill: red; } [ 3|4 ] { fill: red; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/gdl/���������������������������������������������������������������������������0000755�0000764�0000764�00000000000�12150110221�014677� 5����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/gdl/2_right_to_left.gdl��������������������������������������������������������0000644�0000764�0000764�00000000175�11675050456�020474� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph: { orientation: "right_to_left" node: { title: "A" } node: { title: "B" } edge: { source: "A" target: "B" } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/gdl/2_bottom_to_top.gdl��������������������������������������������������������0000644�0000764�0000764�00000000175�11675050456�020533� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph: { orientation: "bottom_to_top" node: { title: "A" } node: { title: "B" } edge: { source: "A" target: "B" } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/gdl/2_top_to_bottom.gdl��������������������������������������������������������0000644�0000764�0000764�00000000175�11675050456�020533� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph: { orientation: "top_to_bottom" node: { title: "A" } node: { title: "B" } edge: { source: "A" target: "B" } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/gdl/1_color_code.gdl�����������������������������������������������������������0000644�0000764�0000764�00000000342�11675050456�017746� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph: { title: "Graph of sub_105D40F" // contains ^M as line-ends and ^L[0-9]+ as color codes node: { title: "0" label: " 58sub_105D40F 31: mov edi, esi push ebp jz short loc_105D27B" vertical_order: 0 } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/gdl/2_left_to_right.gdl��������������������������������������������������������0000644�0000764�0000764�00000000175�11675050456�020474� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph: { orientation: "left_to_right" node: { title: "A" } node: { title: "B" } edge: { source: "A" target: "B" } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/0_empty_groups.txt�������������������������������������������������������������0000644�0000764�0000764�00000000013�11675050456�017670� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������()->()->() ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_bidi_astar.txt���������������������������������������������������������������0000644�0000764�0000764�00000000156�11675050456�017246� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bad Schandau ] { offset: 3,0; origin: Erfurt; } [ Erfurt ] { size: 2,3; } [ Erfurt ] <--> [ Bad Schandau ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_group_no_border.txt����������������������������������������������������������0000644�0000764�0000764�00000000075�11675050456�020332� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( Test\n group [ Bonn ] -> [ Berlin ] ) { border: none; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_nodes_5_edges.txt������������������������������������������������������������0000644�0000764�0000764�00000000312�11675050456�017644� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Testcase courtesy of Scott Beuker: [ Default Page ] -> [ Test Search ] [ Test Search ] -> [ Sign Up ] [ Default Page ] -> [ Sign Up ] [ Default Page ] ..> [ Main Page ] [ Sign Up ] -> [ Main Page ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_cache_bug.txt����������������������������������������������������������������0000644�0000764�0000764�00000000200�11675050456�017034� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A ] { flow: east; } [ B ] { offset: 2,2; origin: A; } (G [ A ] ) [ A ] -- C --> { start: east; end: north; } [ B ] -> [ ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/README�������������������������������������������������������������������������0000644�0000764�0000764�00000000302�11675050456�015034� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������These files are used as input by t/ascii.t, and the ASCII output is compared to the files in t/out. The subdirectories t/dot, t/in/dot, t/out/dot and t/txt/dot contain input files for t/gv.t. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/6_empty_row.txt����������������������������������������������������������������0000644�0000764�0000764�00000000051�11675050456�017170� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[A|| || B] [C|| || D] [AB.0]->[CD.0] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_group_align_center.txt�������������������������������������������������������0000644�0000764�0000764�00000000454�11675050456�021015� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { label: My Graph; font-size: 2em; } node.left { align: left; font-size: 1.5em; } edge { align: right; } ( Nodes: [ Right\nAligned ] { align: right; } -- label\n text --> { align: right; } [ Left\naligned ] { class: left; } --> [ Center\n aligned ] ) { align: center; border: none; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_list_attr.txt����������������������������������������������������������������0000644�0000764�0000764�00000000265�11675050456�017155� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bonn ] { border-style: dotted; }, [ Berlin ] { border-style: dashed; } -- test --> [ Frankfurt ] { border-style: dot-dash; }, [ Frankfurt (Oder) ] { border-style: dot-dot-dash; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_edge_labels.txt��������������������������������������������������������������0000644�0000764�0000764�00000000167�11675050456�017377� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bonn ] == train ==> [ Berlin ] .. bus ..> [ Potsdam ] [ Berlin ] .- bike .-> [ Ulm ] [ Bonn ] -- car --> [ Potsdam ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_selfloop.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000245�11675050456�016767� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { flow: 180; } [ Start ] --> [ Main ] [ Main ] -- Until not done --> [ Main ] [ Main ] -- Until not done --> [ Main ] [ Main ] -- Until not done --> [ Main ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4groups_class.txt��������������������������������������������������������������0000644�0000764�0000764�00000000233�11675050456�017510� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������node.city { color: #801010; } ( German Cities [ Cuxhaven ] -> [ Bremen ] [ Hamburg ], [ Flensburg ] ) { nodeclass: city; border-style: dot-dash; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_nodes_6_edges.txt������������������������������������������������������������0000644�0000764�0000764�00000000353�11675050456�017652� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Testcase courtesy of Scott Beuker: [ Default Page ] -> [ Test Search ] [ Test Search ] -> [ Sign Up ] [ Default Page ] -> [ Sign Up ] [ Default Page ] -> [ Main Page ] [ Sign Up ] -> [ Main Page ] [ Default Page ] -> [ Main Page ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/5_multicell.txt����������������������������������������������������������������0000644�0000764�0000764�00000000421�11675050456�017135� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { flow: up; } [ Client ] { origin: Proxy; offset: 0,2; } <--> [ Proxy ] { size: 3,2; } [ Client1 ] { origin: Proxy; offset: 0,-2; } <--> [ Proxy ] [ Client2 ] { origin: Proxy; offset: 2,0; } <--> [ Proxy ] [ Client3 ] { origin: Proxy; offset: -2,0; } <--> [ Proxy ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_empty_group.txt��������������������������������������������������������������0000644�0000764�0000764�00000000016�11675050456�017513� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������([A]->[B])[C] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_autosplit_hang.txt�����������������������������������������������������������0000644�0000764�0000764�00000000035�11675050456�020163� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ 1 ] -> [ 23.0 ] [ 2|3 ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_cluster_3.txt����������������������������������������������������������������0000644�0000764�0000764�00000000171�11675050456�017045� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Hamburg ] { size: 2,2; } --> [ Altona ] { rows: 2; columns: 3; } [ Hamburg ] --> [ Altona ] [ Hamburg ] --> [ Altona ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_corrupt.txt������������������������������������������������������������������0000644�0000764�0000764�00000000143�11675050456�016640� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ A ] { label: AB%00%0d%0a } --> [ B ] { label: AB%0aCB; } -> [ C ] { label: AB%be%f7%01%7f%91; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_node_edge.txt����������������������������������������������������������������0000644�0000764�0000764�00000000104�11675050456�017051� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[A ] -- [ test ] { shape: edge; } -- [ ] { shape: edge; } --> [ B ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_inherit.txt������������������������������������������������������������������0000644�0000764�0000764�00000000272�11675050456�016607� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������node { align: left; } node.right { align: right; } graph { flow: down; } [ Left ] --> [ Lefty, too ] --> [ Right ] [ Left ], [ Lefty, too ] { class: none; } [ Right ] { class: right; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_edge_repair.txt��������������������������������������������������������������0000644�0000764�0000764�00000000146�11675050456�017413� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( Router: [ Input ] --> [ Output ] ) [ Output ] ==> { start: south; end: north; } [ Network ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_zeros.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000070�11675050456�016302� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[0] -- 0 --> [10] { label: 0; } --> { label: 0; } [ 0 ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/7_tree.txt���������������������������������������������������������������������0000644�0000764�0000764�00000000310�11675050456�016101� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge { start: south, 0; } [ A ] [ A ] -> [ A1 ] { origin: A; offset: 2,1; } [ A ] -> [ A2 ] [ A ] -> [ A3 ] [ A2 ] -> [ A23 ] { origin: A2; offset: 2,1 } [ A2 ] -> [ A22 ] [ A2 ] -> [ A21 ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_classes.txt������������������������������������������������������������������0000644�0000764�0000764�00000000213�11675050456�016574� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������.red { color: red; } .green, .blue, group { color: blue; } [ Red ] { class: red; } -- red --> { class: red; } [ Black ] { class: green; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_bidi_endpoint.txt������������������������������������������������������������0000644�0000764�0000764�00000000124�11675050456�017747� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Bad Schandau ] { offset: 3,0; origin: Erfurt; } [ Erfurt ] <--> [ Bad Schandau ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/1_bidi_loop.txt����������������������������������������������������������������0000644�0000764�0000764�00000000134�11675050456�017100� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Hamm ] <--> [ Hamm ] [ Hamm ] <--> [ Hamm ] [ Hamm ] <--> [ Hamm ] [ Hamm ] <--> [ Hamm ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_edge_labels_from_class.txt���������������������������������������������������0000644�0000764�0000764�00000000135�11675050456�021601� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������edge.yes { label: Yes; } edge { label: MyLabel; } [ A ] --> { class: yes; } [ B ] --> [ C ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_flow.txt���������������������������������������������������������������������0000644�0000764�0000764�00000000111�11675050456�016103� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { flow: 90; } [ Left ] --> { end: left; start: left; } [ Right ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_long_labels.txt��������������������������������������������������������������0000644�0000764�0000764�00000000072�11675050456�017423� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ My\n long\n node\n name ] -- A\n long\n label --> [ B ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_flow.txt���������������������������������������������������������������������0000644�0000764�0000764�00000000164�11675050456�016114� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { flow: down; } [ Enschede ] { flow: left; } [ Enschede ] --> [ Bielefeld ] [ Bielefeld ] --> [ Wolfsburg ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2nodes.txt���������������������������������������������������������������������0000644�0000764�0000764�00000000024�11675050456�016110� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ One ] ==> [ Two ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_selfloop.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000126�11675050456�016766� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Start ] --> [ Main ] [ Main ] -- Until not done --> [ Main ] [ Main ] --> [ End ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_autosplit_class.txt����������������������������������������������������������0000644�0000764�0000764�00000000177�11675050456�020363� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� node.B { border: dotted; } node.C { border: dashed; } node { border: double; } [ A|B ] { class: |B; } [ C|D ] { class: C|; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/5_offsets.txt������������������������������������������������������������������0000644�0000764�0000764�00000000201�11675050456�016610� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A ] { origin: B; offset: 2,1; } -> [ B ] { origin: C; offset: 1,1; } -> [ C ] { origin: D; offset: 1,1; } -> [ D ] -> [ E ] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_selfloop_flow_up.txt���������������������������������������������������������0000644�0000764�0000764�00000000150�11675050456�020516� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { flow: 0; } [ Start ] --> [ Main ] [ Main ] -- Until not done --> [ Main ] [ Main ] --> [ End ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_group_multicell.txt����������������������������������������������������������0000644�0000764�0000764�00000000124�11675050456�020346� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ( Some group: [ Frankfurt a. Main\n (Flughafen) ] { size: 2,2; } -> [ Berlin ] ) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_cluster.txt������������������������������������������������������������������0000644�0000764�0000764�00000000103�11675050456�016616� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Hamburg ] { size: 2,2; } --> [ Altona ] { rows: 2; columns: 3; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_autosplit_shape.txt����������������������������������������������������������0000644�0000764�0000764�00000000064�11675050456�020351� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A|B ] { shape: |none; } [ C|D ] { shape: none|; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/6_autosplit_class.txt����������������������������������������������������������0000644�0000764�0000764�00000000445�11675050456�020363� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# test that columns on a class works, also test that B is # properly offset from A (A with the label is 2 columns # wide!) and displays "D" as label. node.red { color: red; label: D; columns: 2; } [ A|B|C ] { basename: A; } [ 1 ] -> [ A.0 ] { class: red; } [ 2 ] -> [ A.0 ] [ 2 ] -> [ 3 ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_cross_split.txt��������������������������������������������������������������0000644�0000764�0000764�00000000223�11675050456�017506� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ( Cross: [A],[B],[C],[D] ) [ A ] --> [ B ] { offset: 4,0; origin: A; } [ C ] { offset: 2,-2; origin: A; } --> [ D ] { offset: 2,2; origin: A; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/1_undirected_loop.txt����������������������������������������������������������0000644�0000764�0000764�00000000045�11675050456�020320� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[A] -- [A ] -- [ A ] -- [ A ] -- [A] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/8_points.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000415�11675050456�016465� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������node { shape: point; } [ A ] -> [ B ] { point-style: square; } -> [ C ] { point-style: dot; } -> [ D ] { point-style: circle; } -> [ E ] { point-style: diamond; } -> [ F ] { point-style: cross; } -> [ G ] { shape: invisible; } -> [ H ] { point-style: star; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/6_multicell.txt����������������������������������������������������������������0000644�0000764�0000764�00000000246�11675050456�017143� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A ] { size: 2,2; } -> [ B ] { rows: 2; } -> [ C ] { columns: 3; } [ A ] -> [ B ] -> [ C ] -> [ D ] [ D ] -> [ C ] [ B ] -> [ C ] [ A ] -> [ F ] [ A ] -> [ G ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/6_split_join_loop.txt����������������������������������������������������������0000644�0000764�0000764�00000000152�11675050456�020350� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ A ] --> [ B ] --> [ C ] --> { start: front, 0; } [ X ], [ Y ] --> { end: back, 0; } [ I ] --> [ A ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/5_joint_bug2.txt���������������������������������������������������������������0000644�0000764�0000764�00000000362�11675050456�017211� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ A ] { flow: south; size: 5,1; } <--> [ B ], [ C ], [ D ] { origin: A; } <--> { start: south; end: south, 0; } [ E ] { origin: A; offset: 2,0; } [ B ] { offset: 0,2; } [ C ] { origin: B; offset: 2,0; } [ D ] { origin: C; offset: 2,0; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/5_a-star_bug.txt���������������������������������������������������������������0000644�0000764�0000764�00000000257�11675050456�017176� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ 1 ] { size: 1,2; } [ 2 ] { offset: 2,0; origin: 1; } [ 3 ] { offset: 1,0; origin: 2; } [ 4 ] { offset: 1,0; origin: 3; } [ 5 ] { offset: 0,-1; origin: 1; } [ 4 ] --> [ 1 ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/5_joint_label.txt��������������������������������������������������������������0000644�0000764�0000764�00000000145�11675050456�017430� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ Aachen ], [ Cottbus ], [ Buxtehude ], [ Dachau ] -- travel --> { end: back, 0 ; } [ Zittau ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/3_joint.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000205�11675050456�016264� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ Potsdam ], [ Mannheim ] --> { end: back,0; } [ Weimar ] # [ Weimar ] --> { start: front,0; } [ Finsterwalde ], [ Aschersleben ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/4_near.txt���������������������������������������������������������������������0000644�0000764�0000764�00000000137�11675050456�016073� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# near placement [ Bonn ] --> [ Berlin ] [ Berlin ] --> [ Koeln ] [ Essen ] --> [ Berlin ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/2_autosplit_empty.txt����������������������������������������������������������0000644�0000764�0000764�00000000030�11675050456�020376� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ Siegen | Siegburg ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/in/5_joint.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000475�11675050456�016277� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [ Hannover ] { flow: down; } --> { start: south; } [ Aachen ], [ Berlin ], [ Cuxhaven ] --> { start: south; end: south, 0 } [ Zwickau ] { origin: Hannover; offset: 2,-2; } [ Aachen ] { origin : Hannover; offset: 0,2; } [ Berlin ] { origin : Aachen; offset: 2,0; } [ Cuxhaven ] { origin : Aachen; offset: 4,0; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/astar.t���������������������������������������������������������������������������0000644�0000764�0000764�00000007341�11675050456�015057� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use Test::More; use strict; BEGIN { plan tests => 21; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy") or die($@); }; can_ok ("Graph::Easy", qw/ new _find_path_astar _astar_distance _astar_modifier _astar_edge_type /); can_ok ("Graph::Easy::Heap", qw/ new extract_top add /); use Graph::Easy::Edge::Cell qw/ EDGE_N_E EDGE_N_W EDGE_S_E EDGE_S_W EDGE_HOR EDGE_VER /; ############################################################################# # _distance tests my $dis = 'Graph::Easy::_astar_distance'; my $mod = 'Graph::Easy::_astar_modifier'; my $typ = 'Graph::Easy::_astar_edge_type'; { no strict 'refs'; is (&$dis( 0,0, 3,0 ), 3 + 0 + 0, '0,0 => 3,0: 4 (no corner)'); is (&$dis( 3,0, 3,5 ), 0 + 5 + 0, '3,0 => 3,5: 5 (no corner)'); is (&$dis( 0,0, 3,5 ), 3 + 5 + 1, '0,0 => 3,5: 3+5+1 (one corner)'); is (&$mod( 0,0 ), 1, 'modifier(0,0) is 1'); is (&$mod( 0,0, 1,0, 0,1 ), 7, 'going round a bend is 7'); is (&$mod( 0,0, 1,0, -1,0 ), 1, 'going straight is 1'); is (&$typ( 0,0, 1,0, 2,0 ), EDGE_HOR, 'EDGE_HOR'); is (&$typ( 2,0, 3,0, 4,0 ), EDGE_HOR, 'EDGE_HOR'); is (&$typ( 4,0, 3,0, 2,0 ), EDGE_HOR, 'EDGE_HOR'); is (&$typ( 2,0, 2,1, 2,2 ), EDGE_VER, 'EDGE_VER'); is (&$typ( 2,2, 2,3, 2,4 ), EDGE_VER, 'EDGE_VER'); is (&$typ( 2,2, 2,1, 2,0 ), EDGE_VER, 'EDGE_VER'); is (&$typ( 0,0, 1,0, 1,1 ), EDGE_S_W, 'EDGE_S_W'); is (&$typ( 1,1, 1,0, 0,0 ), EDGE_S_W, 'EDGE_S_W'); is (&$typ( 1,1, 1,0, 2,0 ), EDGE_S_E, 'EDGE_S_E'); is (&$typ( 2,0, 1,0, 1,1 ), EDGE_S_E, 'EDGE_S_E'); is (&$typ( 0,0, 1,0, 1,-1 ), EDGE_N_W, 'EDGE_N_W'); is (&$typ( 1,-1, 1,0, 0,0 ), EDGE_N_W, 'EDGE_N_W'); #print &$typ( 1,2, 2,2, 2,1),"\n"; #print &$typ( 0,2, 1,2, 2,2),"\n"; #print &$typ( 0,1, 0,2, 1,2),"\n"; } exit; ############################################################################# # path finding tests my $graph = Graph::Easy->new(); is (ref($graph), 'Graph::Easy'); is ($graph->error(), '', 'no error yet'); my $node = Graph::Easy::Node->new( name => 'Bonn' ); my $node2 = Graph::Easy::Node->new( name => 'Berlin' ); my $cells = {}; place($cells, $node, 0, 0); place($cells, $node2, 3, 0); #my $path = $graph->_find_path_astar( $cells, $node, $node2 ); #is_deeply ($path, [ 0,0, 1,0, 2,0, 3,0 ], '0,0 => 1,0 => 2,0 => 3,0'); place($cells, $node, 0, 0); place($cells, $node2, 3, 1); #$path = $graph->_find_path_astar( $cells, $node, $node2 ); #is_deeply ($path, [ 0,0, 1,0, 2,0, 3,0, 3,1 ], '0,0 => 1,0 => 2,0 => 3,0 => 3,1'); $cells = {}; place($cells, $node, 5, 7); $node2->{cx} = 2; $node2->{cy} = 2; place($cells, $node2, 14, 14); block ($cells,13,14); block ($cells,14,13); block ($cells,13,15); block ($cells,15,13); block ($cells,14,16); block ($cells,16,14); #block ($cells,3,11); #block ($cells,3,10); #block ($cells,4,9); #block ($cells,5,9); #block ($cells,5,11); #block ($cells,5,13); #for (5..15) # { # block ($cells,15,$_); # block ($cells,$_,5); # block ($cells,$_,15); # } #block ($cells,15,16); #block ($cells,14,17); #block ($cells,3,16); $graph->{cells} = $cells; $graph->{_astar_bias} = 0; my ($p, $closed, $open) = $graph->_find_path_astar($node, $node2 ); #use Data::Dumper; print Dumper($cells); open FILE, ">test.html" or die ("Cannot write test.html: $!"); print FILE $graph->_map_as_html($cells, $p, $closed, $open); close FILE; sub block { my ($cells, $x,$y) = @_; $cells->{"$x,$y"} = 1; } sub place { my ($cells, $node,$x,$y) = @_; my $c = ($node->{cx} || 1) - 1; my $r = ($node->{cy} || 1) - 1; $node->{x} = $x; $node->{y} = $y; for my $rr (0..$r) { my $cy = $y + $rr; for my $cc (0..$c) { my $cx = $x + $cc; $cells->{"$cx,$cy"} = $node; } } diag ("Placing $node->{name} at $node->{x},$node->{y}\n"); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/parser_dot.t����������������������������������������������������������������������0000644�0000764�0000764�00000024066�11675050456�016112� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # test Graph::Easy::Parser::Graphviz use Test::More; use strict; use utf8; BEGIN { plan tests => 126; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy::Parser::Graphviz") or die($@); }; can_ok ("Graph::Easy::Parser::Graphviz", qw/ new from_text from_file reset error use_class _parse_attributes _remap_attributes _match_comment _build_match_stack /); binmode (STDERR, ':utf8') or die ("Cannot do binmode(':utf8') on STDERR: $!"); binmode (STDOUT, ':utf8') or die ("Cannot do binmode(':utf8') on STDOUT: $!"); ############################################################################# # parser object my $c = 'Graph::Easy::Parser::Graphviz'; my $parser = Graph::Easy::Parser::Graphviz->new( debug => 0 ); is (ref($parser), $c); is ($parser->error(), '', 'no error yet'); ############################################################################# # from_text() and from_file() with Class->method style calling my $graph = Graph::Easy::Parser::Graphviz->from_text('digraph G1 { "A" }'); is (ref($graph), 'Graph::Easy'); is ($graph->nodes(), 1, 'one node'); $graph = Graph::Easy::Parser::Graphviz->from_text('graph G { run -- init }'); is (ref($graph), 'Graph::Easy'); is ($graph->nodes(), 2, 'two nodes'); is ($graph->edges(), 1, 'one edge'); my @a = $graph->nodes(); for (@a) { $_ = $_->{name}; } is (join (",", sort @a), 'init,run', 'two nodes'); ############################################################################# # matching nodes my $node_qr = $parser->_match_node(); like ('"A"', $node_qr, '"A" is a node'); like ('Bonn12', $node_qr, 'Bonn12 is a node'); like ('"Bonn"', $node_qr, '"Bonn" is a node'); ############################################################################# # scopes and scope attributes $graph = Graph::Easy::Parser::Graphviz->from_text( <<EOG digraph GRAPH_0 { node [ color=red ] Red node [ color=green ] Green { node [ color=blue ] Blue } Green2 } EOG ); is (scalar $graph->nodes(), 4, 'scopes: four nodes'); for my $n (qw/Red Green Green2 Blue/) { my $node = $graph->node($n); my $color = lc($node->{name}); $color =~ s/\d//g; is ($node->attribute('color'), $color, "scopes: $n => $color"); } ############################################################################# # test new scope only overriding new attributes plus one source attribute # mapping to two target attributes (shape=doublecircle => shape: circle, # border-style: double) $graph = Graph::Easy::Parser::Graphviz->from_text( <<EOG1 digraph GRAPH_0 { node [ color=red, shape=doublecircle ] Red node [ color=green ] Green { node [ color=blue ] Blue } Green2 } EOG1 ); is (scalar $graph->nodes(), 4, 'scopes: four nodes'); for my $n (qw/Red Green Green2 Blue/) { my $node = $graph->node($n); my $color = lc($node->{name}); $color =~ s/\d//g; is ($node->attribute('color'), $color, "scopes: $n => $color"); is ($node->attribute('shape'), 'circle', "scopes: ${n}'s shope is 'circle'"); is ($node->attribute('border-style'), 'double', "scopes: ${n}'s border-style is 'doube'"); } ############################################################################# # test "a -> { b c d } $graph = Graph::Easy::Parser::Graphviz->from_text( <<EOG2 digraph GRAPH_0 { a -> { b c d } } EOG2 ); is (scalar $graph->nodes(), 4, 'scopes: four nodes'); is (scalar $graph->edges(), 3, 'scopes: three egdes'); ############################################################################# # color parsing my $tests = { '1.0,0.0,1.0' => 'rgb(255,255,255)', # white '1.0,0.0,0.5' => 'rgb(128,128,128)', # grey '1.0,0.0,0.0' => 'rgb(0,0,0)', # black '0.0,1.0,1.0' => 'rgb(255,0,0)', # red '1.0,1.0,1.0' => 'rgb(255,0,0)', # red '1.0,1.0,0.5' => 'rgb(128,0,0)', # darkred '0.1666,1.0,1.0' => 'rgb(255,255,0)', # yellow '0.3333,1.0,1.0' => 'rgb(0,255,0)', # green '0.3333,1.0,0.5' => 'rgb(0,128,0)', # darkgreen '0.5,1.0,1.0' => 'rgb(0,255,255)', # cyan '0.6666,1.0,1.0' => 'rgb(0,0,255)', # blue '0.8333,1.0,1.0' => 'rgb(255,0,255)', # magenta '0.482,0.714,0.878' => 'rgb(64,224,207)', # turquoise '0.051,0.718,0.627' => 'rgb(160,80,45)', # sienna }; for my $test (keys %$tests) { my $color = 'rgb(' . join(",", Graph::Easy::_hsv_to_rgb(split/,/, $test) ) . ')'; my $result = $tests->{$test}; is ($color, $result, "hsv('$test') results in '$result'"); $result =~ /([0-9]+),([0-9]+),([0-9]+)/; my $hex = sprintf("#%02x%02x%02x", $1, $2, $3); $color = Graph::Easy->color_as_hex( 'hsv(' . $test .')' ); is ($color, $hex, "color_as_hex(hsv($test))"); } my $color = Graph::Easy::Parser::Graphviz->_from_graphviz_color('color',"/accent4/4"); is ($color, '#ffff99', "/accent4/4 works"); ############################################################################# # HSL colors my $hsl_tests = { '0,0.0,1.0' => 'rgb(255,255,255)', # white '0,0.0,0.5' => 'rgb(128,128,128)', # grey '0,0.0,0.0' => 'rgb(0,0,0)', # black '0,1.0,0.5' => 'rgb(255,0,0)', # red '0,1.0,0.75' => 'rgb(255,128,128)', # lightred '360,1.0,0.5' => 'rgb(255,0,0)', # red '120,1.0,0.75' => 'rgb(128,255,128)', # light green '240,1.0,0.25' => 'rgb(0,0,128)', # medium blue '60,1.0,0.5' => 'rgb(255,255,0)', # yellow '300,1.0,0.5' => 'rgb(255,0,255)', # magenta }; for my $test (keys %$hsl_tests) { my $color = 'rgb(' . join(",", Graph::Easy::_hsl_to_rgb(split/,/, $test) ) . ')'; my $result = $hsl_tests->{$test}; is ($color, $result, "hsl('$test') results in '$result'"); $result =~ /([0-9]+),([0-9]+),([0-9]+)/; my $hex = sprintf("#%02x%02x%02x", $1, $2, $3); $color = Graph::Easy->color_as_hex( 'hsl(' . $test .')' ); } ############################################################################# ############################################################################# # general pattern tests my $line = 0; foreach (<DATA>) { chomp; next if $_ =~ /^\s*\z/; # skip empty lines next if $_ =~ /^#/; # skip comments $parser->reset(); die ("Illegal line $line in testdata") unless $_ =~ /^(.*)\|([^\|]*)$/; my ($in,$result) = ($1,$2); my $txt = "digraph G {\n" . $in . "\n}"; $txt =~ s/\\n/\n/g; # insert real newlines eval { $graph = $parser->from_text($txt); # reuse parser object }; if (!defined $graph) { fail($parser->error()); next; } if ($graph->error) { fail($graph->error()); next; } my $got = scalar $graph->nodes(); my @edges = $graph->edges(); my $es = 0; foreach my $e (sort { $a->label() cmp $b->label() } @edges) { $es ++ if $e->label() ne ''; } $got .= '+' . $es if $es > 0; for my $n ( sort { $a->name() cmp $b->name() || $b->{att}->{label} cmp $a->{att}->{label} } ($graph->nodes(), $graph->edges()) ) { $got .= "," . $n->label() unless $n->label() =~ /^\s?\z/ || $n->label() eq $n->name(); $got .= "," . $n->name() unless $n->name() eq ''; } my @groups = $graph->groups(); for my $gr ( @groups ) { $got .= ',' . $gr->name(); } is ($got, $result, $in); } __DATA__ |0 # anon nodes ""|1,#0 ""->""|2,#0,#1 "Bonn"->""|2,#1,Bonn ""->"Bonn"|2,#0,Bonn # lines starting with '#' are discared "Bonn"\n#"Berlin"|1,Bonn # First "#0" and "#1" are created, and ID 2 goes to the edge. # then "#3" is created, and ID 4 goes to the second edge. Therefore # "#0" and "#3" are the two anon nodes. ""->"Bonn"->""|3,#0,#3,Bonn # nodes with _ and reserved text "node" node_1 -> node_2 |2,node_1,node_2 # "foo"+"bar style continuations "frankfurt"+" (oder)"|1,frankfurt (oder) "frankfurt" + " (oder)"|1,frankfurt (oder) "frankfurt" + " (oder)"|1,frankfurt (oder) "frank" + "furt" + " (oder)"|1,frankfurt (oder) # multiple spaces in nodes " Bonn and Berlin "|1,Bonn and Berlin " Bonn and Berlin "|1,Bonn and Berlin " Bonn and Berlin "|1,Bonn and Berlin " Bonn \n and Berlin "|1,Bonn and Berlin " Bonn \n\n and Berlin "|1,Bonn and Berlin # normal tests " Berlin "|1,Berlin "Hamburg"|1,Hamburg " Dresden " |1,Dresden " Pirna " [ color="red" ]|1,Pirna " Bonn " -> " Berlin "|2,Berlin,Bonn " Bonn " -> " Berlin "\n"Berlin" -> "Frankfurt"|3,Berlin,Bonn,Frankfurt " Bonn \( \#1 \) " -> " Berlin "\n"Berlin" -> "Frankfurt"|3,Berlin,Bonn ( #1 ),Frankfurt " Bonn " [ color=red ]\n"Berlin" -> "Frankfurt"|3,Berlin,Bonn,Frankfurt "Bonn"[color=red]\n"Berlin"->"Frankfurt"|3,Berlin,Bonn,Frankfurt " Bonn " -> " Berlin "\n"Berlin" -> "Frankfurt"|3,Berlin,Bonn,Frankfurt " Bonn " -> "Berlin" [color=blue] \n"Berlin" -> "Frankfurt"|3,Berlin,Bonn,Frankfurt Bonn -> Berlin [color=blue] \nBerlin -> Frankfurt|3,Berlin,Bonn,Frankfurt # funky node names and colors _exit -- run [ color = "0.001 0.002 0.4" ]|2,_exit,run # comments " Bonn " -> " Berlin " [ color="#A0a0A0" ] // failed " Bonn " -> [ Ulm ]|2,Berlin,Bonn " Bonn " -> " Berlin " [ color="#A0a0A0" ] //80808080 failed [ Bonn ] -> [ Ulm ]|2,Berlin,Bonn " Bonn " -> " Berlin " [ color="#A0a0A0" ] //808080 failed [ Bonn ] -> [ Ulm ]|2,Berlin,Bonn " Bonn " -> " Berlin " [ color="#A0a0A0" ] /*808080 failed [ Bonn ] -> [ Ulm ]*/|2,Berlin,Bonn " Bonn " -> " Berlin " [ color="#A0a0A0" ] /*808080 failed\n [ Bonn ] -> [ Ulm ]*/|2,Berlin,Bonn " Bonn /* * comment * */ " -> " Berlin " /*808080 failed\n [ Bonn ] -> [ Ulm ]*/|2,Berlin,Bonn /* * comment * */ # node chains " Bonn " -> " Berlin "\n -> " Kassel "|3,Berlin,Bonn,Kassel # node chains across line-endings a1 -> a2\na2 -> a3|3,a1,a2,a3 # attributes w/ and w/o value graph [ center ]|0 graph [ center=1 ]|0 graph [ center="" ]|0 graph [ center="1" ]|0 graph [ center, truecolor ]|0 graph [ center=1, truecolor ]|0 graph [ center="", truecolor ]|0 graph [ center="1", truecolor ]|0 edge [ ]|0 edge [\n ]|0 edge [ f=1 ]|0 # ']' inside attributes "node" [ shape="box" label="[U]" color="red" ]|1,[U],node node [ label="[U]" ]|0 # HTML entities names "> ü €   < & &;; &$;"|1,> ü € < & ; $ # v-- non-breakable-space! "HTML" [label="> ü €   < & &;; &$;"]|1,> ü € < & ; $,HTML # color with no leading 0: "node" [ color=".7 .2 1.2"]|1,node ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/nesting.t�������������������������������������������������������������������������0000644�0000764�0000764�00000005646�11675050456�015422� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # Test nesting of groups use Test::More; use strict; BEGIN { plan tests => 34; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy::Group") or die($@); use_ok ("Graph::Easy::Group::Cell") or die($@); use_ok ("Graph::Easy") or die($@); }; ############################################################################# # $group->add_member($inner); my $graph = Graph::Easy->new(); my $group = $graph->add_group('Outer'); is (ref($group), 'Graph::Easy::Group'); is ($group->error(), '', 'no error yet'); my $inner = $graph->add_group('Inner'); $group->add_member($inner); check_groups($group,$inner); ############################################################################# # groups_within(): is ($graph->groups_within(), 2, '2 groups'); is ($graph->groups_within(-1), 2, '2 groups'); is ($graph->groups_within(0), 1, '1 group in outer'); is ($graph->groups_within(1), 2, '2 groups in outer+inner'); is ($graph->groups_within(2), 2, 'no more groups'); ############################################################################# # $inner->add_to_group($group); $graph = Graph::Easy->new(); $group = $graph->add_group('Outer'); is (ref($group), 'Graph::Easy::Group'); is ($group->error(), '', 'no error yet'); $inner = $graph->add_group('Inner'); $inner->add_to_group($group); check_groups($group,$inner); ############################################################################# # groups_within(): my $inner_2 = $graph->add_group('Inner 2'); my $inner_3 = $graph->add_group('Inner 3'); # Level Groups Sum # 0: Outer 1 # 1: Inner Inner 3 3 # 2: Inner 2 4 $inner_2->add_to_group($inner); $inner_3->add_to_group($group); is ($graph->groups_within(), 4, '4 groups'); is ($graph->groups_within(-1), 4, '4 groups'); is ($graph->groups_within(0), 1, '1 group in outer'); is ($graph->groups_within(1), 3, '3 groups in outer+inner'); is ($graph->groups_within(2), 4, '4 groups in total'); # also test calling add_group() with a scalar on another group: my $inner_4 = $group->add_group('Inner 4'); # Level Groups Sum # 0: Outer 1 # 1: Inner Inner 3 Inner 4 4 # 2: Inner 2 5 is ($graph->groups_within(), 5, '5 groups'); is ($graph->groups_within(-1), 5, '5 groups'); is ($graph->groups_within(0), 1, '1 group in outer'); is ($graph->groups_within(1), 4, '4 groups in outer+inner'); is ($graph->groups_within(2), 5, '5 groups in total'); # all tests done 1; ############################################################################# sub check_groups { my ($group,$inner) = @_; is ($inner->{group}, $group, 'inner is in outer'); my @groups = $group->groups(); is (@groups, 1, 'one group in outer'); is ($groups[0], $inner, 'and it is "Inner"'); @groups = $inner->groups(); is (@groups, 0, 'no group in Inner'); is ($inner->attribute('group'), 'Outer', 'attribute("group")'); is ($group->attribute('group'), '', 'attribute("group")'); } ������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/������������������������������������������������������������������������������0000755�0000764�0000764�00000000000�12150110221�014332� 5����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_selfloop_flip.txt�����������������������������������������������������������0000644�0000764�0000764�00000000274�11675050456�020205� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +------+ v | +--------+ +----------+ +----------+ | Adenau | --> | Monschau | <-- | Nideggen | +--------+ +----------+ +----------+ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_bug_basename.txt������������������������������������������������������������0000644�0000764�0000764�00000000245�12150074277�017753� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +--------------+ | v +---+ +---+----+----+ | u | --> | a | b | c | +---+ +---+----+----+ | ^ +-------------------+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_nodes_5_edges.txt�����������������������������������������������������������0000644�0000764�0000764�00000001125�12150074277�020043� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� #= = = = = = = = = = = = = = = = = = # " v +--------------+ +---------+ +-----------+ + | Default Page | --> | Sign Up | --> | | ' +--------------+ +---------+ | | ' ! | | ' +.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-> | Main Page | ' | | ' | | + - - - - - - - - - - - - - - - - - - > | | +-----------+ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_cluster.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000146�11675050456�017027� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+-------+-----------+-----------+ | Husum | Schleswig | Flensburg | +-------+-----------+-----------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_label_align.txt�������������������������������������������������������������0000644�0000764�0000764�00000000360�11675050456�017574� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+-----------------+ +---+ | Long Node Label | A | | | left | long | B | | right | edge label | | | center | ------------> | | +-----------------+ +---+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_selfloop_flow_down.txt������������������������������������������������������0000644�0000764�0000764�00000000320�11675050456�021241� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+-------+ | Start | +-------+ | | v +-------+ Until not done | | -----------------+ | Main | | | | <----------------+ +-------+ | | v +-------+ | End | +-------+ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_att.txt���������������������������������������������������������������������0000644�0000764�0000764�00000000257�11675050456�016142� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+------------+ +---+ | $a = "9"; | | 2 | | my $b = 1; | --> | | +------------+ +---+ +------------+ +---+ | $a = ";% | --> | 4 | +------------+ +---+ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_flow_chain.txt��������������������������������������������������������������0000644�0000764�0000764�00000000256�11675050456�017462� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---------+ +--------+ | Bonn | --> | Berlin | +---------+ +--------+ | | v +---------+ +--------+ | Cottbus | --> | Ulm | +---------+ +--------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/8_optimize_bend.txt�����������������������������������������������������������0000644�0000764�0000764�00000001564�12147701543�020203� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� .............. : : : : : +----+------------------------------------------------+ v | : v +------+ | +------------+ +-------+ +---------+ +--------+ | init | | | main | | parse | | execute | | make a | | | -+ | | --> | | --> | | --> | string | +------+ +------------+ +-------+ +---------+ +--------+ | | | Testlabel | v v +------------+ +---------+ | cleanup | | compare | +------------+ +---------+ +------------+ | printf | +------------+ ��������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/6_chained.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000643�11675050456�016746� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +-------------------------------------------+ | v +---------+ +-----+ +---------+ +--------+ +--------+ | Bonn | --> | Ulm | --> | Bautzen | --> | Berlin | --> | Kassel | +---------+ +-----+ +---------+ +--------+ +--------+ | | | | v | +---------+ | | Koblenz | <-----+ +---------+ ���������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_comma.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000250�11675050456�016437� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ +---+ +---+ | 1 | --> | 4 | <-- | 3 | +---+ +---+ +---+ ^ | | +---+ | 2 | +---+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_collapse.txt����������������������������������������������������������������0000644�0000764�0000764�00000000366�11675050456�017155� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+-----------+ S P A C E +-----------+ | S P A C E | -----------> | S P A C Y | +-----------+ +-----------+ +-----------+ A B C D +-----------+ | A B C | -----------> | X Z Y | +-----------+ +-----------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/9_flow_south.txt��������������������������������������������������������������0000644�0000764�0000764�00000000660�12150074300�017525� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+----------------------------------------------+ | A | +----------------------------------------------+ | | | | | | | | | | | | | | | | v v v v v v v v +----++----++----++----++----++----++----++----+ | A1 || A2 || A3 || A4 || A5 || A6 || A7 || A8 | +----++----++----++----++----++----++----++----+ ��������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_nodes_edge.txt��������������������������������������������������������������0000644�0000764�0000764�00000000272�11675050456�017443� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ test +---+ | A | ---------------> | B | +---+ +---+ ^ | | +------+ | C | +------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_wrap.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000473�11675050456�016321� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+------------+ Drive +------------+ | Frankfurt | a car | Small | | Oder | to | city near | | Flughafen- | the | a | | Terminal | destination | beautyfull | | | -------------> | river | +------------+ +------------+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/��������������������������������������������������������������������������0000755�0000764�0000764�00000000000�12150110221�015120� 5����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/2_square_bracket_in_attr.txt����������������������������������������������0000644�0000764�0000764�00000000060�11675050456�022641� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ | ] | +---+ | | v +---+ | b | +---+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/4_loose.txt���������������������������������������������������������������0000644�0000764�0000764�00000000267�11675050456�017262� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ | A | -+ +---+ | | | | | | | +---+ | | B | | +---+ | | | | | | | +---+ | | C | | +---+ | | | | | | | +---+ | | D | -+ +---+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/4_html_like.txt�����������������������������������������������������������0000644�0000764�0000764�00000000110�11675050456�020074� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ | | +---+ +---+ | a | +---+ +---+ | | +---+ +---+ | | +---+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/2_ignore.txt��������������������������������������������������������������0000644�0000764�0000764�00000000110�11675050456�017405� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+-------+ | node1 | +-------+ | | v +-------+ | node2 | +-------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/5_scopes_uni.txt����������������������������������������������������������0000644�0000764�0000764�00000000546�12150106110�020263� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ +---+ +---+ | c | --- | a | --- | d | +---+ +---+ +---+ | | | | | | | | | | +---+ | | | b | | | +---+ | | | | | | | | | | | +---+ | +-------| o |-------+ +---+ ����������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/2_nospace.txt�������������������������������������������������������������0000644�0000764�0000764�00000000124�11675050456�017557� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---------+ | mike | +---------+ | | v +---------+ | michael | +---------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/6_group_align.txt���������������������������������������������������������0000644�0000764�0000764�00000001270�11675050456�020444� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+ - - - - - - - - - - - - -+ ' Left: ' ' ' ' +-----+ +------+ ' ' | A | ------> | B | ' ' +-----+ +------+ ' ' ' + - - - - - - - - - - - - -+ + - - - - - - - - - - - - -+ ' Right: ' ' ' ' +-----+ +------+ ' ' | C | ------> | D | ' ' +-----+ +------+ ' ' ' + - - - - - - - - - - - - -+ + - - - - - - - - - - - - -+ ' Center: ' ' ' ' +-----+ +------+ ' ' | E | ------> | F | ' ' +-----+ +------+ ' ' ' + - - - - - - - - - - - - -+ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/3_graph_label_long.txt����������������������������������������������������0000644�0000764�0000764�00000000431�11675050456�021410� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +---------+ | Köln | +---------+ | | v +---------+ | Bonn | +---------+ | | v +---------+ | Koblenz | +---------+ Deutsche Städte am Rhein ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/4_uppercase.txt�����������������������������������������������������������0000644�0000764�0000764�00000000536�11675050456�020127� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+ - - - - - - -+ ' cluster_me ' ' ' ' ............ ' ' : a : ' ' :..........: ' ' | ' ' | foo ' ' v ' ' ............ ' ' : b : ' ' :..........: ' ' ' + - - - - - - -+ ............ : c : :..........: | | foo v ............ : d : :..........: ������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/7_record.txt��������������������������������������������������������������0000644�0000764�0000764�00000000504�11675050456�017414� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� Left aligned label +------------------------+----+----+ | A | B | C | +------------------------+----+----+ +------------------------+ | <f1> Aa | <f2> Bb | Cc | +------------------------+ +------------------------+----+----+ | AA | BB | CC | +------------------------+----+----+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/2_setlinewidth.txt��������������������������������������������������������0000644�0000764�0000764�00000000116�11675050456�020633� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������########## # Bonn # ########## | | v ########## # Berlin # ########## ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/3_ids.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000135�11675050456�016711� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+------+ | 123 | +------+ +------+ | abc | +------+ | | v +------+ | test | +------+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/0_empty.txt���������������������������������������������������������������0000644�0000764�0000764�00000000001�11675050456�017255� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/9_stacking.txt������������������������������������������������������������0000644�0000764�0000764�00000001202�11675050456�017737� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ +----+ +----+ +---+ +---+ +---+ +---+ +---+ | 0 | --> | 11 | --> | 1 | --> | 2 | --> | 3 | --> | 8 | --> | 5 | --> | 6 | +---+ +----+ +----+ +---+ +---+ +---+ +---+ +---+ | ^ | | v | +----+ | | 10 | ----------------------------------------------+ +----+ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/10_numbers.txt������������������������������������������������������������0000644�0000764�0000764�00000000776�11675050456�017676� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+------------+ | -100 | +------------+ | | v +------------+ | 123.1230 | +------------+ +------------+ | .1 | +------------+ | | v +------------+ | -1. | +------------+ +------------+ | .99 | +------------+ | | v +------------+ | 0. | +------------+ +------------+ | 0.99 | +------------+ | | v +------------+ | 12.88 | +------------+ +------------+ | 00019.1001 | +------------+ | | v +------------+ | 1000. | +------------+ ��Graph-Easy-0.73/t/out/dot/9_back.txt����������������������������������������������������������������0000644�0000764�0000764�00000000766�11675050456�017052� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ +----+ +----+ +---+ +---+ +---+ | 0 | --> | 11 | --> | 1 | --> | 2 | --> | 3 | --> | 8 | +---+ +----+ +----+ +---+ +---+ +---+ | ^ | | v | +----+ +---+ +---+ | | 10 | --> | 6 | <-- | 5 | ------+ +----+ +---+ +---+ ����������Graph-Easy-0.73/t/out/dot/3_colors.txt��������������������������������������������������������������0000644�0000764�0000764�00000000434�11675050456�017435� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+ - - - - - - + ' cluster_1 ' ' ' ' +---------+ ' ' | A | ' ' +---------+ ' ' | ' ' | ' ' v ' ' +---------+ ' ' | B | ' ' +---------+ ' ' ' + - - - - - - + | | v +---------+ | C | +---------+ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/3_empty_record.txt��������������������������������������������������������0000644�0000764�0000764�00000000132�11675050456�020623� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+------------+--+-----------+ | Compositor | | Compose() | +------------+--+-----------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/2_ports.txt���������������������������������������������������������������0000644�0000764�0000764�00000000210�11675050456�017272� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +-------+ | north | -+ +-------+ | | +-------------+ | | +-------+ +> | south | +-------+ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/2_no_spaces.txt�����������������������������������������������������������0000644�0000764�0000764�00000000116�11675050456�020102� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+--------+ | bonn | +--------+ | | | +--------+ | berlin | +--------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/2_bool.txt����������������������������������������������������������������0000644�0000764�0000764�00000000116�11675050456�017063� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+--------+ | Bonn | +--------+ | | v +--------+ | Berlin | +--------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/5_scopes.txt��������������������������������������������������������������0000644�0000764�0000764�00000000546�12150106110�017410� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ +---+ +---+ | c | <-- | a | --> | d | +---+ +---+ +---+ | | | | | | | v | | +---+ | | | b | | | +---+ | | | | | | | | v | | +---+ | +-----> | u | <-----+ +---+ ����������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/2_group_labelloc.txt������������������������������������������������������0000644�0000764�0000764�00000000430�11675050456�021120� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+ - - - - - - - - + ' ' ' +-------------+ ' ' | Zugspitze | ' ' +-------------+ ' ' | ' ' | ' ' v ' ' +-------------+ ' ' | Wasserkuppe | ' ' +-------------+ ' ' ' ' Bergtour: ' + - - - - - - - - + ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/9_tree.txt����������������������������������������������������������������0000644�0000764�0000764�00000000527�11675050456�017104� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +---+----+----+ | | A | | +---+----+----+ | | | | v | +---+----+----+ | | | B | | | +---+----+----+ | | | | | v | +--+----+----+ | | | C | | <-----+ +--+----+----+ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/5_scope_atr.txt�����������������������������������������������������������0000644�0000764�0000764�00000000321�11675050456�020110� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +---+ | a | +---+ --- | b | --- | | v +---+ c --> | u | +---+ ^ | | d ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/6_comments.txt������������������������������������������������������������0000644�0000764�0000764�00000001467�12150106110�017745� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +-----------------------------+ +---+ +---+ | c | <-- | a | --> | d | +-----------------------------+ +---+ +---+ | | | +----+ | | | v | | +-----------------------------+ +---+ | | | my node /* not a comment */ | --> | b | | | +-----------------------------+ +---+ | | | | | | | | v | | +---+ | +------------------------------------> | u | <-----+ +---+ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/2_comment_inside_attr.txt�������������������������������������������������0000644�0000764�0000764�00000000060�11675050456�022155� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ | a | +---+ | | v +---+ | b | +---+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/3_output_lone.txt���������������������������������������������������������0000644�0000764�0000764�00000000102�11675050456�020501� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ | a | +---+ | | v +---+ | b | +---+ +---+ | c | +---+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/5_scopes_chain.txt��������������������������������������������������������0000644�0000764�0000764�00000000546�12150106110�020552� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ +---+ +---+ | c | <-- | a | --> | d | +---+ +---+ +---+ | | | | | | | v | | +---+ | | | b | | | +---+ | | | | | | | | v | | +---+ | +-----> | o | <-----+ +---+ ����������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/3_empty_record_LR.txt�����������������������������������������������������0000644�0000764�0000764�00000000151�11675050456�021221� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+------------+ | Compositor | +------------+ | | +------------+ | Compose() | +------------+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/16_split.txt��������������������������������������������������������������0000644�0000764�0000764�00000001036�11675050456�017352� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +-------------------------+ | | | +-----+--+--+----+ | | nil | | | 0 | | +-----+--+--+----+ | | | | v v +-----+--+--+------+ | nil | | | 1 | +-----+--+--+------+ +-------------------------+ | | | +-----+--+--+----+ | | nil | | | 2 | | +-----+--+--+----+ | | | | v v +-----+--+--+------+ | nil | | | 3 | +-----+--+--+------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/4_record.txt��������������������������������������������������������������0000644�0000764�0000764�00000000264�11675050456�017414� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +------+-------+ | test | split | +------+-------+ | | v +--------+-------+ | record | test | +--------+-------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/4_strings.txt�������������������������������������������������������������0000644�0000764�0000764�00000000410�11675050456�017620� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+----------------+ | Bonn am Rhein | +----------------+ | | train (ICE) v +----------------+ | Berlin Ost | +----------------+ +----------------+ | Bonn (Rhei n) | +----------------+ | | v +----------------+ | Cottbus | +----------------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/4_cluster_labeljust.txt���������������������������������������������������0000644�0000764�0000764�00000001420�11675050456�021657� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+ - - - - - - - - - - - - + ' Right aligned ' ' ' ' +---------------------+ ' ' | aaaaaaaaaaaaaaaaaaa | ' ' +---------------------+ ' ' | ' ' | ' ' v ' ' +---------------------+ ' ' | b | ' ' +---------------------+ ' ' ' + - - - - - - - - - - - - + + - - - - - - - - - - - - + ' Left aligned ' ' ' ' +---------------------+ ' ' | c | ' ' +---------------------+ ' ' | ' ' | ' ' v ' ' +---------------------+ ' ' | ddddddddddddddd | ' ' +---------------------+ ' ' ' + - - - - - - - - - - - - + ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/2_linewidth.txt�����������������������������������������������������������0000644�0000764�0000764�00000000031�11675050456�020113� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 123 | | v foo �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/2_strict.txt��������������������������������������������������������������0000644�0000764�0000764�00000000156�11675050456�017444� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+-------------+ | Wolfsbüttel | +-------------+ | | v +-------------+ | Köln | +-------------+ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/2_graph_label_bottom.txt��������������������������������������������������0000644�0000764�0000764�00000000206�11675050456�021754� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +-----------+ | Köln | +-----------+ | | v +-----------+ | Stralsund | +-----------+ Deutsche Städte ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/3_invis.txt���������������������������������������������������������������0000644�0000764�0000764�00000000077�11675050456�017267� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ | a | +---+ | | v | | v +---+ | c | +---+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/9_edge_styles.txt���������������������������������������������������������0000644�0000764�0000764�00000000630�12150106110�020421� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ +---+ | F | <## | A | +---+ +---+ # # # # v v +---+ +---+ | G | | B | +---+ +---+ # # # # v v +---+ +---+ | H | | C | +---+ +---+ # # # # v v +---+ +---+ | I | | D | +---+ +---+ # # v +---+ | E | +---+ ��������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/4_compass.txt�������������������������������������������������������������0000644�0000764�0000764�00000000355�12150106110�017556� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+--------+-----------+ | bonn N | S | -+ +--------+-----------+ | | | +---------+ | | v | +----------+----+ +-----> | berlin N | w | +----------+----+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/6_2_cluster.txt�����������������������������������������������������������0000644�0000764�0000764�00000002173�11675050456�020043� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +- - - - - - - -+ ' first ' ' ' ' +-----+ ' ' | a | ' <# ' +-----+ ' # ' | ' # ' | ' # ' v ' # ' +-----+ ' # ' | b | ' # ' +-----+ ' # ' ' # +- - - - - - - -+ # | # | # v # + - - - - - - - - - - -+ # ' second ' # ' ' # ' +-----+ ' # ' +--------> | q | ' # ' | +-----+ ' # ' | | ' # ' | | ' # ' | v ' # ' | +-----+ ' # ' | | r | ' ## ' | +-----+ ' ' | | ' ' | | ' ' | v ' ' | +-----+ ' ' | | s | ' ' | +-----+ ' ' | | ' ' | | ' ' | v ' ' | +-----+ ' ' +--------- | p | ' ' +-----+ ' ' ' + - - - - - - - - - - -+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/dot/3_node_label.txt����������������������������������������������������������0000644�0000764�0000764�00000000411�11675050456�020213� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� Graphname +---------------------+ | Graphname 0 | +---------------------+ | | 0->1 Graphname 0 1 v +---------------------+ | Graphname 1 | +---------------------+ +---------------------+ | Name: 2 Graphname | +---------------------+ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/8_invisible.txt���������������������������������������������������������������0000644�0000764�0000764�00000001474�12150070646�017334� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� My sample graph +--------+ Test label +-------+ +-------+ #=====> | Five | - - - - - - > | Seven | | Eight | H +--------+ +-------+ +-------+ H H H H #==================================# v +-----+ +--------+ +-------+ Test label +-------+ | Two | | One | ============> | Three | <............> | Six | +-----+ +--------+ +-------+ +-------+ : : Test : label v +--------+ | Four | +--------+ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_group_labelpos.txt����������������������������������������������������������0000644�0000764�0000764�00000000460�11675050456�020361� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+ - - - - - - - - - - - - - - - - - + ' ' ' +-----------+ +-------------+ ' ' | Zugspitze | --> | Wasserkuppe | ' ' +-----------+ +-------------+ ' ' ' ' Bergtour: ' + - - - - - - - - - - - - - - - - - + ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_endless_loop_2.txt����������������������������������������������������������0000644�0000764�0000764�00000000121�11675050456�020247� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ +---+ | 1 | --> | 2 | +---+ +---+ +---+----+ | 3 | 4 | +---+----+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/6_multicell_offset.txt��������������������������������������������������������0000644�0000764�0000764�00000000526�11675050456�020713� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +------+ | E | +------+ ^ | | +------+ | High | +------+ | | v +------+ | D | +------+ +---+ +------+ +---+ | C | <-- | Wide | --> | B | +---+ +------+ +---+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/6_group_align.txt�������������������������������������������������������������0000644�0000764�0000764�00000001270�11675050456�017656� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+ - - - - - - - - - - - - -+ ' Left: ' ' ' ' +-----+ +------+ ' ' | A | ------> | B | ' ' +-----+ +------+ ' ' ' + - - - - - - - - - - - - -+ + - - - - - - - - - - - - -+ ' Right: ' ' ' ' +-----+ +------+ ' ' | C | ------> | D | ' ' +-----+ +------+ ' ' ' + - - - - - - - - - - - - -+ + - - - - - - - - - - - - -+ ' Center: ' ' ' ' +-----+ +------+ ' ' | E | ------> | F | ' ' +-----+ +------+ ' ' ' + - - - - - - - - - - - - -+ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/25_autosplit_empty.txt��������������������������������������������������������0000644�0000764�0000764�00000001053�11675050456�020672� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +--+----+-----+ +----+ | | C | | --> | A1 | +--+----+-----+ +----+ +---+---+ +----+ | D | | --> | A2 | +---+---+ +----+ +--+----+ +----+ | | E | --> | A3 | +--+----+ +----+ +---+ +----+ | F | --> | A4 | +---+ +----+ +---+ +----+ | G | --> | A5 | +---+ +----+ +--+---+ +----+ | | H | --> | A6 | +--+---+ +----+ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/8_align.txt�������������������������������������������������������������������0000644�0000764�0000764�00000001740�11675050456�016446� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� My Graph +--------------+ +---+ | | Left center | | | B | align align | A | | | ---------> no border --> no border --> | | +--------------+ +---+ +--------------+ aligned +-----------+ | Center | left | Rightmost | | aligned text | ---------> | | +--------------+ +-----------+ + - - - - - - - - - - - - - - - - - - - - - + ' Nodes: ' ' ' ' +--------------+ label +-----------+ ' ' | Right | text | Left | ' ' | Aligned | ---------> | aligned | ' ' +--------------+ +-----------+ ' ' ' + - - - - - - - - - - - - - - - - - - - - - + ��������������������������������Graph-Easy-0.73/t/out/4_2x2nodes.txt����������������������������������������������������������������0000644�0000764�0000764�00000000342�11675050456�017011� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# XXX TODO There should be a space between the rows +-----------+ +--------+ | Frankfurt | --> | Hof | +-----------+ +--------+ +-----------+ +--------+ | Hamburg | --> | Altona | +-----------+ +--------+ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_minlen.txt������������������������������������������������������������������0000644�0000764�0000764�00000000633�12150070646�016622� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---------+ +---------+ +---------+ | Potsdam | <------- | Berlin | -------> | Cottbus | +---------+ +---------+ +---------+ | | | | | v +---------+ | Leipzig | +---------+ �����������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/1node.txt���������������������������������������������������������������������0000644�0000764�0000764�00000000030�11675050456�016122� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+-----+ | One | +-----+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_invisible.txt���������������������������������������������������������������0000644�0000764�0000764�00000000305�11675050456�017330� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +---------------------+ v | +---------------+ +--------+ | Bischofswerda | --> --> --> | Bremen | +---------------+ +--------+ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_autolabel.txt���������������������������������������������������������������0000644�0000764�0000764�00000000325�11675050456�017314� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---------------------+ Acme Tr ... porated +---------------------+ | Bonn (e ... tstadt) | ---------------------> | Frankfu ... ughafen | +---------------------+ +---------------------+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_joint_short.txt�������������������������������������������������������������0000644�0000764�0000764�00000000240�11675050456�017703� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +---+ | C | +---+ ^ | | +---++---+ | | A || B | | +---++---+ | | | | +----+----+ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_flow.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000473�11675050456�016321� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +--------+ | Passau | +--------+ | | v +------------+ +--------+ +---------------+ | Regensburg | <-- | Siegen | --> | Aschaffenburg | +------------+ +--------+ +---------------+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_lists.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000154�11675050456�016503� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+------+ +--------+ +-----+ | Bonn | --> | Berlin | <-- | Ulm | +------+ +--------+ +-----+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_list_attr.txt���������������������������������������������������������������0000644�0000764�0000764�00000000102�11675050456�017342� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+ - - - -+ ' Berlin ' + - - - -+ + - - - -+ ' Bonn ' + - - - -+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/5_tree_joint.txt��������������������������������������������������������������0000644�0000764�0000764�00000000336�11675050456�017513� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ | A | +---+ | +----+ +-----> | A1 | | +----+ | | | +-----> | A2 | | +----+ | | | +-----> | A3 | | +----+ | | | +-----> | A4 | +----+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_edge_cross.txt��������������������������������������������������������������0000644�0000764�0000764�00000000574�12150070646�017461� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +---------------------+ | | | | +----------+----------+ | | | v v +----+ +----+ +----+ +----+ | E1 | --> | E2 | --> | S1 | --> | S2 | +----+ +----+ +----+ +----+ | ^ +--------------------------------+ ������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3nodes.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000146�11675050456�016317� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+-----+ +-----+ +-------+ | One | ==> | Two | - > | Three | +-----+ +-----+ +-------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_newlines.txt����������������������������������������������������������������0000644�0000764�0000764�00000000150�11675050456�017164� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+--------+ .......... | Berlin | : Berlin : | (W) | --> : (O) : +--------+ :........: ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_autosplit_escaped.txt�������������������������������������������������������0000644�0000764�0000764�00000000052�11675050456�021051� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+-------+---+ | A | B | C | +-------+---+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_invisible_left.txt����������������������������������������������������������0000644�0000764�0000764�00000000071�11675050456�020340� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +--------+ --> | Berlin | +--------+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_list_attr.txt���������������������������������������������������������������0000644�0000764�0000764�00000000226�11675050456�017352� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+ - - - -+ test +-.-.-.-.-.-+ test + - - -+ ' Berlin ' ------> ! Frankfurt ! <------ ' Bonn ' + - - - -+ +-.-.-.-.-.-+ + - - -+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_bend_bug.txt����������������������������������������������������������������0000644�0000764�0000764�00000000330�11675050456�017107� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+--------+ +-----------+ | Bonn | -+ | Ulm | +--------+ | +-----------+ | | | +--------+ | +-----------+ | Berlin | +> | Frankfurt | +--------+ +-----------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/5_rounded.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000476�11675050456�017016� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ......... - - - - ========== ########## ----------- : Pullach : ' Parsing ' H Pankow H # Pullheim # | Paderborn | : : --> ' ' --> H (Berlin) H --> # # --> | | ......... - - - - ========== ########## ----------- ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/8_flow.txt��������������������������������������������������������������������0000644�0000764�0000764�00000001441�11675050456�016321� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +--------------+ +-------+ | East | --> | South | +--------------+ +-------+ | | v +--------------+ +-------+ | South Too | <-- | West | +--------------+ +-------+ | | v +----------------+ +--------------+ | left (south) | <-- | right (west) | +----------------+ +--------------+ | | v +----------------+ +--------------+ | east (default) | --> | final | +----------------+ +--------------+ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_edge_types.txt��������������������������������������������������������������0000644�0000764�0000764�00000000440�11675050456�017474� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---------------+ +--------------+ | Bingen | <----------> | Mainz | +---------------+ +--------------+ +---------------+ test-text +--------------+ | Bischofswerde | -----------> | Finsterwalde | +---------------+ +--------------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/10_repair.txt�����������������������������������������������������������������0000644�0000764�0000764�00000005361�12147700036�016701� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +---------------------+ | | + - - - - - - - - - - - - - - - - - + | ' DMZ1: ' | ' ' v ' +---------+ +-----------+ ' +----------+ ' | 1Server | <------ | | ' --> | 1Check | -+ ' +---------+ | | ' +----------+ | ' ^ | | ' | | ' +---------------- | | ' | | ' | 1Proxy | ' | | ' | | - - -+ | | + - - - - - - - - - | | ' | | ' | | ' | | ' | | ---+ ' | | ' +-----------+ | ' | | ' | | ' | | ' | | ' | | ' | | ' v | +- - - | | - - - - - - -+ | ' v | ' | ' +-----------+ | +----------+ ' | ' +--- | 1Database | <--+ | | ' <+ ' | +-----------+ | | ' ' | | | | ' ' | +-----------------> | 1Backend | ' ' | | | ' ' | | | ' ' +------------------------> | | ' ' +----------+ ' ' ' +- - - - - - - - - - - - - - - - - - - - - -+ + - - - - - - - - - - - - - - - - - + ' DMZ: ' ' ' ' +---------+ +-----------+ ' +----------+ ' | Server | <------ | Proxy | ' --> | Check | ' +---------+ +-----------+ ' +----------+ ' | ' + - - - - - - - - - | ' ' | ' | ' | ' | ' | ' v ' | - - - - - - - - - -+ ' v ' ' +-----------+ +----------+ ' ' | Database | ------> | Backend | ' ' +-----------+ +----------+ ' ' ' + - - - - - - - - - - - - - - - - - -+ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_joining.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000433�12150074277�016776� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +---------+----+ | v | +---+ +---+ | | A | | B | | +---+ +---+ | | ^ | | | | +---------+ | | | | | | | v | +---+ | | C | <+ +---+ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_label.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000060�11675050456�016417� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ +---+ | A | --> | A | +---+ +---+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_selfloop_flow_down.txt������������������������������������������������������0000644�0000764�0000764�00000000541�12150070645�021234� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +-------+ | Start | +-------+ | | v Until done +-------+ Until not done +------------- | | -----------------+ | | Main | | +------------> | | <----------------+ +-------+ ���������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/0_empty_group.txt�������������������������������������������������������������0000644�0000764�0000764�00000000001�11675050456�017703� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/8_endless_loop.txt������������������������������������������������������������0000644�0000764�0000764�00000000320�11675050456�020033� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +-------------------+ | v +---+ +---+ +---+ | 1 | --> | 2 | --> | 3 | +---+ +---+ +---+ +---+----+ | 3 | 4 | +---+----+ +---+----+----+ | 5 | 6 | 7 | +---+----+----+ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/18_multiples.txt��������������������������������������������������������������0000644�0000764�0000764�00000002472�11675050456�017456� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ................................................ : v +----------+ +------+ +----------+ +---------+ +---------+ | 1Bonn | --> | 1Ulm | --> | 1Bautzen | --> | 1Berlin | --> | 1Kassel | +----------+ +------+ +----------+ +---------+ +---------+ ! | ! | v | +----------+ | | 1Koblenz | <-----+ +----------+ ................................................ : v +----------+ +------+ +----------+ +---------+ +---------+ | 2Bonn | --> | 2Ulm | --> | 2Bautzen | --> | 2Berlin | --> | 2Kassel | +----------+ +------+ +----------+ +---------+ +---------+ ! | ! | v | +----------+ | | 2Koblenz | <-----+ +----------+ ................................................ : v +----------+ +------+ +----------+ +---------+ +---------+ | Bonn | --> | Ulm | --> | Bautzen | --> | Berlin | --> | Kassel | +----------+ +------+ +----------+ +---------+ +---------+ ! | ! | v | +----------+ | | Koblenz | <-----+ +----------+ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/5_group_split.txt�������������������������������������������������������������0000644�0000764�0000764�00000001635�11675050456�017723� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +--------------------------------------------+ | | + - - - - - - - - - - - - - - - - - - - - - - + | ' test: ' | ' ' | ' +------+ +--------+ +-----------+ ' | ' | Bonn | --> | Berlin | ----> | Frankfurt | ' | ' +------+ +--------+ +-----------+ ' | ' | ' | + - - - - - - - - - - - - - - | ' | ' | ' | ' | ' | ' v ' v ' +-----------+ '+-----+ ' | Hagen | '| Ulm | ' +-----------+ '+-----+ ' ' + - - - - - - - + ���������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/10borders.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000677�11675050456�016736� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+ - - - - - - -+ ' Dashed ' ==> none + - - - - - - -+ +--------------+ .......... | Solid | ..> : Dotted : +--------------+ :........: +-.-.-.-.-.-.-.+ ########## ! dot-dash ! - > # Bold # +-.-.-.-.-.-.-.+ ########## +.-..-..-..-..-+ +~~~~~~~~+ | dot-dot-dash | .-> { wave { +.-..-..-..-..-+ +~~~~~~~~+ # = = = = = = =# #========# " double-dash " ~~> H Double H # = = = = = = =# #========# �����������������������������������������������������������������Graph-Easy-0.73/t/out/5_group_repair.txt������������������������������������������������������������0000644�0000764�0000764�00000001452�11675050456�020047� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+ - - - - - - - - - - - - - - - + ' DMZ: ' ' ' ' +--------+ +----------+ ' +---------+ ' | Server | <---- | Proxy | ' --> | Check | ' +--------+ +----------+ ' +---------+ ' | ' + - - - - - - - - | ' ' | ' | ' | ' | ' | ' v ' | - - - - - - - - + ' v ' ' +----------+ +---------+ ' ' | Database | ----> | Backend | ' ' +----------+ +---------+ ' ' ' +- - - - - - - - - - - - - - - - + ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4nodes.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000275�11675050456�016323� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+-----------+ +--------+ +---------+ | Bonn | --> | Berlin | ..> | Cottbus | +-----------+ +--------+ +---------+ | | v +-----------+ | Frankfurt | +-----------+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/9_cross.txt�������������������������������������������������������������������0000644�0000764�0000764�00000001061�11675050456�016502� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +------------------------+ | | | | +---------+ +----+-------------------+ | | v | | | | +---+ +---+ | +---+ +---+----+ | | U | | I | <+ | A | -------> | B | I | <+ +---+ +---+ +---+ +---+----+ | | |----+ | | C | D | v +---+----+ +---+ | | | Z | | E | +---+ +---+ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_invisible_right.txt���������������������������������������������������������0000644�0000764�0000764�00000000037�11675050456�020525� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+------+ | Bonn | --> +------+ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_cross.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000402�11675050456�016473� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +---+ | C | +---+ ' ' ' +---+ ' +---+ | A | ------+-----> | B | +---+ ' +---+ ' ' v +---+ | D | +---+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/5_arrow_styles.txt������������������������������������������������������������0000644�0000764�0000764�00000001136�12150074277�020101� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +--------------+ | Hagnau | -------+ +--------------+ | | | | | | | | | +--------------+ +---------------+ +> | Oberuhlingen | <--> | Uhlingen | | +--------------+ +---------------+ | | +---------------------+ | | | | +--------------+ ................. | | Mainau | ---- : Unteruhlingen : | +--------------+ :...............: | ^ +--------------------------+ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/8_basename.txt����������������������������������������������������������������0000644�0000764�0000764�00000000402�11675050456�017121� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +---+ | 1 | +---+ | | v +---+---+----+ | A | B | C | +---+---+----+ +---+ | 2 | +---+ | | v +---+---+----+ | A | B | C | +---+---+----+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_joint_bug_flags.txt���������������������������������������������������������0000644�0000764�0000764�00000000361�12147701556�020501� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ | E | +---+ ^ | | | | +---+ +----+-> | B | | +---+ | | | | +---+ +-> | C | | +---+ | | | | +---+ +-> | D | +---+ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_cross_split_hor.txt���������������������������������������������������������0000644�0000764�0000764�00000001270�12147700037�020552� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +- - - -+ ' ' ' +---+ ' ' | A | ' ' +---+ ' ' | ' ' | ' ' | ' + - - - - - - - | - - - - - -+ ' Cross: | ' ' | ' ' +------+ | +---+ ' ' | C | --------+-------> | D | ' ' +------+ | +---+ ' ' | ' + - - - - - - - | - - - - - -+ ' | ' ' | ' ' v ' ' +---+ ' ' | B | ' ' +---+ ' ' ' +- - - -+ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/1_selfloop_2.txt��������������������������������������������������������������0000644�0000764�0000764�00000000766�11675050456�017420� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� Until not done +----------------+ v | Until not done +--------------------+ Until not done +----------------- | | -----------------+ | | Main | | +----------------> | | <----------------+ +--------------------+ ^ Until not done | +----------------+ ����������Graph-Easy-0.73/t/out/6_ranks.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000344�11675050456�016467� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ +---+ +---+ | A | --> | B | --> | C | +---+ +---+ +---+ | +-------------------+ v +---+ +---+ | D | --> | E | +---+ +---+ | +---------+ v +---+ | F | +---+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/1_selfloop.txt����������������������������������������������������������������0000644�0000764�0000764�00000000052�11675050456�017163� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +--+ v | +------+ | One | +------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/5_flow.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000344�11675050456�016317� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---------+ +-----------+ | Bonn | --> | Berlin | +---------+ +-----------+ | | v +---------+ +-----------+ +-----+ | Cottbus | --> | Moselkern | --> | Ulm | +---------+ +-----------+ +-----+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_cross_inv.txt���������������������������������������������������������������0000644�0000764�0000764�00000000402�11675050456�017347� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +---+ | C | +---+ ' ' ' +---+ ' +---+ | A | ' | B | +---+ ' +---+ ' ' v +---+ | D | +---+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_dot_dot_dash.txt������������������������������������������������������������0000644�0000764�0000764�00000000264�11675050456�020001� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+.-..-..-..-..-..+ +-.-+ | Test | ! ! : Test : ! B ! | Test test test | ! ! : test : ..-> ! ! +.-..-..-..-..-..+ +-.-+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_bug_joint_2.txt�������������������������������������������������������������0000644�0000764�0000764�00000000361�12150074277�017543� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ | E | +---+ | | | | | +---+ +----+-- | B | | +---+ | | | | +---+ +-- | C | | +---+ | | | | +---+ +-- | D | +---+ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/7_star.txt��������������������������������������������������������������������0000644�0000764�0000764�00000001020�12150074300�016272� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +-----------+ | Weimar | +-----------+ ^ | | +------+ +-----------+ +----------+ | Kiel | <-- | | --> | Berlin | +------+ | Dachau | +----------+ +------+ | | +----------+ | Ulm | <-- | | --> | Chemnitz | +------+ +-----------+ +----------+ | | v +-----------+ | Frankfurt | +-----------+ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/9_chain.txt�������������������������������������������������������������������0000644�0000764�0000764�00000001576�11675050456�016446� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +-----------+ | Dresden | +-----------+ ^ | | +------+ +---------+ +-------+ +-----------+ +----------+ +---------+ | Bonn | --> | Berlin | --> | Koeln | --> | Frankfurt | --> | Hannover | --> | Hamburg | +------+ +---------+ +-------+ +-----------+ +----------+ +---------+ | | | | v v +---------+ +-----------+ | Cottbus | | Chemnitz | +---------+ +-----------+ ����������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/6_joint.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000771�11675050456�016500� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+----------+ +------+ +------+ | Hagen | ------+-> | Bonn | --> | Prag | +----------+ | +------+ +------+ | | | +----------+ | | Hannover | ------+ +----------+ | | | | +----------+ | | Hamburg | ------+ +----------+ | | | | +----------+ | | Hanau | ------+ +----------+ �������Graph-Easy-0.73/t/out/8_chain.txt�������������������������������������������������������������������0000644�0000764�0000764�00000001557�11675050456�016444� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +-----------+ | Dresden | +-----------+ ^ | | +------+ +--------+ +-------+ +-----------+ +----------+ +---------+ | Bonn | --> | Berlin | --> | Koeln | --> | Frankfurt | --> | Hannover | --> | Hamburg | +------+ +--------+ +-------+ +-----------+ +----------+ +---------+ | | v +-----------+ | Chemnitz | +-----------+ �������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_edge_start.txt��������������������������������������������������������������0000644�0000764�0000764�00000000140�12150070645�017450� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ +---+ | | --> | B | | A | +---+ | | +---+ | | --> | C | +---+ +---+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_group.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000374�11675050456�016504� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+ - - - - - - - - - - - - + ' Test ' ' group ' ' ' ' +------+ +--------+ ' ' | Bonn | --> | Berlin | ' ' +------+ +--------+ ' ' ' + - - - - - - - - - - - - + ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_colors.txt������������������������������������������������������������������0000644�0000764�0000764�00000000170�11675050456�016644� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+----------+ +-----+ +--------+ | Preserve | --> | The | --> | Colors | +----------+ +-----+ +--------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_lists.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000373�12150074277�016503� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +---------------------------------------+ | v +------+ +--------+ +-----+ +-----------+ | Bonn | --> | Berlin | <-- | Ulm | --> | Frankfurt | +------+ +--------+ +-----+ +-----------+ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_class.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000060�11675050456�016445� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#===# #===# H A H --> H B H #===# #===# ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/7_cluster.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000134�11675050456�017030� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+-----+---+---+ | A | BCD | E | F | +---+-----+---+---+ | G | | H | +---+-----+---+ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/6_chain_10_edges.txt����������������������������������������������������������0000644�0000764�0000764�00000002075�12147701556�020104� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +--------------------------------------------------------+ | | | | | +-----------+ | | | v v +---------+ +------+ +-----+ +---------+ +--------+ +--------+ | Koblenz | <-- | Bonn | --> | Ulm | --> | Bautzen | --> | | --> | Kassel | +---------+ +------+ +-----+ +---------+ | | +--------+ ^ | | | | | | | | +-----------> | Berlin | | | | | | | | | | | +---------------+------------+ | | | +--------+ | ^ +----------------------------------------+ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/1_empty_group.txt�������������������������������������������������������������0000644�0000764�0000764�00000000044�11675050456�017713� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---------+ | Outside | +---------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_dot.txt���������������������������������������������������������������������0000644�0000764�0000764�00000000172�11675050456�016132� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� // digraph G { +-------------+ +---------+ | Kummersbach | --> | Düsburg | +-------------+ +---------+ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_autosplit_offset.txt��������������������������������������������������������0000644�0000764�0000764�00000000211�11675050456�020732� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ | 1 | -----------+ +---+ v +---+----+ +---+ | 2 | 3 | --> | 3 | +---+----+ +---+ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_autosplit_empty.txt���������������������������������������������������������0000644�0000764�0000764�00000000131�11675050456�020603� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +---------+ | v +---+----+----+---+ | 1 | 2 | | 3 | +---+----+----+---+ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_split_bug.txt���������������������������������������������������������������0000644�0000764�0000764�00000000106�11675050456�017331� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+-----------+ | Some [] | +-----------+ | Autosplit | +-----------+ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_joint.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000240�11675050456�016465� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +---++---+ | U || C | +---++---+ ^ ^ | | | | +---++---+ | | A || B | | +---++---+ | | | | +----+----+ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_split_attribute.txt���������������������������������������������������������0000644�0000764�0000764�00000000142�11675050456�020560� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+ - - - - - + ' Some ' +-----------+ | Autosplit | +-----------+ | Node | +-----------+ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/6_nested_groups.txt�����������������������������������������������������������0000644�0000764�0000764�00000001100�11675050456�020221� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+ - - - - - - - - - - + ' Inner 1 ' ' ' ' +-------+ +---+ ' ' | A | --> | B | ' ' +-------+ +---+ ' ' ' + - - - - - - - - - - + + - - - - - - - - - - + ' Inner 2 ' ' ' ' +-------+ +---+ ' ' | C | --> | D | ' ' +-------+ +---+ ' ' ' + - - - - - - - - - - + + - - - - - - - - - - + ' Outer ' ' ' ' +-------+ +---+ ' ' | E | --> | F | ' ' +-------+ +---+ ' ' ' + - - - - - - - - - - + ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_invisible_both.txt����������������������������������������������������������0000644�0000764�0000764�00000000067�11675050456�020350� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +------+ --> | Bonn | --> +------+ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_selfloop_flow_left.txt������������������������������������������������������0000644�0000764�0000764�00000000325�11675050456�021231� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+-----+ +--------------------+ +-------+ | End | <-- | Main | <-- | Start | +-----+ +--------------------+ +-------+ ^ Until not done | +----------------+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/1_selfloop_label.txt����������������������������������������������������������0000644�0000764�0000764�00000000224�11675050456�020323� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� Alle Jahre Wieder +-------------------+ v | +-----------------------+ | Freiburg | +-----------------------+ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/5_offsets_2.txt���������������������������������������������������������������0000644�0000764�0000764�00000000505�11675050456�017241� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ +---+ | E | --> | D | -+ +---+ +---+ v | +---+ +> | C | <+ +---+ | ^ +---+ +- | B | ------+ +---+ v ^ +---+ +------ | A | +---+ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/5_long_edge_labels.txt��������������������������������������������������������0000644�0000764�0000764�00000000525�11675050456�020616� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� very long edge label +---------------------------------------+ | v +---+ +---+ +---+ +---+ +---+ | A | --> | B | --> | C | --> | D | --> | E | +---+ +---+ +---+ +---+ +---+ ^ another very long edge label | +---------------------------------------+ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_graph_label.txt�������������������������������������������������������������0000644�0000764�0000764�00000000157�11675050456�017607� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� My Graph +------------+ +--------+ | Regensburg | --> | Passau | +------------+ +--------+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_cluster_2.txt���������������������������������������������������������������0000644�0000764�0000764�00000000242�11675050456�017244� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---------+ +--------+ | | --> | | | Hamburg | | Altona | | | | | | | --> | | +---------+ +--------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_bend_bug.txt����������������������������������������������������������������0000644�0000764�0000764�00000000256�11675050456�017115� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+------+ +-----------+ | Bonn | | Berlin | +------+ +-----------+ | | | | +-----------+ +--------> | Frankfurt | +-----------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_edges.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000135�11675050456�016432� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +-----------+ | v +-----+ +-----+ | One | --> | Two | +-----+ +-----+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/drop_result.txt���������������������������������������������������������������0000644�0000764�0000764�00000002512�11675050456�017465� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ┌────────────┐ ∨ │ ┌───────┐ ┌──────┐ ┌────────┐ ┌───────────┐ │ Mainz │ ──> │ Bonn │ ──> │ │ ──> │ Frankfurt │ └───────┘ └──────┘ │ │ └───────────┘ │ │ │ └────────> │ Berlin │ │ │ │ │ ┌───────────┐ │ │ <── │ Ulm │ └────────┘ └───────────┘ ∧ │ └────┘ ┌───────┐ ┌──────┐ ┌───────────┐ │ Mainz │ ──> │ Bonn │ ──> │ Frankfurt │ └───────┘ └──────┘ └───────────┘ ∧ │ │ ┌──────┐ │ Ulm │ └──────┘ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/8_labels.txt������������������������������������������������������������������0000644�0000764�0000764�00000002016�12150074300�016572� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� My sample graph +------------------------+ | v +--------+ Test label +-------+ +-------+ #=====> | Five | - - - - - - > | Seven | -------------- | Eight | H +--------+ +-------+ +-------+ H | ^ H +-------------------------------------------------+ H H #==================================# v +-----+ label +--------+ +-------+ Test label +-------+ | Two | <------- | One | ============> | Three | <............> | Six | +-----+ +--------+ +-------+ +-------+ : : Test : label v +--------+ | Four | +--------+ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4groups.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000710�11675050456�016524� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+ - - - - - - - - - - - - - - - - -+ ' German Cities ' ' ' ' +-------------+ +--------+ ' ' | Cuxhaven | ----> | Bremen | ' ' +-------------+ +--------+ ' ' ' ' - - - - - - - -+ ' +-------------+ ' ' | Flensburg | ' ' +-------------+ ' ' +-------------+ ' ' | Hamburg | ' ' +-------------+ ' ' ' + - - - - - - - - + ��������������������������������������������������������Graph-Easy-0.73/t/out/6_fanout.txt������������������������������������������������������������������0000644�0000764�0000764�00000001350�11675050456�016643� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+-------------+ +-------------+ +--------------+ | Hello World | --> | /dev/fanout | -+-----> | Hello World! | +-------------+ +-------------+ | +--------------+ | +--------------+ +-----> | Hello World! | | +--------------+ | +--------------+ +-----> | Hello World! | | +--------------+ | +--------------+ +-----> | Hello World! | +--------------+ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_nodes_inv.txt���������������������������������������������������������������0000644�0000764�0000764�00000000060�11675050456�017324� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ +---+ | A | ~~> | B | +---+ +---+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_endless_loop.txt������������������������������������������������������������0000644�0000764�0000764�00000000121�11675050456�020026� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ +---+ | 1 | --> | 2 | +---+ +---+ +---+----+ | 3 | 4 | +---+----+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/gdl/��������������������������������������������������������������������������0000755�0000764�0000764�00000000000�12150110221�015100� 5����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/gdl/2_bottom_to_top.txt�������������������������������������������������������0000644�0000764�0000764�00000000060�11675050456�020776� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ | B | +---+ ^ | | +---+ | A | +---+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/gdl/1_color_code.txt����������������������������������������������������������0000644�0000764�0000764�00000000317�11675050456�020222� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� Graph of sub_105D40F +---------------------------+ | sub_105D40F: | | mov edi, esi | | push ebp | | jz short loc_105D27B | +---------------------------+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/gdl/2_right_to_left.txt�������������������������������������������������������0000644�0000764�0000764�00000000060�11675050456�020737� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ +---+ | B | <-- | A | +---+ +---+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/gdl/2_left_to_right.txt�������������������������������������������������������0000644�0000764�0000764�00000000060�11675050456�020737� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ +---+ | A | --> | B | +---+ +---+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/gdl/2_top_to_bottom.txt�������������������������������������������������������0000644�0000764�0000764�00000000060�11675050456�020776� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ | A | +---+ | | v +---+ | B | +---+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/0_empty_groups.txt������������������������������������������������������������0000644�0000764�0000764�00000000001�11675050456�020066� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_bidi_astar.txt��������������������������������������������������������������0000644�0000764�0000764�00000000165�11675050456�017447� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+--------+ +--------------+ | Erfurt | <--------> | Bad Schandau | +--------+ +--------------+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_group_no_border.txt���������������������������������������������������������0000644�0000764�0000764�00000000124�11675050456�020526� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Test group +------+ +--------+ | Bonn | --> | Berlin | +------+ +--------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_nodes_5_edges.txt�����������������������������������������������������������0000644�0000764�0000764�00000000653�11675050456�020055� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +----------------------------------------+ | v +--------------+ +-------------+ +---------+ +-----------+ | Default Page | --> | Test Search | --> | Sign Up | --> | Main Page | +--------------+ +-------------+ +---------+ +-----------+ : ^ .......................................................... �������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_cache_bug.txt���������������������������������������������������������������0000644�0000764�0000764�00000000353�11675050456�017246� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+ - - - + ' G ' ' ' ' +---+ ' ' | A | ' ------+ ' +---+ ' | ' ' | + - - - + | | | C v +----+ | B | --> +----+ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/6_empty_row.txt���������������������������������������������������������������0000644�0000764�0000764�00000000160�11675050456�017372� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ +---+ | A | --> | C | +---+ +---+ | | | | +---+ +---+ | B | | D | +---+ +---+ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_group_align_center.txt������������������������������������������������������0000644�0000764�0000764�00000000401�11675050456�021206� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� My Graph Nodes: +---------+ label +---------+ +---------+ | Right | text | Left | | Center | | Aligned | -------> | aligned | --> | aligned | +---------+ +---------+ +---------+ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_list_attr.txt���������������������������������������������������������������0000644�0000764�0000764�00000000571�12147701556�017355� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� test +---------------------------------------------------------+ | v + - - - -+ test +.-..-..-..-+ test + - - -+ test +.-..-..-..-..-..-.+ ' Berlin ' ------> | Frankfurt | <------ ' Bonn ' ------> | Frankfurt (Oder) | + - - - -+ +.-..-..-..-+ + - - -+ +.-..-..-..-..-..-.+ ���������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_edge_labels.txt�������������������������������������������������������������0000644�0000764�0000764�00000000613�12150074277�017570� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� car +-----------------------------------+ | v +------+ train +--------+ bus +---------+ | Bonn | =======> | Berlin | .....> | Potsdam | +------+ +--------+ +---------+ ! ! bike v +--------+ | Ulm | +--------+ ���������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_selfloop.txt����������������������������������������������������������������0000644�0000764�0000764�00000001111�11675050456�017161� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +--------------------+ | Start | +--------------------+ | | v Until not done +--------------------+ Until not done +----------------- | | -----------------+ | | Main | | +----------------> | | <----------------+ +--------------------+ ^ Until not done | +----------------+ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4groups_class.txt�������������������������������������������������������������0000644�0000764�0000764�00000000710�11675050456�017711� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.+ ! German Cities ! ! ! ! +-------------+ +--------+ ! ! | Cuxhaven | ----> | Bremen | ! ! +-------------+ +--------+ ! ! ! ! -.-.-.-.-.-.-.-.+ ! +-------------+ ! ! | Flensburg | ! ! +-------------+ ! ! +-------------+ ! ! | Hamburg | ! ! +-------------+ ! ! ! +-.-.-.-.-.-.-.-.-+ ��������������������������������������������������������Graph-Easy-0.73/t/out/4_nodes_6_edges.txt�����������������������������������������������������������0000644�0000764�0000764�00000001264�11675050456�020055� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +-------------------------------------------------------------+ | | | | | +----------------------------------------+ | | | v v | +--------------+ +-------------+ +---------+ +-----------+ +- | Default Page | --> | Test Search | --> | Sign Up | --> | Main Page | +--------------+ +-------------+ +---------+ +-----------+ | ^ +--------------------------------------------------------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/5_multicell.txt���������������������������������������������������������������0000644�0000764�0000764�00000000740�11675050456�017342� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +---------+ | Client1 | +---------+ ^ | | v +---------+ +---------+ +---------+ | Client3 | <--> | Proxy | <--> | Client2 | +---------+ +---------+ +---------+ ^ | | v +---------+ | Client | +---------+ ��������������������������������Graph-Easy-0.73/t/out/3_empty_group.txt�������������������������������������������������������������0000644�0000764�0000764�00000000103�11675050456�017711� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +---+ +---+ | A | --> | B | +---+ +---+ +---+ | C | +---+ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_autosplit_hang.txt����������������������������������������������������������0000644�0000764�0000764�00000000074�11675050456�020367� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ +---+---+ | 1 | --> | 2 | 3 | +---+ +---+---+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_cluster_3.txt���������������������������������������������������������������0000644�0000764�0000764�00000000313�11675050456�017244� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +---------+ | v +---------+ +--------+ | | --> | | | Hamburg | | Altona | | | | | | | --> | | +---------+ +--------+ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_corrupt.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000135�11675050456�017042� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+----+ +------+ +----+ | AB | --> | ABCB | --> | AB | +----+ +------+ +----+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_node_edge.txt���������������������������������������������������������������0000644�0000764�0000764�00000000157�11675050456�017262� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ test +---+ | A | -----------------------> | B | +---+ +---+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_inherit.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000237�11675050456�017011� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+------------+ | Left | +------------+ | | v +------------+ | Lefty, too | +------------+ | | v +------------+ | Right | +------------+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_edge_repair.txt�������������������������������������������������������������0000644�0000764�0000764�00000000701�11675050456�017611� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� #====# H H + - - - - - - - - - - - - -+ H H ' Router: ' H H ' ' H v ' +-------+ +--------+ ' H +---------+ ' | Input | --> | Output | ' H | Network | ' +-------+ +--------+ ' H +---------+ ' ' H + - - - - - - - - - - - - -+ H H H #===========# ���������������������������������������������������������������Graph-Easy-0.73/t/out/2_zeros.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000131�11675050456�016501� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 0 +----------+ v | +---+ 0 +---+ | 0 | ---> | 0 | +---+ +---+ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/7_tree.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000660�12147701556�016311� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ | A | +---+ | +----+ +-----> | A1 | | +----+ | +----+ +-----> | A2 | | +----+ | | +-----+ | +------> | A23 | | | +-----+ | | +-----+ | +------> | A21 | | | +-----+ | | +-----+ | +------> | A22 | | +-----+ | +----+ +-----> | A3 | +----+ ��������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_classes.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000113�11675050456�016774� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+-----+ red +-------+ | Red | -----> | Black | +-----+ +-------+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_bidi_endpoint.txt�����������������������������������������������������������0000644�0000764�0000764�00000000165�11675050456�020155� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+--------+ +--------------+ | Erfurt | <--------> | Bad Schandau | +--------+ +--------------+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/1_bidi_loop.txt���������������������������������������������������������������0000644�0000764�0000764�00000000230�11675050456�017276� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +--+ v v +------+ +--> | | <--+ | | Hamm | | +--> | | <--+ +------+ ^ ^ +--+ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_edge_labels_from_class.txt��������������������������������������������������0000644�0000764�0000764�00000000154�11675050456�022003� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ Yes +---+ MyLabel +---+ | A | -----> | B | ---------> | C | +---+ +---+ +---+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_flow.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000150�11675050456�016307� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +------------+ | v +------+ +-------+ | Left | | Right | +------+ +-------+ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_long_labels.txt�������������������������������������������������������������0000644�0000764�0000764�00000000220�11675050456�017617� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+------+ +---+ | My | A | | | long | long | B | | node | label | | | name | -------> | | +------+ +---+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_flow.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000402�11675050456�016310� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+-----------+ +-----------+ | Wolfsburg | <-- | Bielefeld | +-----------+ +-----------+ ^ | | +-----------+ | Enschede | +-----------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2nodes.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000074�11675050456�016316� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+-----+ +-----+ | One | ==> | Two | +-----+ +-----+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_selfloop.txt����������������������������������������������������������������0000644�0000764�0000764�00000000372�11675050456�017172� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� Until not done +----------------+ v | +-------+ +--------------------+ +-----+ | Start | --> | Main | --> | End | +-------+ +--------------------+ +-----+ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_autosplit_class.txt���������������������������������������������������������0000644�0000764�0000764�00000000074�11675050456�020560� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#===#.... H A H B : #===#...: + - +===# ' C H D H + - +===# ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/5_offsets.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000451�11675050456�017020� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ +---+ | E | <-- | D | +---+ +---+ ^ +---+ +- | C | +---+ ^ +---+ +- | B | +---+ ^ +---+ +------ | A | +---+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_selfloop_flow_up.txt��������������������������������������������������������0000644�0000764�0000764�00000000753�11675050456�020730� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +-------+ | End | +-------+ ^ | | Until not done +-------+ +----------------- | | | | Main | +----------------> | | +-------+ ^ | | +-------+ | Start | +-------+ ���������������������Graph-Easy-0.73/t/out/2_group_multicell.txt���������������������������������������������������������0000644�0000764�0000764�00000000614�11675050456�020553� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+ - - - - - - - - - - - - - - - - - - - -+ ' Some group: ' ' ' ' +-------------------+ +--------+ ' ' | Frankfurt a. Main | ----> | Berlin | ' ' | (Flughafen) | +--------+ ' ' | | ' ' +-------------------+ - - - - - - - -+ ' ' + - - - - - - - - - - - + ��������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_cluster.txt�����������������������������������������������������������������0000644�0000764�0000764�00000000121�11675050456�017017� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---------+ +--------+ | Hamburg | --> | Altona | +---------+ +--------+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_autosplit_shape.txt���������������������������������������������������������0000644�0000764�0000764�00000000062�11675050456�020550� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ | A | B +---+ |---+ C | D | ---+ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/6_autosplit_class.txt���������������������������������������������������������0000644�0000764�0000764�00000000322�11675050456�020556� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ +--------+----+---+ | 1 | --> | D | B | C | +---+ +--------+----+---+ ^ | | +---+ +---+ | 2 | --> | 3 | +---+ +---+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_cross_split.txt�������������������������������������������������������������0000644�0000764�0000764�00000001270�12147701555�017710� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +- - - -+ ' ' ' +---+ ' ' | C | ' ' +---+ ' ' | ' ' | ' ' | ' + - - - - - - - | - - - - - -+ ' Cross: | ' ' | ' ' +------+ | +---+ ' ' | A | --------+-------> | B | ' ' +------+ | +---+ ' ' | ' + - - - - - - - | - - - - - -+ ' | ' ' | ' ' v ' ' +---+ ' ' | D | ' ' +---+ ' ' ' +- - - -+ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/1_undirected_loop.txt���������������������������������������������������������0000644�0000764�0000764�00000000230�11675050456�020515� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +--+ | | +------+ +--- | | ---+ | | A | | +--- | | ---+ +------+ | | +--+ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/8_points.txt������������������������������������������������������������������0000644�0000764�0000764�00000000111�11675050456�016657� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� * --> # --> . --> o --> <> --> + --> --> * �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/6_multicell.txt���������������������������������������������������������������0000644�0000764�0000764�00000000664�11675050456�017350� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +---------+ +---------+ | v v | +---+ +---+ +---+ +--------+ +---+ | G | <-- | | --> | | --> | C | --> | D | +---+ | A | | B | +--------+ +---+ | | | | ^ | | --> | | ------+ +---+ +---+ | | v +---+ | F | +---+ ����������������������������������������������������������������������������Graph-Easy-0.73/t/out/6_split_join_loop.txt���������������������������������������������������������0000644�0000764�0000764�00000001070�12150074300�020530� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +--------------------------------------------------+ v | +---+ +---+ +---+ +---+ +---+ | A | --> | B | --> | C | -+-----> | X | ------+-> | I | +---+ +---+ +---+ | +---+ | +---+ | | | | | | | +---+ | +-----> | Y | ------+ +---+ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/5_joint_bug2.txt��������������������������������������������������������������0000644�0000764�0000764�00000000352�11675050456�017411� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+-------------++---+ | A || E | +-------------++---+ ^ ^ ^ ^ | | | | | | | | v v v | +---++---++---+ | | B || C || D | | +---++---++---+ | ^ ^ ^ | +----+----+----+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/5_a-star_bug.txt��������������������������������������������������������������0000644�0000764�0000764�00000000221�11675050456�017366� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+---+ | 5 | +---+ | | +---+---+----+ | | | 2 | 3 | 4 | | 1 | +---+---+----+ | | | | | <--------------+ +---+ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/5_joint_label.txt�������������������������������������������������������������0000644�0000764�0000764�00000001062�11675050456�017630� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+-----------+ travel +--------+ | Aachen | ------+-----------------> | Zittau | +-----------+ | +--------+ | | travel | +-----------+ | | Dachau | ------+ +-----------+ | | | travel | +-----------+ | | Buxtehude | ------+ +-----------+ | | | travel | +-----------+ | | Cottbus | ------+ +-----------+ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/3_joint.txt�������������������������������������������������������������������0000644�0000764�0000764�00000000334�11675050456�016470� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+----------+ +--------+ | Mannheim | ------+-> | Weimar | +----------+ | +--------+ | | | +----------+ | | Potsdam | ------+ +----------+ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/4_near.txt��������������������������������������������������������������������0000644�0000764�0000764�00000000355�11675050456�016276� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+------+ +--------+ +-------+ | Bonn | --> | Berlin | --> | Koeln | +------+ +--------+ +-------+ ^ | | +--------+ | Essen | +--------+ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/2_autosplit_empty.txt���������������������������������������������������������0000644�0000764�0000764�00000000102�11675050456�020577� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+--------+----------+ | Siegen | Siegburg | +--------+----------+ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/out/5_joint.txt�������������������������������������������������������������������0000644�0000764�0000764�00000001174�11675050456�016475� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +---------+ | Zwickau | +---------+ ^ | | +------------------------------+ | | Hannover | | +------------------------------+ | | | | | | | | | v v v | +--------++--------++----------+ | | Aachen || Berlin || Cuxhaven | | +--------++--------++----------+ | | | | | +---------+---------+-----------+ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/graphml.t�������������������������������������������������������������������������0000644�0000764�0000764�00000013532�11675050456�015376� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # Some basic GraphML tests use Test::More; use strict; use utf8; BEGIN { plan tests => 14; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy") or die($@); use_ok ("Graph::Easy::Parser") or die($@); }; can_ok ('Graph::Easy', qw/ as_graphml as_graphml_file /); ############################################################################# my $graph = Graph::Easy->new(); my $graphml_file = $graph->as_graphml_file(); $graphml_file =~ s/\n.*<!--.*-->\n//; _compare ($graph, $graphml_file, 'as_graphml and as_graphml_file are equal'); my $graphml = $graph->as_graphml(); like ($graphml, qr/<\?xml version="1.0" encoding="UTF-8"\?>/, 'as_graphml looks like xml'); ############################################################################# # some nodes and edges $graph->add_edge('Ursel','Viersen'); $graphml = $graph->as_graphml(); like ($graphml, qr/<node.*id="Ursel"/, 'as_graphml contains nodes'); like ($graphml, qr/<node.*id="Viersen"/, 'as_graphml contains nodes'); like ($graphml, qr/<edge.*source="Ursel"/, 'as_graphml contains edge'); like ($graphml, qr/<edge.*target="Viersen"/, 'as_graphml contains edge'); ############################################################################# # some attributes: # node.foo { color: red; } [A] {class:foo;}-> { color: blue; } [B] $graph = Graph::Easy->new(); my ($A,$B,$edge) = $graph->add_edge('A','B'); $graph->set_attribute('node.foo','color','red'); $edge->set_attribute('color','blue'); $A->set_attribute('class','foo'); my $result = <<EOT <key id="d0" for="node" attr.name="color" attr.type="string"> <default>black</default> </key> <key id="d1" for="edge" attr.name="color" attr.type="string"> <default>black</default> </key> <graph id="G" edgedefault="directed"> <node id="A"> <data key="d0">red</data> </node> <node id="B"> </node> <edge source="A" target="B"> <data key="d1">blue</data> </edge> </graph> </graphml> EOT ; _compare($graph, $result, 'GraphML with attributes'); ############################################################################# # some attributes with no default valu with no default value: # Also test escaping for valid XML: $edge->set_attribute('label', 'train-station & <Überlingen "Süd">'); $result = <<EOT2 <key id="d0" for="node" attr.name="color" attr.type="string"> <default>black</default> </key> <key id="d1" for="edge" attr.name="color" attr.type="string"> <default>black</default> </key> <key id="d2" for="edge" attr.name="label" attr.type="string"/> <graph id="G" edgedefault="directed"> <node id="A"> <data key="d0">red</data> </node> <node id="B"> </node> <edge source="A" target="B"> <data key="d1">blue</data> <data key="d2">train-station & <Überlingen "Süd"></data> </edge> </graph> </graphml> EOT2 ; _compare($graph, $result, 'GraphML with attributes'); ############################################################################# # node names with things that need escaping: $graph->rename_node('A', '<&\'">'); $result = <<EOT3 <key id="d0" for="node" attr.name="color" attr.type="string"> <default>black</default> </key> <key id="d1" for="edge" attr.name="color" attr.type="string"> <default>black</default> </key> <key id="d2" for="edge" attr.name="label" attr.type="string"/> <graph id="G" edgedefault="directed"> <node id="<&'">"> <data key="d0">red</data> </node> <node id="B"> </node> <edge source="<&'">" target="B"> <data key="d1">blue</data> <data key="d2">train-station & <Überlingen "Süd"></data> </edge> </graph> </graphml> EOT3 ; _compare($graph, $result, 'GraphML with attributes'); ############################################################################# # double attributes $graph = Graph::Easy->new(); ($A,$B,$edge) = $graph->add_edge('A','B'); my ($C,$D,$edge2) = $graph->add_edge('A','C'); $edge->set_attribute('label','car'); $edge2->set_attribute('label','train'); $result = <<EOT4 <key id="d0" for="edge" attr.name="label" attr.type="string"/> <graph id="G" edgedefault="directed"> <node id="A"> </node> <node id="B"> </node> <node id="C"> </node> <edge source="A" target="B"> <data key="d0">car</data> </edge> <edge source="A" target="C"> <data key="d0">train</data> </edge> </graph> </graphml> EOT4 ; _compare($graph, $result, 'GraphML with attributes'); ############################################################################# # as_graphml() with groups (bug until v0.63): $graph = Graph::Easy->new(); my $bonn = Graph::Easy::Node->new('Bonn'); my $cities = $graph->add_group('Cities"'); $cities->add_nodes($bonn); $result = <<EOT5 <graph id="G" edgedefault="directed"> <graph id="Cities"" edgedefault="directed"> <node id="Bonn"> </node> </graph> </graph> </graphml> EOT5 ; _compare($graph, $result, 'GraphML with group'); # all tests done ############################################################################# ############################################################################# sub _compare { my ($graph, $result, $name) = @_; my $graphml = $graph->as_graphml(); $graphml =~ s/\n.*<!--.*-->\n//; $result = <<EOR <?xml version="1.0" encoding="UTF-8"?> <graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd"> EOR . $result unless $result =~ /<\?xml/; if (!is ($graphml, $result, $name)) { eval { require Test::Differences; }; if (defined $Test::Differences::VERSION) { Test::Differences::eq_or_diff ($result, $graphml); } } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/custom.t��������������������������������������������������������������������������0000644�0000764�0000764�00000003773�11675050456�015264� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # Test the custom attributes. use Test::More; use strict; BEGIN { plan tests => (4*4+4) * 6 + (8+2) * 4 + 3; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy::Attributes") or die($@); use_ok ("Graph::Easy") or die($@); }; can_ok ("Graph::Easy", qw/ valid_attribute /); ############################################################################# # valid_attribute: my $att = Graph::Easy->new(); $att->no_fatal_errors(1); for my $n (qw/ foo-bar bar-foo b-f-a boo-f-bar bar b-f /) { my $new_value = $att->valid_attribute( "x-$n", 'furble, barble' ); is ($new_value, "furble, barble", "x-$n is valid"); my @new_value = $att->validate_attribute( "x-$n", 'furble, barble' ); is ($new_value[0], undef, "x-$n is valid"); is ($new_value[1], "x-$n", "x-$n is valid"); is ($new_value[2], "furble, barble", "x-$n is valid"); for my $class (qw/ graph group node edge /) { my $new_value = $att->valid_attribute( "x-$n", 'furble, barble', $class ); is ($new_value, "furble, barble", "x-$n is valid in class $class"); my @new_value = $att->validate_attribute( "x-$n", 'furble, barble', $class ); is ($new_value[0], undef, "x-$n is valid in class $class"); is ($new_value[1], "x-$n", "x-$n is valid"); is ($new_value[2], "furble, barble", "x-$n is valid in class $class"); } } for my $n (qw/ -foo-bar bar-foo- b--a -boo-f-bar- /) { my $new_value = $att->valid_attribute( "x-$n", 'furble, barble' ); is (ref($new_value), 'ARRAY', "x-$n is not valid"); my @new_value = $att->validate_attribute( "x-$n", 'furble, barble' ); is ($new_value[0], 1, "x-$n is not valid"); for my $class (qw/ graph group node edge /) { my $new_value = $att->valid_attribute( "x-$n", 'furble, barble', $class ); is (ref($new_value), 'ARRAY', "x-$n is not valid in class $class"); my @new_value = $att->validate_attribute( "x-$n", 'furble, barble', $class ); is ($new_value[0], 1, "x-$n is not valid in class $class"); } } 1; �����Graph-Easy-0.73/t/parser.t��������������������������������������������������������������������������0000644�0000764�0000764�00000030054�11675050456�015236� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use Test::More; use strict; BEGIN { plan tests => 146; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy::Parser") or die($@); }; can_ok ("Graph::Easy::Parser", qw/ new from_text from_file reset error use_class _parse_attributes /); ############################################################################# # parser object my $parser = Graph::Easy::Parser->new( debug => 0 ); is (ref($parser), 'Graph::Easy::Parser'); is ($parser->error(), '', 'no error yet'); ############################################################################# # parse_error(): $parser->no_fatal_errors(1); $parser->reset(); $parser->{line_nr} = 0; is ($parser->parse_error(1,'foo','bar','node'), "Error in attribute: 'bar' is not a valid attribute for a node at line 0"); $parser->{line_nr} = 0; is ($parser->parse_error(2,'boldly','style','edge'), "Error in attribute: 'boldly' is not a valid style for a edge at line 0"); $parser->{line_nr} = 0; is ($parser->parse_error(3), "Error: Found attributes, but expected group or node start at line 0"); ############################################################################# # from_text() and from_file() with Class->method style calling my $graph = Graph::Easy::Parser->from_text('[A]'); is (ref($graph), 'Graph::Easy'); is ($graph->nodes(), 1, 'one node from_text'); # from_text with graphviz code $graph = Graph::Easy::Parser->from_text('digraph Graph1 { Bonn1 -> Berlin1 }'); is (ref($graph), 'Graph::Easy'); is ($graph->nodes(), 2, 'two nodes from graphviz texts'); $graph = Graph::Easy::Parser->from_file('in/1node.txt'); is (ref($graph), 'Graph::Easy'); is ($graph->nodes(), 1, 'one node'); ############################################################################# # test for invalid input with only one line my $graph2 = $parser->from_text('invalid'); like ($parser->error(), qr/invalid/, 'one invalid line results in error'); ############################################################################# # matching classes with space in front $graph2 = $parser->from_text("# comment\n node { color: red; }\n"); is ($parser->error(), '', 'parsed ok'); ############################################################################# # matching nodes my $node_qr = $parser->_match_node(); like ('[]', $node_qr, '[] is a node'); like ('[ ]', $node_qr, '[ ] is a node'); ############################################################################# # check that setting a new subclass invalidates the cache in Base.pm $graph = Graph::Easy::Parser->from_text( <<EOF group.local { fill: yellow; } ( A [A] { class: foo; } ) { class: local; } EOF ); is ($graph->attribute('group.local','fill'), 'yellow', 'fill is yellow'); my $group = $graph->group('A'); is ($graph->attribute('group.local','fill'), 'yellow', 'fill is yellow'); is ($group->attribute('fill'), 'yellow', 'fill is still yellow'); is ($group->class(), 'group.local', 'group class is group.local'); ############################################################################# # general pattern tests my $line = 0; foreach (<DATA>) { chomp; next if $_ =~ /^\s*\z/; # skip empty lines next if $_ =~ /^#/; # skip comments $parser->reset(); die ("Illegal line $line in testdata") unless $_ =~ /^(.*)\|([^\|]*)$/; my ($in,$result) = ($1,$2); my $txt = $in; $txt =~ s/\\n/\n/g; # insert real newlines my $graph = $parser->from_text($txt); # reuse parser object if (!defined $graph || $graph->error() || $parser->error()) { my $error = $parser->error(); $error = $graph->error() if ref($graph) && $graph->error(); if ($result =~ /ERROR/) { isnt ($error, '', 'got some error'); } else { fail("$error. Input was: $txt"); } next; } my $got = scalar $graph->nodes(); my @edges = $graph->edges(); my $es = 0; foreach my $e (sort { $a->label() cmp $b->label() } @edges) { $es ++ if $e->label() ne ''; } $got .= '+' . $es if $es > 0; for my $n ( sort { $a->name() cmp $b->name() } ($graph->nodes(), $graph->edges()) ) { $got .= "," . $n->label() unless $n->label() =~ /^\s?\z/ || $n->label() eq $n->name(); $got .= "," . $n->name() unless $n->name() eq ''; } my @groups = $graph->groups(); for my $gr ( @groups ) { $got .= ',' . $gr->name(); } is ($got, $result, $in); } __DATA__ |0 # attributes graph { color: red; }|0 group { color: red; }|0 node { color: red; }|0 edge { color: red; }|0 # attributes with space in front graph { color: red; }|0 group { color: red; }|0 node { color: red; }|0 edge { color: red; }|0 # anon nodes []|1,#0 []->[]|2,#0,#1 [Bonn]->[]|2,#1,Bonn []->[Bonn]|2,#0,Bonn # First "#0" and "#1" are created, and ID 2 goes to the edge. # then "#3" is created, and ID 4 goes to the second edge. Therefore # "#0" and "#3" are the two anon nodes. []->[Bonn]->[]|3,#0,#3,Bonn # multiple spaces in nodes [ Bonn and Berlin ]|1,Bonn and Berlin [ Bonn and Berlin ]|1,Bonn and Berlin [ Bonn and Berlin ]|1,Bonn and Berlin [ Bonn \n and Berlin ]|1,Bonn and Berlin [ Bonn \n\n and Berlin ]|1,Bonn and Berlin # split nodes [ A | B ]|2,A,AB.0,B,AB.1 [ A | B | C ]|3,A,ABC.0,B,ABC.1,C,ABC.2 [ A | B | C ] => [ A ]|4,A,A,ABC.0,B,ABC.1,C,ABC.2 [ A | B | C ] => [ A ] [ A | B | C ] => [ A ]|7,A,A,ABC-1.0,B,ABC-1.1,C,ABC-1.2,A,ABC.0,B,ABC.1,C,ABC.2 # unique cluster names, despite trickery in source with "ABC-1" as split node: [ A | B | C | -1 ] => [ A ] [ A | B | C ] => [ A ]|8,A,A,ABC-1.0,B,ABC-1.1,C,ABC-1.2,-1,ABC-1.3,A,ABC.0,B,ABC.1,C,ABC.2 [ A | B | C | -1 ] => [ A ] [ A | B | C ] => [ A ] [ A | B | C ]|11,A,A,ABC-1.0,B,ABC-1.1,C,ABC-1.2,-1,ABC-1.3,A,ABC-2.0,B,ABC-2.1,C,ABC-2.2,A,ABC.0,B,ABC.1,C,ABC.2 # nodes with \[\] [ char\[\] ]|1,char[] [ char\[\] ] -> [ \[\] ]|2,[],char[] # split nodes with \[\] [ char\[\] || int ]|2,char[],char[]int.0,int,char[]int.1 # error testing (no end of node) [ Bonn\[\]|ERROR # normal tests [ Berlin ]|1,Berlin [Hamburg]|1,Hamburg [ Dresden ] |1,Dresden [ Pirna ] { color: red; }|1,Pirna [ Bonn ] -> [ Berlin ]|2,Berlin,Bonn [ Bonn ] -> [ Berlin ]\n[Berlin] -> [Frankfurt]|3,Berlin,Bonn,Frankfurt [ Bonn ] ==> [ Berlin ]\n[Berlin] -> [Frankfurt]|3,Berlin,Bonn,Frankfurt [ Bonn ] = > [ Berlin ]\n[Berlin] -> [Frankfurt]|3,Berlin,Bonn,Frankfurt [ Bonn ] ~~> [ Berlin ]\n[Berlin] -> [Frankfurt]|3,Berlin,Bonn,Frankfurt [ Bonn ] ..> [ Berlin ]\n[Berlin] -> [Frankfurt]|3,Berlin,Bonn,Frankfurt [ Bonn ] - > [ Berlin ]\n[Berlin] -> [Frankfurt]|3,Berlin,Bonn,Frankfurt [ Bonn \( \#1 \) ] - > [ Berlin ]\n[Berlin] -> [Frankfurt]|3,Berlin,Bonn ( #1 ),Frankfurt [ Bonn ] { color: red; }\n[Berlin] -> [Frankfurt]|3,Berlin,Bonn,Frankfurt [Bonn]{color:red;}\n[Berlin]->[Frankfurt]|3,Berlin,Bonn,Frankfurt [ Bonn ] { color: red; } -> [ Berlin ]\n[Berlin] -> [Frankfurt]|3,Berlin,Bonn,Frankfurt [ Bonn ] { color: red; } -> [ Berlin ] {color: blue} \n[Berlin] -> [Frankfurt]|3,Berlin,Bonn,Frankfurt [ Bonn ] { color: #fff; } -> [ Berlin ] { color: #A0a0A0 } # failed in v0.09 [ Bonn ] -> [ Ulm ]|2,Berlin,Bonn [ Bonn ] { color: #fff; } -> [ Berlin ] { color: #A0a0A0 } #80808080 failed in v0.09 [ Bonn ] -> [ Ulm ]|2,Berlin,Bonn [ Bonn ] { color: #fff; } -> [ Berlin ] { color: #A0a0A0 } #808080 failed in v0.09 [ Bonn ] -> [ Ulm ]|2,Berlin,Bonn # node chains [ Bonn ] -> [ Berlin ]\n -> [ Kassel ]|3,Berlin,Bonn,Kassel [ Bonn ] { color: #fff; } -> [ Berlin ] { color: #A0a0A0 }\n -> [ Kassel ] { color: red; }|3,Berlin,Bonn,Kassel [ Bonn ] -> [ Berlin ] -> [ Kassel ]|3,Berlin,Bonn,Kassel [ Bonn ] { color: #fff; } -> [ Berlin ] { color: #A0a0A0 } -> [ Kassel ] { color: red; }|3,Berlin,Bonn,Kassel [ Bonn ] -> [ Berlin ]\n -> [ Kassel ] -> [ Koblenz ]|4,Berlin,Bonn,Kassel,Koblenz [ Bonn ] -> [ Berlin ] -> [ Kassel ]\n -> [ Koblenz ]|4,Berlin,Bonn,Kassel,Koblenz [ Bonn ] -> [ Berlin ] -> [ Kassel ] -> [ Koblenz ]|4,Berlin,Bonn,Kassel,Koblenz # attributes with ":" in their value [ Bonn ] { link: http://www.bloodgate.com/Bonn; }|1,Bonn # attributes "link", "autolink", and "linkbase": [ Bonn ] { linkbase: http://www.bloodgate.com/; autolink: name; }|1,Bonn [ Bonn ] { autolink: none; }|1,Bonn [ Bonn ] { autolink: title; }|1,Bonn [ Bonn ] { autolink: name; }|1,Bonn [ Bonn ] { autotitle: label; }|1,Bonn [ Bonn ] { autotitle: name; }|1,Bonn [ Bonn ] { autotitle: none; }|1,Bonn [ Bonn ] { title: my title; }|1,Bonn [ Bonn ] { shape: point; point-style: square; }|1,Bonn [ Bonn ] { background: red; }|1,Bonn [ Bonn ] { background: rgb(255,0,0); }|1,Bonn [ Bonn ] { background: rgb(100%,0,0); }|1,Bonn [ Bonn ] { background: rgb(0.0,0.5,1.0); }|1,Bonn [ Bonn ] { background: rgb(100%,0.5,12); }|1,Bonn [ Bonn ] { background: #ff0000; }|1,Bonn [ Bonn ] { background: #ff0; }|1,Bonn node.red { background: red; } [ Bonn ] { class: red; }|1,Bonn edge.red { background: red; } [ Bonn ] -> { class: red; } [ Berlin ]|2,Berlin,Bonn graph { background: red; } [ Bonn ] -> [ Berlin ]|2,Berlin,Bonn # edges with label # matching sides [ Bonn ] - Auto -> [ Berlin ]|2+1,Auto,Berlin,Bonn [ Bonn ] ~ Auto ~> [ Berlin ]|2+1,Auto,Berlin,Bonn [ Bonn ] . Auto .> [ Berlin ]|2+1,Auto,Berlin,Bonn [ Bonn ] = Auto => [ Berlin ]|2+1,Auto,Berlin,Bonn [ Bonn ] -- Auto --> [ Berlin ]|2+1,Auto,Berlin,Bonn [ Bonn ] == Auto ==> [ Berlin ]|2+1,Auto,Berlin,Bonn [ Bonn ] ~~ Auto ~~> [ Berlin ]|2+1,Auto,Berlin,Bonn [ Bonn ] .. Auto ..> [ Berlin ]|2+1,Auto,Berlin,Bonn # with pattern in the middle [ Bonn ] -- Au-to --> [ Berlin ]|2+1,Au-to,Berlin,Bonn [ Bonn ] == Au--to ==> [ Berlin ]|2+1,Au--to,Berlin,Bonn # groups ( Group [ Bonn ] -- Auto --> [ Berlin ] )|2+1,Auto,Berlin,Bonn,Group ( Group [ Bonn ] --> [ Berlin ] )|2,Berlin,Bonn,Group # lists [ Bonn ], [ Berlin ]\n --> [ Hamburg ]|3,Berlin,Bonn,Hamburg [ Bonn ], [ Berlin ] --> [ Hamburg ]|3,Berlin,Bonn,Hamburg [ Bonn ], [ Berlin ], [ Ulm ] --> [ Hamburg ]|4,Berlin,Bonn,Hamburg,Ulm [ Bonn ], [ Berlin ], [ Ulm ] --> [ Hamburg ] [ Trier ] --> [ Ulm ]|5,Berlin,Bonn,Hamburg,Trier,Ulm ( Group [ Bonn ], [ Berlin ] => [ Leipzig ] ) { color: red; }|3,Berlin,Bonn,Leipzig,Group [ Bonn ] -> [ Berlin ]\n --> { color: red; } [ Leipzig ]|3,Berlin,Bonn,Leipzig [ Bonn ] --> { label: test; } [ Berlin ]|2+1,Berlin,Bonn,test [ Bonn ] --> { label: test; } [ Berlin ] { color: blue; }|2+1,Berlin,Bonn,test [ Bonn ] --> { label: test; } [ Berlin ] { color: blue; }|2+1,Berlin,Bonn,test [ Bonn ] --> { label: test; } [ Berlin ] { color: blue; } --> { label: test2; } [ Leipzig ]|3+2,Berlin,Bonn,Leipzig,test,test2 # undirected edges [ Bonn ] -- [ Berlin ]|2,Berlin,Bonn [ Bonn ] -- [ Berlin ] [Ulm] --> [ Mainz]|4,Berlin,Bonn,Mainz,Ulm [ Bonn ] -- { color: red; } [ Berlin ] [Ulm] --> [ Mainz]|4,Berlin,Bonn,Mainz,Ulm # left over attributes due to node consumed first [ Bonn ]\n { color: red; } --> [ Berlin ]|2,Berlin,Bonn [ Bonn ] { color:\n red; } --> [ Berlin ]|2,Berlin,Bonn ( Group [ Bonn ] ) { color: red; }|1,Bonn,Group ([Bonn]){color:red;}|1,Bonn,Group #0 (0[Bonn]){color:red;}|1,Bonn,0 [ $sys$Node ]|1,$sys$Node # lists on the right side [ Bonn ] -- test --> [ Berlin], [ Chemnitz ]|3+2,Berlin,Bonn,Chemnitz,test,test # empty group ()|0,Group #0 # empty group ( )|0,Group #0 # empty group with link ( )->[Bonn]|1,Bonn,Group #0 # empty group linked to another empty group ( )->( )|0,Group #0,Group #1 # link ending at empty group (#1 because Bonn is #0) [Bonn]->( )|1,Bonn,Group #1 # link ending at empty group, and starting at empty group # 0,1,3 (and not 0,1,2) because: # "()" - create first group # "->()" - create second group and *then* the edge (id #3) # "()" - create third group as "#3" ()->()->()|0,Group #0,Group #1,Group #3 # group w/o name ([Bonn])|1,Bonn,Group #0 # edge labels with escaped chars [ Bonn ] -- \[ A \] \<\> \=\-\. --> [ Berlin ]|2+1,Berlin,Bonn,[ A ] <> =-. # ERROR testing # no space [ Bonn ]--test-->[ Berlin ]|ERROR [ Bonn ]-- test-->[ Berlin ]|ERROR [ Bonn ]--test -->[ Berlin ]|ERROR [ Bonn ]-- test--> [ Berlin ]|ERROR [ Bonn ] -- test--> [ Berlin ]|ERROR # mismatching left/right side [ Bonn ] - Auto--> [ Berlin ]|ERROR [ Bonn ] - Auto --> [ Berlin ]|ERROR [ Bonn ] == Auto --> [ Berlin ]|ERROR # unknown edge style [ Bonn ] . > [ Berlin ]\n[Berlin] -> [Frankfurt]|ERROR [ Bonn ] . > [ Berlin ]\n[Berlin] -> [Frankfurt]|ERROR ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/drop.t����������������������������������������������������������������������������0000644�0000764�0000764�00000002502�11675050456�014703� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # test multiple calls to as_ascii()/as_boxart() as well as merge_nodes use Test::More; BEGIN { plan tests => 5; chdir 't' if -d 't'; use lib '../lib'; use_ok("Graph::Easy") or die (@!); use_ok("Graph::Easy::Parser") or die (@!); } my $graph = Graph::Easy::Parser->from_file('stress/drop.txt'); binmode STDOUT, ':utf8' or die ("binmode STDOUT, ':utf8' failed: $!"); binmode STDERR, ':utf8' or die ("binmode STDERR, ':utf8' failed: $!"); my $bonn = $graph->node('Bonn'); my $first = $graph->as_ascii(); my $second = $graph->as_ascii(); is ($first, $second, 'two times as_ascii() changes nothing'); $first = $graph->as_boxart(); $second = $graph->as_boxart(); is ($first, $second, 'two times as_boxart() changes nothing'); # drop any connection between Bonn and Berlin, as well as self-loops # from Berlin to Berlin $graph->merge_nodes('Bonn', 'Berlin'); my $result = $first . "\n" . $graph->as_boxart(); my $expected = readfile('out/drop_result.txt'); is ($result, $expected, 'dropping a node works'); # all tests done 1; sub readfile { my ($file) = @_; open FILE, $file or die ("Cannot read file $file: $!"); binmode FILE, ':utf8' or die ("binmode $file, ':utf8' failed: $!"); local $/ = undef; # slurp mode my $doc = <FILE>; close FILE; $doc; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/cell.t����������������������������������������������������������������������������0000644�0000764�0000764�00000005167�11675050456�014670� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # Test Graph::Easy::Node::Cell use Test::More; use strict; BEGIN { plan tests => 28; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy::Node::Cell") or die($@); use_ok ("Graph::Easy") or die($@); use_ok ("Graph::Easy::As_ascii") or die($@); }; can_ok ("Graph::Easy::Node::Cell", qw/ new as_ascii as_html error class name successors predecessors width height pos x y class title del_attribute set_attribute set_attributes attribute group add_to_group /); ############################################################################# my $cell = Graph::Easy::Node::Cell->new(); is (ref($cell), 'Graph::Easy::Node::Cell'); is ($cell->error(), '', 'no error yet'); is ($cell->x(), 0, 'x == 0'); is ($cell->y(), 0, 'x == 0'); is ($cell->label(), '', 'label'); is ($cell->name(), '', 'name'); is ($cell->title(), '', 'no title per default'); is (join(",", $cell->pos()), "0,0", 'pos = 0,0'); is ($cell->width(), undef, 'w == undef'); is ($cell->height(), undef, 'h == undef'); is ($cell->class(), '', 'no class'); ############################################################################# # as_ascii/as_html is ($cell->as_ascii(), '', 'as_ascii'); is ($cell->as_html(), '', 'as_html'); $cell->_correct_size(); is ($cell->width(), 0, 'w = 0'); is ($cell->height(), 0, 'h = 0'); ############################################################################# # group tests use Graph::Easy::Group; my $group = Graph::Easy::Group->new( { name => 'foo' } ); # fake that the cell belongs as filler to a node my $node = Graph::Easy::Node->new( 'foo' ); $cell->{node} = $node; is ($cell->node(), $node, 'node for cell'); is ($cell->group(), undef, 'no group yet'); $node->add_to_group($group); is ($cell->node(), $node, 'node for cell'); is ($cell->group(), $group, 'group foo'); ############################################################################# # title tests $cell->set_attribute('title', "foo title"); is ($cell->title(), 'foo title', 'foo title'); $cell->del_attribute('title'); $cell->set_attribute('autotitle', 'name'); is ($cell->title(), $cell->name(), 'title equals name'); ############################################################################# # invisible nodes $node = Graph::Easy::Node->new( { name => "anon 0", label => 'X' } ); $node->set_attribute('shape', "invisible"); is ($node->as_ascii(), "", 'invisible text node'); ############################################################################# # as_txt() use_ok ('Graph::Easy::As_txt'); can_ok ("Graph::Easy::Node::Cell", qw/ attributes_as_txt as_txt as_pure_txt /); ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/parse_edge.t����������������������������������������������������������������������0000644�0000764�0000764�00000005224�11675050456�016041� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use Test::More; use strict; BEGIN { plan tests => 83; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy::Parser") or die($@); }; can_ok ("Graph::Easy::Parser", qw/ new from_text from_file reset error _match_edge _match_node _match_attributes _match_optional_attributes /); ############################################################################# # create parser object my $parser = Graph::Easy::Parser->new(); is (ref($parser), 'Graph::Easy::Parser'); is ($parser->error(), '', 'no error yet'); my $line = 0; my $qr_edge = $parser->_match_edge(); my $qr_node = $parser->_match_node(); my $qr_oatr = $parser->_match_optional_attributes(); #my $r = '-- Label --> [ AA ]'; #$r = '--> [ AB ]'; #$r = '<--> [ AB ]'; #$r = '<- Label -> [ AB ]'; #$r = '--> { color: red; } [ AB ]'; foreach my $l (<DATA>) { chomp ($l); next if $l =~ /^\s*\z/; # skip empty ones next if $l =~ /^#/; # skip comments my ($in,$type,$style,$label) = split /\|/, $l; if ($type < 0) { if (!unlike ($in, qr/^$qr_edge\z/, "$in")) { $in =~ /^$qr_edge/; diag ("# '$1' '$2' '$3' '$4' '$5' '$6'\n"); } next; } # XXX TODO check edge style and type: # 0 - undirected # 2 - right # 3 - left and right like ($in, qr/^$qr_edge\z/, "$in"); # $in =~ /^$qr_edge\z/; # diag("# '$1' '$2' '$3' '$4' '$5' '$6' '$7' '$8' '$9'\n"); } __DATA__ # edges without arrows --|0|-- ==|0|== ..|0|.. - |-1| - - - |0| - ---|0|-- ===|0|== ...|0|.. - - |0| - ----|0|-- ====|0|== ....|0|.. <->|3|-- <=>|3|== <.>|3|.. <- >|3| - <-->|3|-- <==>|3|== <..>|3|.. <- - >|3| - <--->|3|-- <===>|3|== <...>|3|.. <- - >|3| - ->|2|-- =>|2|== .>|2|.. - >|2| - -->|2|-- ==>|2|== ..>|2|.. ~~>|2|~~ = >|2|= - - >|2| - --->|2|-- ===>|2|== ...>|2|.. - - >|2| - # with labels <- ->|3| - - Landstrasse --|-1|-- == Autobahn ==>|2|== .. Im Bau ..>|2|.. - Tunnel - >|2| - = label =>|2|==|label <-- Landstrasse -->|3|-- <== Autobahn ==>|3|== <.. Im Bau ..>|3|.. <- Tunnel - >|3| - <- Tunnel -->|-1| <-- Tunnel -->|3| <-- Landstrasse -->|3|-- <~~ Landstrasse ~~>|3|~~ <== Landstrasse ==>|3|== <.- Landstrasse .->|3|.- <..- Landstrasse ..->|3|..- -- Landstrasse -->|2|-- ~~ Landstrasse ~~>|2|~~ == Landstrasse ==>|2|== .- Landstrasse .->|2|.- ..- Landstrasse ..->|2|..- ################## # Failures # no left-only edges allowed <-|-1|-- <=|-1|== <.|-1|.. <- |-1| - <--|-1|-- <==|-1|== <..|-1|.. <- -|-1| - <-- Landstrasse -|-1| <== Autobahn =|-1| <.. Im Bau .|-1| <- - Tunnel -|-1| <--|-1| # mismatching pattern <-- Landstrasse ==>|-1| # double "<<" or ">>" are not good <<--|-1| <<--|-1| <<-->>|-1| <<. -.->>|-1| < - Tunnel - >|-1| ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/graphml_yed.t���������������������������������������������������������������������0000644�0000764�0000764�00000013732�11675050456�016241� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # Some basic GraphML tests with the format=yED use Test::More; use strict; use utf8; BEGIN { plan tests => 14; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy") or die($@); use_ok ("Graph::Easy::Parser") or die($@); }; can_ok ('Graph::Easy', qw/ as_graphml as_graphml_file /); ############################################################################# my $graph = Graph::Easy->new(); my $graphml_file = $graph->as_graphml_file( format => 'yED' ); $graphml_file =~ s/\n.*<!--.*-->\n//; _compare ($graph, $graphml_file, 'as_graphml and as_graphml_file are equal'); my $graphml = $graph->as_graphml( format => 'yED' ); like ($graphml, qr/<\?xml version="1.0" encoding="UTF-8"\?>/, 'as_graphml looks like xml'); ############################################################################# # some nodes and edges $graph->add_edge('Ursel','Viersen'); $graphml = $graph->as_graphml(); like ($graphml, qr/<node.*id="Ursel"/, 'as_graphml contains nodes'); like ($graphml, qr/<node.*id="Viersen"/, 'as_graphml contains nodes'); like ($graphml, qr/<edge.*source="Ursel"/, 'as_graphml contains edge'); like ($graphml, qr/<edge.*target="Viersen"/, 'as_graphml contains edge'); ############################################################################# # some attributes: # node.foo { color: red; } [A] {class:foo;}-> { color: blue; } [B] $graph = Graph::Easy->new(); my ($A,$B,$edge) = $graph->add_edge('A','B'); $graph->set_attribute('node.foo','color','red'); $edge->set_attribute('color','blue'); $A->set_attribute('class','foo'); my $result = <<EOT <key id="d0" for="node" attr.name="color" attr.type="string"> <default>black</default> </key> <key id="d1" for="edge" attr.name="color" attr.type="string"> <default>black</default> </key> <graph id="G" edgedefault="directed"> <node id="A"> <data key="d0">red</data> </node> <node id="B"> </node> <edge source="A" target="B"> <data key="d1">blue</data> </edge> </graph> </graphml> EOT ; _compare($graph, $result, 'GraphML with attributes'); ############################################################################# # some attributes with no default valu with no default value: # Also test escaping for valid XML: $edge->set_attribute('label', 'train-station & <Überlingen "Süd">'); $result = <<EOT2 <key id="d0" for="node" attr.name="color" attr.type="string"> <default>black</default> </key> <key id="d1" for="edge" attr.name="color" attr.type="string"> <default>black</default> </key> <key id="d2" for="edge" attr.name="label" attr.type="string"/> <graph id="G" edgedefault="directed"> <node id="A"> <data key="d0">red</data> </node> <node id="B"> </node> <edge source="A" target="B"> <data key="d1">blue</data> <data key="d2">train-station & <Überlingen "Süd"></data> </edge> </graph> </graphml> EOT2 ; _compare($graph, $result, 'GraphML with attributes'); ############################################################################# # node names with things that need escaping: $graph->rename_node('A', '<&\'">'); $result = <<EOT3 <key id="d0" for="node" attr.name="color" attr.type="string"> <default>black</default> </key> <key id="d1" for="edge" attr.name="color" attr.type="string"> <default>black</default> </key> <key id="d2" for="edge" attr.name="label" attr.type="string"/> <graph id="G" edgedefault="directed"> <node id="<&'">"> <data key="d0">red</data> </node> <node id="B"> </node> <edge source="<&'">" target="B"> <data key="d1">blue</data> <data key="d2">train-station & <Überlingen "Süd"></data> </edge> </graph> </graphml> EOT3 ; _compare($graph, $result, 'GraphML with attributes'); ############################################################################# # double attributes $graph = Graph::Easy->new(); ($A,$B,$edge) = $graph->add_edge('A','B'); my ($C,$D,$edge2) = $graph->add_edge('A','C'); $edge->set_attribute('label','car'); $edge2->set_attribute('label','train'); $result = <<EOT4 <key id="d0" for="edge" attr.name="label" attr.type="string"/> <graph id="G" edgedefault="directed"> <node id="A"> </node> <node id="B"> </node> <node id="C"> </node> <edge source="A" target="B"> <data key="d0">car</data> </edge> <edge source="A" target="C"> <data key="d0">train</data> </edge> </graph> </graphml> EOT4 ; _compare($graph, $result, 'GraphML with attributes'); ############################################################################# # as_graphml() with groups (bug until v0.63): $graph = Graph::Easy->new(); my $bonn = Graph::Easy::Node->new('Bonn'); my $cities = $graph->add_group('Cities"'); $cities->add_nodes($bonn); $result = <<EOT5 <graph id="G" edgedefault="directed"> <graph id="Cities"" edgedefault="directed"> <node id="Bonn"> </node> </graph> </graph> </graphml> EOT5 ; _compare($graph, $result, 'GraphML with group'); # all tests done ############################################################################# ############################################################################# sub _compare { my ($graph, $result, $name) = @_; my $graphml = $graph->as_graphml( { format => 'yED' } ); $graphml =~ s/\n.*<!--.*-->\n//; $result = <<EOR <?xml version="1.0" encoding="UTF-8"?> <graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.0/ygraphml.xsd"> EOR . $result unless $result =~ /<\?xml/; if (!is ($result, $graphml, $name)) { eval { require Test::Differences; }; if (defined $Test::Differences::VERSION) { Test::Differences::eq_or_diff ($result, $graphml); } } } ��������������������������������������Graph-Easy-0.73/t/edge_cell.t�����������������������������������������������������������������������0000644�0000764�0000764�00000006055�11675050456�015651� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # Test Graph::Easy::Edge::Cell use Test::More; use strict; BEGIN { plan tests => 25; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy::Edge::Cell") or die($@); use_ok ("Graph::Easy") or die($@); use_ok ("Graph::Easy::As_ascii") or die($@); }; can_ok ("Graph::Easy::Edge::Cell", qw/ new as_ascii as_html error pos x y label width height style type edge_type _draw_cross _draw_ver _draw_hor _draw_corner _make_cross /); use Graph::Easy::Edge::Cell qw/ EDGE_SHORT_W EDGE_CROSS EDGE_END_N EDGE_START_E EDGE_HOR EDGE_VER EDGE_N_W_S /; use Graph::Easy::Edge; ############################################################################# my $edge = Graph::Easy::Edge->new(); my $path = Graph::Easy::Edge::Cell->new( edge => $edge ); is (ref($path), 'Graph::Easy::Edge::Cell'); is ($path->error(), '', 'no error yet'); is ($path->x(), 0, 'x == 0'); is ($path->y(), 0, 'x == 0'); is ($path->label(), '', 'no label'); is (join(",", $path->pos()), "0,0", 'pos = 0,0'); is ($path->width(), undef, 'w = undef'); # no graph => thus no width yet $path = Graph::Easy::Edge::Cell->new( edge => $edge, type => EDGE_SHORT_W); is ($path->type(), EDGE_SHORT_W, 'edge to the left'); ############################################################################# # attribute() $edge->set_attribute( color => 'blue', border => 'none'); $path = Graph::Easy::Edge::Cell->new( type => EDGE_SHORT_W, edge => $edge); is ($path->attribute('color'), 'blue'); ############################################################################# # as_txt/as_ascii $path->_correct_size(); is ($path->{w}, 5, 'w == 5'); is ($path->{h}, 3, 'h == 3'); my $ascii = $path->as_ascii(0,0); $ascii =~ s/^\s+//; $ascii =~ s/\s+\z//; is ($ascii, "<--", 'as ascii'); # rendering of seems $edge = Graph::Easy::Edge->new( style => 'dot-dash' ); $path = Graph::Easy::Edge::Cell->new( type => EDGE_HOR, edge => $edge); $path->{w} = 10; $ascii = $path->as_ascii(0,0); $ascii =~ s/^\s+//; $ascii =~ s/\s+\z//; is ($ascii, ".-.-.-.-.-", 'as ascii'); $ascii = $path->as_ascii(1,0); $ascii =~ s/^\s+//; $ascii =~ s/\s+\z//; is ($ascii, "-.-.-.-.-.", 'as ascii'); my $other = Graph::Easy::Edge->new( style => 'dashed' ); $path->{type} = EDGE_HOR; $path->_make_cross($other); $ascii = $path->as_ascii(); is ($ascii, " ' \n.-+-.-.-.-\n ' ", 'crossing between dot-dash and dashed'); $path->{style} = 'dotted'; $path->{style_ver} = 'solid'; $ascii = $path->as_ascii(); is ($ascii, " | \n..!.......\n | ", 'crossing between dotted and solid'); ############################################################################# # edge_type() my $et = 'Graph::Easy::Edge::Cell::edge_type'; { no strict 'refs'; is (&$et( EDGE_HOR() ), 'horizontal', 'EDGE_HOR'); is (&$et( EDGE_VER() ), 'vertical', 'EDGE_VER'); is (&$et( EDGE_CROSS() ), 'crossing', 'EDGE_CROSS'); is (&$et( EDGE_SHORT_W() ), 'horizontal, ending west, starting east', 'EDGE_SHORT_W'); is (&$et( EDGE_N_W_S() ), 'selfloop, northwards', 'EDGE_N_W_S'); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/ascii.t���������������������������������������������������������������������������0000644�0000764�0000764�00000006543�12150101414�015014� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use Test::More; use strict; # test text file input => ASCII output, and back to as_txt() again BEGIN { plan tests => 451; # TEST use_ok ("Graph::Easy") or die($@); # TEST use_ok ("Graph::Easy::Parser") or die($@); }; ############################################################################# # parser object my $parser = Graph::Easy::Parser->new( debug => 0); is (ref($parser), 'Graph::Easy::Parser'); is ($parser->error(), '', 'no error yet'); opendir DIR, "t/in" or die ("Cannot read dir 'in': $!"); my @files = readdir(DIR); closedir(DIR); my @failures; eval { require Test::Differences; }; binmode (STDERR, ':utf8') or die ("Cannot do binmode(':utf8') on STDERR: $!"); binmode (STDOUT, ':utf8') or die ("Cannot do binmode(':utf8') on STDOUT: $!"); foreach my $f (sort @files) { my $path = "t/in/$f"; next unless -f $path; # only files next unless $f =~ /\.txt/; # ignore anything else print "# at $f\n"; my $txt = readfile($path); my $graph = $parser->from_text($txt); # reuse parser object $txt =~ s/\n\s+\z/\n/; # remove trailing whitespace $txt =~ s/(^|\n)\s*#[^#]{2}.*\n//g; # remove comments $f =~ /^(\d+)/; my $nodes = $1; if (!defined $graph) { warn ("Graph input was invalid: " . $parser->error()); push @failures, $f; next; } is (scalar $graph->nodes(), $nodes, "$nodes nodes"); # for slow testing machines $graph->timeout(20); my $ascii = $graph->as_ascii(); my $out_path = "t/out/$f"; my $out = readfile($out_path); $out =~ s/(^|\n)\s*#[^#=]{2}.*\n//g; # remove comments $out =~ s/\n\n\z/\n/mg; # remove empty lines # print "txt: $txt\n"; # print "ascii: $ascii\n"; # print "should: $out\n"; if (! is ($ascii, $out, "from $f")) { if ($ENV{__SHLOMIF__UPDATE_ME}) { require IO::All; IO::All->new->file($out_path)->utf8->print($ascii); } push @failures, $f; if (defined $Test::Differences::VERSION) { Test::Differences::eq_or_diff ($ascii, $out); } else { fail ("Test::Differences not installed"); } } my $txt_path = "t/txt/$f"; # if the txt output differes, read it in if (-f $txt_path) { $txt = readfile($txt_path); } # else # { # # input might have whitespace at front, remove it because output doesn't # $txt =~ s/(^|\n)\x20+/$1/g; # } if (! is ($graph->as_txt(), $txt, "$f as_txt")) { if ($ENV{__SHLOMIF__UPDATE_ME}) { require IO::All; IO::All->new->file($txt_path)->utf8->print($graph->as_txt()); } push @failures, $f; if (defined $Test::Differences::VERSION) { Test::Differences::eq_or_diff ($graph->as_txt(), $txt); } else { fail ("Test::Differences not installed"); } } # print a debug output my $debug = $ascii; $debug =~ s/\n/\n# /g; print "# Generated:\n#\n# $debug\n"; } if (@failures) { print "# !!! Failed the following tests:\n"; for my $f (@failures) { print "# $f\n"; } print "# !!!\n\n"; } 1; sub readfile { my ($filename) = @_; open my $fh, $filename or die ("Cannot read file ${filename}: $!"); binmode ($fh, ':utf8') or die ("Cannot do binmode(':utf8') on ${fh}: $!"); local $/ = undef; # slurp mode my $doc = <$fh>; close $fh; $doc; } �������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/path.t����������������������������������������������������������������������������0000644�0000764�0000764�00000001356�11675050456�014701� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use Test::More; use strict; BEGIN { plan tests => 7; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy::Layout") or die($@); use_ok ("Graph::Easy") or die($@); }; can_ok ("Graph::Easy", qw/ _path_is_clear /); my $path = [ 0,0,0, 1,0,0, 2,0,0, 2,1,0, 2,2,0, 2,3,0, 2,4,0, ]; my $cells = {}; ############################################################################# # path tests my $graph = Graph::Easy->new(); is (ref($graph), 'Graph::Easy'); is ($graph->error(), '', 'no error yet'); $graph->{cells} = $cells; is ($graph->_path_is_clear( $path, $cells), 1, 'path is clear'); $cells->{"2,2"} = 1; is ($graph->_path_is_clear( $path, $cells), 0, 'path is blocked'); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/anon_group.t����������������������������������������������������������������������0000644�0000764�0000764�00000003532�11675050456�016112� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # test anonymous groups use Test::More; use strict; BEGIN { plan tests => 15; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy::Group::Anon") or die($@); use_ok ("Graph::Easy") or die($@); use_ok ("Graph::Easy::As_txt") or die($@); require_ok ("Graph::Easy::As_ascii") or die($@); }; can_ok ("Graph::Easy::Group::Anon", qw/ new as_txt as_html error class name successors predecessors width height pos x y class del_attribute set_attribute set_attributes attribute attributes_as_txt as_pure_txt group add_to_group /); ############################################################################# my $group = Graph::Easy::Group::Anon->new(); is (ref($group), 'Graph::Easy::Group::Anon'); is ($group->error(), '', 'no error yet'); is ($group->label(), '', 'label'); is ($group->name(), 'Group #0', 'name'); is ($group->title(), '', 'no title per default'); is ($group->{graph}, undef, 'no graph'); is (scalar $group->successors(), undef, 'no outgoing links'); is (scalar $group->predecessors(), undef, 'no incoming links'); is ($group->{graph}, undef, 'successors/predecssors leave graph alone'); ############################################################################# # as_txt/as_html my $graph = Graph::Easy->new(); $graph->add_group($group); is ($group->as_txt(), "( )\n\n", 'anon group as_txt'); #is ($group->as_html(), " <td colspan=4 rowspan=4 class='node_anon'></td>\n", # 'as_html'); #is ($group->as_ascii(), "", 'anon as_ascii'); #is ($group->as_graphviz_txt(), '"\#0"', 'anon as_graphviz'); ############################################################################# # anon node as_graphviz #my $grviz = $graph->as_graphviz(); #my $match = quotemeta('"\#0" [ color="#ffffff", label=" ", style=filled ]'); #like ($grviz, qr/$match/, 'anon node'); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/delete.t��������������������������������������������������������������������������0000644�0000764�0000764�00000005207�11675050456�015206� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # Test deletion of nodes and edges use Test::More; use strict; BEGIN { plan tests => 46; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy") or die($@); }; can_ok ("Graph::Easy", qw/ del_node del_edge /); ############################################################################# # first we add edges/nodes my $graph = Graph::Easy->new(); is (ref($graph), 'Graph::Easy'); is ($graph->error(), '', 'no error yet'); $graph->add_edge('Bonn', 'Berlin'); # check that it contains 2 nodes and one edge is_ok ($graph); ############################################################################# print "# add edge, delete it again\n"; my $edge = $graph->add_edge('Bonn', 'Berlin'); $graph->del_edge($edge); # check that it contains 2 nodes and one edge is_ok ($graph); ############################################################################# print "# add selfloop edge, delete it again\n"; $edge = $graph->add_edge('Bonn', 'Bonn'); $graph->del_edge($edge); # check that it contains 2 nodes and one edge is_ok ($graph); $edge = $graph->add_edge('Berlin', 'Berlin'); $graph->del_edge($edge); # check that it contains 2 nodes and one edge is_ok ($graph); ############################################################################# print "# add node, delete it again\n"; my $node = $graph->add_node('Cottbus'); $graph->del_node($node); # check that it contains 2 nodes and one edge is_ok ($graph); ############################################################################# print "# add node with edge, delete it again\n"; my ($n1, $n2, $e) = $graph->add_edge('Cottbus', 'Bonn'); $graph->del_node($n1); # check that it contains 2 nodes and one edge is_ok ($graph); ($n1, $n2, $e) = $graph->add_edge('Cottbus', 'Bonn'); ($n1, $n2, $e) = $graph->add_edge('Cottbus', 'Berlin'); $graph->del_node($n1); # check that it contains 2 nodes and one edge is_ok ($graph); 1; # all tests done ############################################################################# # test graph after deletion sub is_ok { my $graph = shift; is ($graph->nodes(), 2, '2 nodes'); is ($graph->edges(), 1, '1 edge'); my $t = ''; for my $n (sort { $a->{name} cmp $b->{name} } $graph->nodes()) { $t .= $n->name(); } is ($t, 'BerlinBonn', 'two nodes'); my $bonn = $graph->node('Bonn'); my $berlin = $graph->node('Berlin'); is (scalar keys %{$bonn->{edges}}, 1, 'one edge'); is (scalar keys %{$berlin->{edges}}, 1, 'one edge'); my $ids = join (',', keys %{$bonn->{edges}}, keys %{$berlin->{edges}}, keys %{$graph->{edges}} ); is ($ids, '0,0,0', 'edge with ID is the only one'); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/fun/������������������������������������������������������������������������������0000755�0000764�0000764�00000000000�12150110221�014313� 5����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/fun/0030.txt����������������������������������������������������������������������0000644�0000764�0000764�00000000761�11675050456�015472� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Example from Graphviz extension # http://www.wickle.com/wiki/index.php/Graphviz_extension node { shape: circle; } [ main ] { shape: rect; } # this is a comment -> [ parse ] -> [ execute ] -> [ make_string ] { label: make a\n string; } [ main ] ..> [ init ] [ main ] -- Testlabel --> [ cleanup ] [ init ] -> [ make_string ] [ main ] == 100 times ==> { color: red; style: bold; } [ printf ] # bold red edge! [ execute ] -> { color: red; } [ compare ] { shape: rect; background: #c1b2ff; } ���������������Graph-Easy-0.73/t/fun/0020.txt����������������������������������������������������������������������0000644�0000764�0000764�00000000320�11675050456�015460� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Enlightment # http://bloodgate.com/perl/graph/ [ Textual description ] { fill: #804020; } -> [ Parser ] { fill: #a06040; } -> [ Layouter ] { fill: #d08060; } -> [ Output (ASCII/HTML) ] { fill: #ffa090; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/fun/0011.txt����������������������������������������������������������������������0000644�0000764�0000764�00000000506�11675050456�015466� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Elan and Roy on Teamwork # http://www.cafepress.com/orderofthestick.10272636?zoom=yes#zoom [ Teamwork ] { fill: yellow; title: Elan; } -- is the key to --> [ Victory ] { fill: red; title: Roy; } [ Victory ] -- the order of the --> [ Stick ] { fill: #ff80a0; } [ Victory ] -- Treasure --> [ Haley ] { fill: lightblue; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/fun/geek_dating.txt���������������������������������������������������������������0000644�0000764�0000764�00000003575�11675050456�017357� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# The famous 1999 geek dating flow char from userfriendly.org # http://www.userfriendly.org/cartoons/archives/99mar/19990314.html graph { label: GEEK DATING FLOWCHART; font-size: 3em; color: red; font: serif; } node { text-wrap: auto; color: black; font-size: 0.8em; } edge { color: black; } edge.yes { label: Yes; } edge.no { label: No; } [ Start ] { label: Do you have someone in mind? } --> { class: no; flow: forward; } [ GPF ] { fill: #00b0ff; } [ Start ] --> { class: yes; } [ Available ] { label: Are they available? } --> { class: yes; } [ Exist ] { label: Do they know you exist? } --> { class: yes; } [ Truly remararkable. They're available and they know you exist. Will wonders never cease. ] { origin: Exist; offset: 0,4; } --> { end: back, 0; } [ Askout ] { label: So now you just need to ask them out on a date. What do they say? } --> { class: yes; } [ Hahahaha! Yeah. Right. ] --> { start: front,0; end: right, 0; } [ End ] { label: Well, you can still have a date with your Palm S<Pilot>; format: pod; } [ Available ] --> { class: no; } [ GPF ] [ Exist ] --> { class: no; } [ Ask them if they like Computers: ] --> { class: no; } [ Ask them if they like 'Star Wars': ] --> { class: no; } [ Ask them if they like the "Hitchhiker's Guide': ] --> { class: no; } [ Qualities ] { label: Ask them if they have any redeeming qualities at all: } [ Ask them if they like Computers: ], [ Ask them if they like 'Star Wars': ] [ Ask them if they like the "Hitchhiker's Guide': ], [ Qualities ] --> { class: yes; end: back, 0; } [ Lying ] { label: They're lying. Ditch them; } [ Qualities ] --> { class: no; } [ Smell ] { label: At least they're honest. Ask them if you smell bad: } --> { class: no; } [ Lying ] --> [ End ] [ Smell ] --> { class: yes;} [ Yup, they're honest all right. But now you know they think you smell bad. Ditch them. ] --> [ End ] �����������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/fun/0200.txt����������������������������������������������������������������������0000644�0000764�0000764�00000000635�11675050456�015471� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Example from: http://wiki.debian.org.hk/w/Generate_SSL_cert # http://wiki.debian.org.hk/w/Generate_SSL_cert [ Start ] { shape: rounded; } -- openssl\n genrsa --> [ key ] -- openssl\n req --> [ Certificate\n Signing\n Request\n (CSR) ] -> [ CA ] { label: "Certificate\n Authority\n (CA)"; shape: circle; } -> [ Certificate\n (CRT) ] [ CA ] -> [ Certificate\n Revocation\n List\n (CRL) ] [ CA Key ] -> [ CA ] ���������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/fun/0010.txt����������������������������������������������������������������������0000644�0000764�0000764�00000000307�11675050456�015464� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Elan and Roy on Teamwork # http://www.cafepress.com/orderofthestick.10272636?zoom=yes#zoom [ Teamwork ] { fill: yellow; title: Elan; } -- is the key to --> [ Victory ] { fill: red; title: Roy; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/fun/overview.txt������������������������������������������������������������������0000644�0000764�0000764�00000001165�11675050456�016755� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������graph { flow: south; } node.input { fill: lime; } node.output { fill: darkorange; } node.dot { fill: gold; } [ Text ], [ Graphviz ] { class: input; } -> [ Parser ] [ Perl ] { class: input; }, [ Parser ] -> [ Graph::Easy ] -> { start: front; } [ Text2 ] { label: Text; }, [ Graphviz2 ] { class: output } [ Graph::Easy ] -> [ Layouter ] [ Graphviz2 ] { label: Graphviz; } -> { minlen: 1; } [ dot ] { fill: #8080ff; } -> { start: front; } [ .pdf ], [ .png ], [ .ps ] { class: dot; } [ Layouter ] { offset: 2,0; origin: Graph::Easy; } -> { start: front; } [ ASCII /\nUnicode ], [ HTML ], [ SVG ] { class: output } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/fun/biofuel.txt�������������������������������������������������������������������0000644�0000764�0000764�00000001140�11675050456�016525� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Example from: http://www.islandseeds.org/wiki/Biofuel # http://www.islandseeds.org/wiki/Biofuel graph { flow: east; } node { shape: ellipse; } [ solar energy ] { fill: yellow; } [ water] { fill: cyan; } [ soil ] { fill: burlywood; } -> [ vegetation ] -> [ processing ] -> [ biofuel ] -> [ combustion ] -> [ useful work ] { fill: lightgray; } [ combustion ] -> [ carbon dioxide ] [ combustion ] -> [ pollution ] [ processing ] ..> [ compost ] ..> [ soil ] [ processing ] ..> [ carbon dioxide ] [ processing ] ..> [ pollution ] [ water ] -> [ vegetation ] [ solar energy ] -> [ vegetation ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/fun/0000.txt����������������������������������������������������������������������0000644�0000764�0000764�00000000761�11675050456�015467� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Example from Graphviz extension # http://www.wickle.com/wiki/index.php/Graphviz_extension node.red { background: red; } [ ns ] { class: red } -> [ addons ] -> [ metamod ] { class: red } -> [ dlls2 ] { label: dlls } [ metamod ] -> [ doc ] -> [html ] [ ns ] -> [ logs ] [ ns ] -> [ gfx ] -> [ vgui ] [ addons ] -> [ amxmodx ] -> [ configs ] { class: red } [ amxmodx ] -> [ data ] -> [ lang ] [ amxmodx ] -> [ dlls ] [ amxmodx ] -> [ plugins ] { class: red } [ doc ] -> [ txt ] ���������������Graph-Easy-0.73/t/fun/0131.txt����������������������������������������������������������������������0000644�0000764�0000764�00000000612�11675050456�015467� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Three non-overlapping groups group.dmz { fill: #ffa070; } group.outer { fill: #f07070; } ( Internal Network: [ Workstation ] ) { fill: #70b070; } [ Workstation ] --> [ Inner Firewall ] ( DMZ: [ Inner Firewall ] -> [ Proxy ] --> [ Outer Firewall ] [ Proxy ] --> [ Database\n Server ] ) { class: dmz; } ( Outer: [ Internet ] ) { class: outer } [ Outer Firewall ] --> [ Internet ] ����������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/class.t���������������������������������������������������������������������������0000644�0000764�0000764�00000003634�11675050456�015053� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # Test class selectors use Test::More; use strict; BEGIN { plan tests => 23; chdir 't' if -d 't'; use lib '../lib'; use_ok ("Graph::Easy") or die($@); }; can_ok ("Graph::Easy", qw/ _check_class /); ############################################################################# my $graph = Graph::Easy->new(); is (ref($graph), 'Graph::Easy'); $graph->add_edge( 'A', 'B' ); for my $class ('node', 'edge', 'graph', 'group', 'node.foo', 'edge.foo', 'group.foo') { _is ($class, $graph->_check_class($class)); } _is ('edge.foo,group.foo,node.foo', $graph->_check_class('.foo')); _is ('edge.b,group.b,node.b', $graph->_check_class('.b')); ############################################################################# # lists of class selectors _is ('edge.f,group.f,node.f,edge.b,group.b,node.b', $graph->_check_class('.f, .b')); _is ('edge,group,node', $graph->_check_class('edge, group, node')); _is ('edge,group,node', $graph->_check_class('edge,group, node')); _is ('edge,group,node', $graph->_check_class('edge , group , node')); _is ('edge,group,node', $graph->_check_class('edge, group,node')); _is ('edge,group,node', $graph->_check_class('edge,group,node')); _is ('edge.red,group.red,node.red,edge.green,group.green,node.green,group', $graph->_check_class('.red, .green, group')); ############################################################################# # invalid classes _is (\'.', $graph->_check_class('.')); _is (\'node.', $graph->_check_class('node.')); _is (\'foo', $graph->_check_class('foo')); _is (\'.foo, bar', $graph->_check_class('.foo, bar')); # all tests done 1; ############################################################################# sub _is { my ($expect, @results) = @_; if (ref($expect)) { is (scalar @results, 0, "invalid selector $$expect"); } else { is (join(",", @results), $expect, $expect); } } ����������������������������������������������������������������������������������������������������Graph-Easy-0.73/t/pod_cov.t�������������������������������������������������������������������������0000644�0000764�0000764�00000003526�11675050456�015377� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use Test::More; use strict; my $tests; BEGIN { $tests = 21; plan tests => $tests; chdir 't' if -d 't'; use lib '../lib'; }; SKIP: { skip("Test::Pod::Coverage 1.08 and Pod::Coverage 0.19 required for testing POD coverage", $tests) unless do { eval "use Test::Pod::Coverage 1.08"; my $r = ($@ ? 0 : 1); eval "use Pod::Coverage 0.19"; # need this on newer Perls to avoid false-fails $r & ($@ ? 0 : 1); # only return true if we have both }; for my $m (qw/ Graph::Easy Graph::Easy::Node Graph::Easy::Group Graph::Easy::Edge Graph::Easy::Base Graph::Easy::As_ascii Graph::Easy::As_txt Graph::Easy::As_graphviz Graph::Easy::As_vcg Graph::Easy::Layout::Chain Graph::Easy::Layout::Grid Graph::Easy::Layout::Path Graph::Easy::Layout::Scout Graph::Easy::Layout::Repair Graph::Easy::Attributes /) { pod_coverage_ok( $m, "$m is covered" ); } my $trustparents = { coverage_class => 'Pod::Coverage::CountParents' }; for my $m (qw/ Graph::Easy::Node::Anon Graph::Easy::Group::Anon /) { pod_coverage_ok( $m, $trustparents, "$m is covered" ); } # Define the global CONSTANTS for internal usage my $trustme = { trustme => [ qr/^( ACTION_CHAIN| ACTION_NODE| ACTION_TRACE| ACTION_EDGES| ACTION_SPLICE| ATTR_COLOR| ATTR_DEFAULT_SLOT| ATTR_DESC_SLOT| ATTR_EXAMPLE_SLOT| ATTR_MATCH_SLOT| ATTR_STRING| ATTR_ANGLE| ATTR_PORT| ATTR_TYPE_SLOT| )\z/x ] }; pod_coverage_ok( "Graph::Easy::Layout", $trustme ); # Define the global CONSTANTS for internal usage $trustme = { trustme => [ qr/^( NO_MULTIPLES )\z/x ] }; pod_coverage_ok( "Graph::Easy::Parser", $trustme ); pod_coverage_ok( "Graph::Easy::Parser::Graphviz", $trustme ); pod_coverage_ok( "Graph::Easy::Parser::VCG", $trustme ); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/bin/��������������������������������������������������������������������������������0000755�0000764�0000764�00000000000�12150110221�014030� 5����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/bin/graph-easy����������������������������������������������������������������������0000755�0000764�0000764�00000045101�11675050456�016047� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use strict; use Graph::Easy 0.63; use Graph::Easy::Parser; my $help_requested = 0; # echo "[A]" | graph-easy # should work # graph-easy # need help $help_requested = 1 if @ARGV == 0 && -t STDIN; # list of supported output formats for external renderers like dot: my @external = qw/png bmp gif jpg pdf ps ps2 tif tga pcl hpgl/; my $external = join('|',@external); my $qr_ext = qr/^($external)\z/; my $OUT = \*STDERR; my $opt = get_options(); # error? $help_requested = 1 if !ref($opt); # no error and --help was specified $help_requested = 2 if ref($opt) && $opt->{help} ne ''; my $copyright = "Graph::Easy v$Graph::Easy::VERSION (c) by Tels 2004-2008. " ."Released under the GPL 2.0 or later.\n\n"; if (ref($opt) && $opt->{version} != 0) { print $copyright; print "Running under Perl v$]"; eval { require Graph::Easy::As_svg; }; if (defined $Graph::Easy::As_svg::VERSION) { print " and using Graph::Easy::As_svg v$Graph::Easy::As_svg::VERSION"; } print ".\n\n"; exit 2; } if ($help_requested > 0) { print STDERR $copyright; require Pod::Usage; if ($help_requested > 1 && $Pod::Usage::VERSION < 1.35) { # The way old Pod::Usage executes "perldoc" might fail: system('perldoc', $0); exit 2; } Pod::Usage::pod2usage( { -exitval => 2, -verbose => $help_requested } ); } my $verbose = $opt->{verbose}; print $OUT $copyright if $verbose; ############################################################################# # Create the parser object my $parser_class = 'Graph::Easy::Parser'; if ($opt->{from} eq 'graphviz') { require Graph::Easy::Parser::Graphviz; $parser_class = 'Graph::Easy::Parser::Graphviz'; } elsif ($opt->{from} =~ /^(vcg|gdl)\z/) { require Graph::Easy::Parser::VCG; $parser_class = 'Graph::Easy::Parser::VCG'; } print $OUT "Creating $parser_class object.\n" if $verbose; my $parser = $parser_class->new( debug => $opt->{debug} ); ############################################################################# # parse the input file print $OUT "Parsing input in $opt->{from} from $opt->{inputname}.\n" if $verbose; my $graph = $parser->from_file($opt->{input}); my $error = ''; $error = $parser->error() if !$graph || $parser->error(); $error = $graph->error() if $graph && $graph->error(); die ($error) if $error; ############################################################################# # If wanted, generate the statistics: if ($opt->{stats}) { print STDERR "\nInput is a ", $graph->is_simple() ? 'simple' : 'multi-edged', ", ", $graph->is_undirected() ? 'undirected' : 'directed', " graph with:\n"; my $nodes = $graph->nodes(); my $edges = $graph->edges(); my $groups = $graph->groups(); print STDERR " $nodes node" . ($nodes != 1 ? 's' : '') . ", $edges edge" . ($edges != 1 ? 's' : '') . " and $groups group" . ($groups != 1 ? 's' : '') . "\n\n"; for my $g ($graph->groups()) { my $nodes = $g->nodes(); my $edges = $g->edges(); my $groups = $g->groups(); print STDERR " Group '$g->{name}':\n"; print STDERR " $nodes node" . ($nodes != 1 ? 's' : '') . ", $edges edge" . ($edges != 1 ? 's' : '') . " and $groups group" . ($groups != 1 ? 's' : '') . "\n\n"; } } ############################################################################# # Generate the wanted output format and write it to the output: if (! $opt->{parse}) { my $method = 'as_' . $opt->{as} . '_file'; if ($verbose) { if ($opt->{outputname} =~ /\.$external\z/) { print $OUT "Piping output to '$opt->{renderer} -T$opt->{ext} -o \"$opt->{outputname}\"'.\n"; } else { print $OUT "Writing output as $opt->{as} to $opt->{outputname}.\n"; } } $graph->timeout(abs($opt->{timeout} || 240)); my $FILE = $opt->{output}; print $FILE $graph->$method(); print $OUT "Everything done. Have fun!\n\n" if $verbose; } ############################################################################# # Everything done ############################################################################# ############################################################################# sub get_options { # set the defaults my $opt = { input => undef, output => undef, as => '', from => 'txt', help => '', as_ascii => '', as_boxart => '', as_html => '', as_svg => '', as_graphviz => '', as_txt => '', as_vcg => '', as_gdl => '', as_graphml => '', debug => 0, from_txt => '', from_vcg => '', from_gdl => '', from_graphviz => '', verbose => 0, version => 0, parse => 0, stats => 0, timeout => 240, renderer => 'dot', }; # insert the ones from @external for my $e (@external) { $opt->{$e} = ''; } # map the output format to the method to generate the output: my $formats = { html => 'html', txt => 'ascii', svg => 'svg', dot => 'graphviz', vcg => 'vcg', gdl => 'gdl', graphml => 'graphml', }; # insert the ones from @external for my $e (@external) { $formats->{$e} = 'graphviz'; } # do we have some options? if (@ARGV > 0) { require Getopt::Long; my @o = ( "input=s" => \$opt->{input}, "output=s" => \$opt->{output}, "as=s" => \$opt->{as}, "from=s" => \$opt->{from}, "help|?" => \$opt->{help}, "version" => \$opt->{version}, "verbose" => \$opt->{verbose}, "debug=i" => \$opt->{debug}, "parse" => \$opt->{parse}, "as_ascii|ascii" => \$opt->{as_ascii}, "as_html|html" => \$opt->{as_html}, "as_svg|svg" => \$opt->{as_svg}, "as_txt|txt" => \$opt->{as_txt}, "as_vcg|vcg" => \$opt->{as_vcg}, "as_gdl|gdl" => \$opt->{as_gdl}, "as_graphml|graphml" => \$opt->{as_graphml}, "as_graphviz|graphviz|as_dot|dot" => \$opt->{as_graphviz}, "as_boxart|boxart" => \$opt->{as_boxart}, "timeout=i" => \$opt->{timeout}, "renderer=s" => \$opt->{renderer}, "stats" => \$opt->{stats}, "from_txt" => \$opt->{from_txt}, "from_vcg" => \$opt->{from_vcg}, "from_gdl" => \$opt->{from_gdl}, "from_graphviz" => \$opt->{from_graphviz}, ); # insert the ones from @external for my $e (@external) { push @o, "as_$e|$e" => \$opt->{"as_$e"}; } return unless Getopt::Long::GetOptions (@o); } # allow "as=dot" for easier usage: $opt->{as} = 'graphviz' if $opt->{as} eq 'dot'; # make the renderer argument sane to avoid --renderer=';rm -fR *': $opt->{renderer} =~ s/[^a-zA-Z0-9_\\\/\:\.-]//g; # if there are arguments left, they are input and possible output $opt->{input} = shift @ARGV if @ARGV; $opt->{output} = shift @ARGV if @ARGV; if (!defined $opt->{input}) { $opt->{input} = \*STDIN; $opt->{inputname} = 'STDIN'; } else { $opt->{inputname} = $opt->{input}; } # This code gets confused if the user specified multiple options. Not much # can be done about that except whack the user with something heavy: for my $format (qw/ascii boxart html svg txt graphviz vcg gdl graphml/, @external ) { warn ("Warning: Output format '$format' overrides specified '$opt->{as}'") if $opt->{"as_$format"} && $opt->{as}; $opt->{as} = $format if $opt->{"as_$format"}; delete $opt->{"as_$format"}; } if ($opt->{as} =~ $qr_ext) { $opt->{output} = $opt->{input} unless defined $opt->{output}; # set some default output name, so the replace works correctly $opt->{output} = 'graph.txt' if ref($opt->{input}); # two-step process to fix bug #37534 - overwrites input with no extension # example.txt => example $opt->{output} =~ s/\.(txt|dot|vcg|gdl|graphml|$external)\z//; # example => example.png $opt->{output} .= ".$opt->{as}"; } if (!defined $opt->{output}) { $opt->{outputname} = 'STDOUT'; $opt->{output} = \*STDOUT; # default to ASCII if nothing is known $opt->{as} = 'ascii' if $opt->{as} eq ''; } else { my $file = $opt->{output}; $opt->{outputname} = $opt->{output}; if ($opt->{as} eq '') { $opt->{as} = 'ascii'; # default $opt->{as} = $formats->{$1} if $file =~ /\.(html|svg|txt|dot|vcg|gdl|graphml|$external)\z/; } $opt->{output} = undef; if ($opt->{as} !~ $qr_ext) { # do not clobber the output file if we cannot read the input return unless ref $opt->{input} || -R $opt->{input}; open $opt->{output}, ">", $file or die ("Cannot write to $file: $!"); } else { # open a pipe to dot/neato etc. my $file_save = $file; $file_save =~ s/["'\|;]//g; # remove potentially unsafe characters open $opt->{output}, "|$opt->{renderer} -T$opt->{as} -o \"$file_save\"" or die ("Cannot open pipe to dot: $!"); binmode $opt->{output}, ':utf8'; } } if ($opt->{as} !~ $qr_ext) { binmode ($opt->{output}, ':utf8') or die ("Cannot do binmode(output,':utf8')"); } else { $opt->{ext} = $opt->{as}; $opt->{as} = 'graphviz'; } # convert "from_vcg" to "from=vcg" for my $format (qw/txt graphviz dot vcg gdl/) { $opt->{from} = $format if $opt->{"from_$format"}; delete $opt->{"from_$format"}; } $opt->{from} = 'graphviz' if $opt->{from} eq 'dot'; die ("Unknown input format '$opt->{from}'") unless $opt->{from} =~ /^(vcg|gdl|graphviz|txt)\z/; $opt; } __END__ =pod =head1 NAME graph-easy - render/convert graphs in/from various formats =head1 SYNOPSIS Convert between graph formats and layout/render graphs: graph-easy [options] [inputfile [outputfile]] echo "[ Bonn ] - car -> [ Berlin ]" | graph-easy graph-easy --input=graph.dot --as_ascii graph-easy --html --output=mygraph.html graph.txt graph-easy graph.txt graph.svg graph-easy graph.txt --as_dot | dot -Tpng -o graph.png graph-easy graph.txt --png graph-easy graph.vcg --dot graph-easy graph.dot --gdl graph-easy graph.dot --graphml =head1 ARGUMENTS Here are the most important options, more are listed in the full documentation: =over 10 =item --help Print the full documentation, not just this short overview. =item --input Specify the input file name. Example: graph-easy --input=input.txt The format will be auto-detected, override it with L<--from>. =item --output Specify the output file name. Example: graph-easy --output=output.txt input.txt =item --as Specify the output format. Example: graph-easy --as=ascii input.txt Valid formats are: ascii ASCII art rendering boxart Unicode Boxart rendering html HTML svg Scalable Vector Graphics graphviz the DOT language dot alias for "graphviz" txt Graph::Easy text vcg VCG (Visualizing Compiler Graphs - a subset of GDL) text gdl GDL (Graph Description Language) text graphml GraphML In addition, the following formats are understood and piped through the program specified with the --renderer option (default: dot): bmp Windows bitmap gif GIF hpgl HP-GL/2 vector graphic jpg JPEG pcl PCL printer language pdf PDF png PNG ps Postscript ps2 Postscript with PDF notations (see graphviz documentation) tga Targa bitmap tif TIFF bitmap The default format will be determined by the output filename extension, and is C<ascii>, if the output filename was not set. You can also use B<ONE> argument of the form C<--as_ascii> or C<--ascii>. =item --from Specify the input format. Valid formats are: graphviz the DOT language txt Graph::Easy text vcg VCG text gdl GDL (Graph Description Language) text If not specified, the input format is auto-detected. You can also use B<ONE> argument of the form C<--from_dot>, etc. =item --renderer The external program (default: "dot") used to render the output formats like C<png>, C<jpg> etc. Some choices are "neato", "twopi", "fdp" or "circo". =item --parse Input will only be parsed, without any output generation. Useful in combination with C<--debug=1> or C<--stats>. Example: graph-easy input.txt --parse --debug=1 =item --stats Write various statistics about the input graph to STDERR. Best used in combination with C<--parse>: graph-easy input.txt --parse --stats =item --timeout Set the timeout B<in seconds> for the Graph::Easy layouter that generates ASCII, HTML, SVG or boxart output. If the layout does not finish in this time, it will be aborted. Example: graph-easy input.txt --timeout=500 Conversion to DOT, VCG/GDL, GraphML or plain text ignores the timeout. The default is 240 seconds (4 minutes). =item --verbose Write info regarding the conversion process to STDERR. =back =head1 DESCRIPTION C<graph-easy> reads a description of a graph (a connected network of nodes and edges, not a pie chart :-) and then converts this to the desired output format. By default, the input will be read from STDIN, and the output will go to STDOUT. The input is expected to be encoded in UTF-8, the output will also be UTF-8. It understands the following formats as input: Graph::Easy http://bloodgate.com/perl/graph/manual/ DOT http://www.graphviz.org/ VCG http://rw4.cs.uni-sb.de/~sander/html/gsvcg1.html GDL http://www.aisee.com/ The formats are automatically detected, regardless of the input file name, but you can also explicitly declare your input to be in one specific format. The output can be a dump of the graph in one of the following formats: Graph::Easy http://bloodgate.com/perl/graph/manual/ DOT http://www.graphviz.org/ VCG http://rw4.cs.uni-sb.de/~sander/html/gsvcg1.html GDL http://www.aisee.com/ GraphML http://graphml.graphdrawing.org/ In addition, C<Graph::Easy> can also create layouts of graphs in one of the following output formats: HTML SVG ASCII BOXART Note that for SVG output, you need to install the module L<Graph::Easy::As_svg> first. As a shortcut, you can also specify the output format as 'png', this will cause C<graph-easy> to pipe the input in graphviz format to the C<dot> program to create a PNG file in one step. The following two examples are equivalent: graph-easy graph.txt --dot | dot -Tpng -o graph.png graph-easy graph.txt --png X<svg> X<html> X<ascii> X<boxart> X<png> X<dot> X<graphviz> X<vcg> X<gdl> X<graph description language> X<unicode> =head1 OTHER ARGUMENTS C<graph-easy> supports a few more arguments in addition to the ones from above: =over 10 =item --version Write version info and exit. =item --debug=N Set the debug level (1..3). Warning, this will generate huge amounts of hard to understand output on STDERR. Example: graph-easy input.txt --output=test.html --debug=1 =item --png, --dot, --vcg, --gdl, --txt, --ascii, --boxart, --html, --svg Given exactly one of these options, produces the desired output format. =back =head1 EXAMPLES =head2 ASCII output echo "[ Bonn ] -- car --> [ Berlin ], [ Ulm ]" | graph-easy +--------+ car +-----+ | Bonn | -----> | Ulm | +--------+ +-----+ | | car v +--------+ | Berlin | +--------+ =head2 Graphviz example output echo "[ Bonn ] -- car --> [ Berlin ], [ Ulm ]" | graph-easy --dot digraph GRAPH_0 { edge [ arrowhead=open ]; graph [ rankdir=LR ]; node [ fontsize=11, fillcolor=white, style=filled, shape=box ]; Bonn -> Ulm [ label=car ] Bonn -> Berlin [ label=car ] } =head2 VCG example output echo "[ Bonn ] -- car --> [ Berlin ], [ Ulm ]" | graph-easy --vcg graph: { title: "Untitled graph" node: { title: "Berlin" } node: { title: "Bonn" } node: { title: "Ulm" } edge: { label: "car" sourcename: "Bonn" targetname: "Ulm" } edge: { label: "car" sourcename: "Bonn" targetname: "Berlin" } } =head2 GDL example output GDL (Graph Description Language) is a superset of VCG, and thus the output will look almost the same as VCG: echo "[ Bonn ] -- car --> [ Berlin ], [ Ulm ]" | graph-easy --gdl graph: { title: "Untitled graph" node: { title: "Berlin" } node: { title: "Bonn" } node: { title: "Ulm" } edge: { label: "car" source: "Bonn" target: "Ulm" } edge: { label: "car" source: "Bonn" target: "Berlin" } } =head2 GraphML example output GraphML is XML: echo "[ Bonn ] -- car --> [ Berlin ], [ Ulm ]" | graph-easy --graphml <?xml version="1.0" encoding="UTF-8"?> <graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd"> <!-- Created by Graph::Easy v0.58 at Mon Aug 20 00:01:25 2007 --> <key id="d0" for="edge" attr.name="label" attr.type="string"/> <graph id="G" edgedefault="directed"> <node id="Berlin"> </node> <node id="Bonn"> </node> <node id="Ulm"> </node> <edge source="Bonn" target="Berlin"> <data key="d0">car</data> </edge> <edge source="Bonn" target="Ulm"> <data key="d0">car</data> </edge> </graph> <graphml> =head1 CAVEATS Please note that it is impossible to convert 100% from one format to another format since every graph language out there has features that are unique to only this language. In addition, the conversion process always converts the input first into an L<Graph::Easy> graph, and then to the desired output format. This means that only features and attributes that are actually valid in Graph::Easy are supported yet. Work in making Graph::Easy an universal format supporting as much as possible is still in progress. Attributes that are not yet supported natively by Graph::Easy are converted to custom attributes with a prefixed C<x-format->, f.i. C<x-dot->. Upon output to the same format, these are converted back, but conversion to a different format will lose these attributes. For a list of what problems still remain, please see the TODO file in the C<Graph::Easy> distribution on CPAN: L<http://search.cpan.org/~tels/Graph-Easy/> If you notice anything wrong, or miss attributes, please file a bug report on L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Graph-Easy> so we can fix it and include the missing things into Graph::Easy! X<bugreport> =head1 LICENSE This library is free software; you can redistribute it and/or modify it under the terms of the GPL. See the LICENSE file of Graph::Easy for a copy of the GPL. This product includes color specifications and designs developed by Cynthia Brewer (L<http://colorbrewer.org/>). See the LICENSE file for the full license text that applies to these color schemes. X<gpl> X<apache-style> X<cynthia> X<brewer> X<colorscheme> X<license> =head1 AUTHOR Copyright (C) 2004 - 2008 by Tels L<http://bloodgate.com> =head1 SEE ALSO More information can be found in the online manual of Graph::Easy: L<http://bloodgate.com/perl/graph/manual/> See also: L<Graph::Easy>, L<Graph::Easy::Manual> =cut ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Graph-Easy-0.73/CHANGES�����������������������������������������������������������������������������0000644�0000764�0000764�00000306535�12150110122�014267� 0����������������������������������������������������������������������������������������������������ustar �shlomif�������������������������shlomif����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Revision history for Graph::Easy (formerly known as Graph::Simple): 2013-05-25 v0.73 Shlomi Fish 2938 tests * Got the tests to pass on perl-5.18.0 (and hopefully above). - Done by making sure hash traversals (each/keys/values) are only done by lexicographical order of the keys. 2012-10-20 v0.72 Shlomi Fish 2938 tests * Update Module-Install from version 1.04 (which is broken on perl-5.16.x). - http://weblog.bulknews.net/post/33907905561/do-not-ship-modules-with-module-install-1-04 - Thanks to MIYAGAWA for the report. 2011-12-23 v0.71 Shlomi Fish 2938 tests * Fix the SIGNATURE file. - https://rt.cpan.org/Ticket/Display.html?id=58892 - Thanks to ANDK for reporting it. 2010-11-05 v0.70 Shlomi Fish * Fix "GraphML: end tag of nodes before definition of their attributes" - https://rt.cpan.org/Public/Bug/Display.html?id=46992 - Thanks to a patch by ironcamel ( https://github.com/ironcamel ) * Got rid of a warning in recent Perls for doing tr/\t/ /d due to /d being usless in that case. 2010-07-01 v0.69 Shlomi Fish 2938 tests * Add support for GraphViz subgraphs. - Thanks to a patch by Yves Agostini ( http://www.crium.univ-metz.fr/ ) 2010-06-28 v0.68 Shlomi Fish 2933 tests * Add .*\.swp to the MANIFEST.SKIP in order to skip vim temporary files. * Fixed the signature file. - https://rt.cpan.org/Ticket/Display.html?id=58892 - Thanks to Andreas Koenig (ANDK). 2010-05-28 v0.67 Shlomi Fish 2933 tests * Fix the as_vcg() vs. as_vcg_file() test that sometimes failed due to timestamp offsets in both files. - See: http://www.cpantesters.org/cpan/report/07338331-b19f-3f77-b713-d32bba55d77f 2010-05-23 v0.66 Shlomi Fish 2933 tests * Removed the leftover .orig files by request of a Debian maintainer (by private E-mail). 2010-05-23 v0.65 Shlomi Fish 2933 tests * Partial fix to bug #27663 + Fix the name of the function in the code excerpt of as_graphviz() + Mark as_graphviz() and as_graphviz() as for internal use. + Make sure to indicate that one needs to load Graph::Easy::As_graphviz beforehand. * Fix for https://rt.cpan.org/Ticket/Display.html?id=48657 + Specify what the default timeout is in the POD of Graph::Easy. + Thanks to MTHURN for reporting it. * Fix for https://rt.cpan.org/Ticket/Display.html?id=51820 + Changed C<..> to C<< ... >>> for correct POD rendering. * Applied patch from https://rt.cpan.org/Ticket/Display.html?id=57777 + Spelling errors. 2008-08-10 v0.64 Tels 2933 tests * fix bug #37534 - overwrites input without warning (Thanx C. Khan!) * fix bug #37566 - Graphviz (v2.16) output broken (Thanx C. Khan!) * fix bug #37842 - typo in README (Thanx Craig Sparks!) * fix add_nodes(), would return instead of skipping existing nodes * as_graphml: + fix #36533: GraphML end tag (Thanx Thiago M. Venancio!) + fix indent and endtag of groups (Thanx Thiago M. Venancio!) + properly escape special characters in group/graph names + add format => 'yED' to output as yED compatible (Thanx to Thiago M. Venancio for requesting and testing this!) * Groups: + nodes added to the group are also put into the same graph as the group + add_node('A') etc. (add with non-object) works now + add_node('A') returns node, not group + add_edge('A','B) and add_edge_once('A','B') work now * Graph::Easy: + add compatibility methods to mimick the interface of Graph: add_edges, add_cycle, add_path, add_vertices, add_vertex, set_vertex_attribute, and has_edge (Thanx to Matt Spear for requesting this!) 2008-05-22 v0.63 Tels 2895 tests * require Perl v5.8.2 (it fails badly on 5.8.1; due to hash order?) * add examples/wikicrawl.pl - thanx integral from forum.xkcd.com! * Parser/Graphviz: + HTML-like attributes can also appear in lower-case + generate the base name for HTML-like nodes from the actual name, this allows things like "11 [ label=<<..>> ]\n 11:e -> 1" to work + try to handle <font> tags inside HTML-like labels * graph-easy: + change default timeout to 240 seconds + --version also prints Perl and Graph::Easy::As_svg versions + handle UTF-8 output when piping to graphviz renders * Base: + fix main_class() (caused lost group attributes in graphviz output) * Group: + class for inner cells is "group gi", not "groupgi" + adding a node to a group via add_node() or add_member() also sets the "group" attribute on that node + add_nodes() correctly registers nodes with the group * Node: + add get_raw_attribute() as alias for raw_attribute() * Attributes: also accept "auto-title", "auto-link" and "auto-label" * _class_styles: fix graph class attribute output for SVG * Layout: + A*: when we can't find a path, warn and continue gracefully + A*: raise max. number of tries from 50_000 to 2_000_000 + make Heap do binary search upon insert, instead of sort upon extract, this makes complex layouts much faster because the A* algorithm does no longer spent 90% of its time to manage the heap + streamline _astar_distance() to be 50% faster * as_graphviz(): + quote "name":"portname" for HTML-like nodes + properly output arrows on bidirectional edges that join/split (aka no more arrow heads on the invisible helper node) + put invisible helper nodes for join/split edges into the subcluster if both from/to are in the same cluster + fix output of spacer TDs in HTML-like labels for relative nodes 2008-03-30 v0.62 Tels 2813 tests * graph-easy: + better error handling, now dies on errors in the input text * added the following options to graph-easy: + --jpg, --ps, --bmp etc.: support a lot more formats for dot + --renderer: set the program to render PNG, JPG etc. output (to change "dot" to "neato" etc.) * Parser: + fix autosplitting nodes with "empty" parts like "| |" (was also broken in DOT parsing as the same code was used) + don't remove spaces at front of lines to support "\n || B" * Parser/Graphviz: + detect missing "}" at end of file + bail out if "digraph {" is found twice in the input + better parsing of attributes in scopes (inside "{" and "}") + support ".7" (no leading zero) in color notation + fix parsing of shape=record labels like "{A||B}" * Parser/VCG (also for GDL): + "layout_segments" is actually "linear_segments" + support "status" attribute for subgraphs + support "textmode" attribute for nodes + support "orientation" attribute on graphs + support "invisible" on nodes + handle "colorentry 7: ..." (more than one space) + port_sharing, inport_sharing and outport_sharing => autojoin/autosplit + fix attribute remapping to go through custom filter + strip out \\f- (hor line), \\f01 (colors) for now until we can support them for real + improve detection of multi-line labels + better support for node shapes + handle both "0x0c" (aka ^L) and \\f in input * as_vcg()/as_gdl(): + remap flow to orientation for graph flows + remap align to textmode + output class attributes for edges/nodes + support "invisible" nodes * as_graphviz(): + handle "align" attribute on nodes properly * unify error handling for unknown attributes * remove Build.PL support (updated Module::Build to 0.71) 2008-03-16 v0.61 Tels 2778 tests * graph-easy: + reject unknown input format specifications + support --from_vcg and --from_gdl, too * various POD fixes * relax rules for custom attributes by also allowing digits anywhere * fix bug #32206: Fix for as_vcg.t test failures when disk io is very slow * fix bug #31958: Missing methods in Edge class + add as_ascii() (and simplify it) + add as_txt() stub (require Graph::Easy::As_txt and call _as_txt) + add nodes() + remove the to_nodes() and from_nodes() POD entries * disallow border attributes on edges (it was ignored in all output formats) * make it possible to call Graph::Easy->new('[A]->[B]'); * add: + $graph->copy() - clone the entire graph + get_custom_attributes() + merge_nodes() takes a third parameter: the string to join the label of the second node to the label of the first node (thanx Brad Bowman!) * Parser/Graphviz: + handle "border: 0" + handle graph attributes: maxiter, minquit and pagedir + handle edge attribute: weight + better parsing of attributes on HTML-like labels (no longer generates bogus attribute names like "balign" and warns about unknown ones) + convert <BR/> in HTML-like labels to line breaks + re-connect edges between nodes with HTML-like labels to the generated autosplit parts (formerly, these edges were simply lost) * Parser/VGC: + attributes like "node.color:" are valid for all following nodes only * as_graphviz(): + suppress style and peripheries for borderwidth == 0 2007-11-19 v0.60 Tels 2700 tests * Base: + prepend 'Warning: ' to warnings * Graph::Easy: + add add_anon_node() + _class_styles(): allow extra class names in overlay (for SVG) + fix typo in SYNOPSIS (thanx skim!) * Graph::Easy::Node: + set attributes for origin/offset properly, too, so that get_attribute() works for these (Thanx Amanda B Hickman!) + allow numbers on custom attributes so "infoname1" in VCG works correctly * Parser: + warn on nodes with offset, but no origin (Thanx Amanda B Hickman!) * Parser/VCG: + allow "layoutalgorithm" (Thanx Sid Touati for the report!) + handle 'classname 1: "foo"' correctly (Thanx Sid Touati again!) * Layout: + remove one call to ->attribute() per Node by re-using the shape + fix A* boundaries cache: create it if it doesn't exist yet, otherwise edges went missing (see t/in/3_cache_bug.txt for an example) * as_vcg: + output classname attributes correctly (Thanx Sid Touati!) * as_graphviz: + remap "\c" to "\n" since graphviz doesn't know "\c" * Tests: + fix test failures in t/graphml.t when second changes while comparing as_graphml() and as_graphml_file() output + fix test failures with Pod-Coverage v0.19 - a few alias routines in Graph::Easy::Node need to have POD replicated from Graph::Easy + require Pod-Coverage v0.19 2007-09-10 v0.59 Tels 2686 tests * fix test failures in t/graphml.t (oops) 2007-09-09 v0.58 Tels 2686 tests * add Graph/Easy/As_graphml.pm - output graph as GraphML text * bin/graph-easy: + make "graph-easy --png input.txt output.png" work + fix POD structure for example outputs + expand section about conversion limits and problems * Parser: speed up all parsers by: + making the regexp for empty/commented lines simpler and by combining two regexps to replace/remove tabs and 0x0d into one tr// (~ 5%) + cache regexps needed for _parse_attributes() as these calls can be expensive and occur frequently per graph (~ 10% for Graphviz) * Parser/VCG: + about 3x as fast by swapping out the matchstack while we are in matching multi-line labels. The speedup depends on how many lines are in multi-line labels compared to the rest of the graph. * Graph: + fix rename_node() to work with plain node names * Graph::Easy and Graph::Easy::Group: + add: root_node(), type() * Base: + add: + catch_messages(), catch_errors(), catch_warnings(), errors(), warnings() * Layout: + add a stub for force-directed layouts (doesn't work yet) + fix bug #29039: hang in path-straighten code (Thanx mperilstein!) (also fixes the hang reported in bug #27759) * as_html: + fix undef warning in Graph/Easy.pm line 1038 2007-08-12 v0.57 Tels 2673 tests * bin/graph-easy: + don't die on non-fatal parser errors (good for unknown dot attributes) + warn if both --as=txt and --as_foo or more than one --as_foo are given + add --stats option: output various statistics about the input graph * Graph: + add rename_group() + rename_node() turns anon nodes into normal nodes (with a name) * Graph/Group: + add a groups_within() method that returns recursively the contained groups up to the specified level (-1 - infinite, 0 => level 0 etc.) * Group: + fix add_group() when called with a scalar as group name * Parser: + parse nested groups: "( Outer ( Inner [ A ] ) [ B ] )" * Parser/VCG: + support for multi-line labels (Thanx Ohad Ben-Cohen for the testcase) + work around faulty inputs with unescaped double quotes in labels (gconv, I am looking at you!) (Thanx Ohad Ben-Cohen for the testcase) + speed up parsing of large graphs by ca. 50% by simplifying regexps + add support for \fi065 (ISO-8859-1 characters) + support subgraphs (aka nested graphs) * Parser/Graphviz: + speed up parsing of large graphs by ca. 45% by direct attribute lookup * as_vcg: + generate classname attribute for edge classes if nec. * Attributes: !!!!!!!!!!!!!!!!!!!!!!!!!!!!! ATTENTION !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! The attribute "pointstyle" has been redefined as to make it consistent with "arrowshape" and "arrowstyle". The attribute "pointstyle" is now one of "closed", or "filled" and the shape can be set with "pointshape" as "diamond", "cross" etc. !!!!!!!!!!!!!!!!!!!!!!!!!!!!! ATTENTION !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * Misc: + fix SYNOPSIS (Thanx Donuvi Tanoga!) 2007-07-22 v0.56 Tels 2650 tests * bin/graph-easy: + add --version * Parser/VCG: + support more attributes, remap unknown/unsupported ones to "x-vcg-foo" * Parser/Graphviz: + allow style="setlinewidth(0.1)" (fractions) in attributes + translate unknown or unsupported attributes into "x-dot-foo" style * the following methods in Groups/Edges are only usefull during the layout phase and are now internal and thus were renamed with an underscore in front of them: + add_cell, del_cell, clear_cells, cells * POD: + fix a few typos and duplicate some documentation into the various POD files, as well as add some more doc * Graph::Easy: + add add_nodes(), is_simple(), is_directed(), is_undirected(), edges_within() * Graph::Easy::Group: + fix add_nodes(), edges() + add edges_within(), has_as_predecessor(), has_as_successor() * Graph::Easy::Node: + add has_as_predecessor(), has_as_successor() * Misc: + save a bit memory by not setting default values for edges and nodes * Attributes: + support "type" for graphs (directed or undirected) + support custom attributes named like "x-foo", "x-bar-baz", "x-f-o-o" * as_graphviz: + use "--" for edges for undirected graphs + translate "x-dot-foo" attributes back to "foo", making it round-trip * as_vcg: + translate "x-vcg-foo" attributes back to "foo", making it round-trip + first stab at attribute remapping 2007-05-05 v0.55 Tels 2433 tests * bin/graph-easy: + as_png and as=png + add support for VCG/GDL + do not clobber output file if we cannot read input file + make --help work under older Pod::Usage versions + make "echo '[A]' | graph-easy" work while "graph-easy" outputs help + optimize startup to be about 10% faster if we don't have arguments * add support for VCG/GDL (Parser/VCG) * remove a few stray "use utf8;" in files with no high-bit bytes (Thanx Juerd!) * add rename_node() * Attributes: + an unset attribute in a subclass will cause the attribute to be looked up in the base class, and not just use blindly the default attribute from the base class: node { align: left; } [ L ] { class: foo; } # L is be left-aligned and not center! Due to the way CSS inheritance works and HTML+CSS is emitted, this bug showed only up in as_ascii() + properly support \N, \E, \G, \T, and \H in labels, links and titles + support \L links and titles * as_txt(): + quote characters like '[' for autosplit nodes, too + remove spaces inside autosplit nodes to normalize their text output: "[ Some || test | B ]" becomes "[ Some || test | B ]" + attributes for autosplit nodes were not properly output, especially if they were something like "color|" or "red|blue" * as_html(): + fix undirected edges (unitialized warning and edges sticking too close to the start node) + adjust alignment of the edge arrows by using more relative positioning + make the spacing of edges smaller, so they don't inflate node sizes * as_graphviz(): + fix fillcolor on nodes with shape plaintext + adjust the linewidth style to not be that overly huge + handle fill for nodes with subclasses correctly * Parser: + support "\]" in node names like so: "[ char\[\] ]" + attributes ending in '|' like "color|;" on autosplit nodes were ignored + autosplit nodes with the attributes following on the next line had any set basename attribute ignored (see t/in/4_bug_basename.txt) * Parser/Graphviz: + a lone "node:port" did not result in "node" to be created + set the graph name as the default graph title + handle a semicolon after "}" for subgraphs and graphs + handle the variant "graph { A--B }" + support the "output" attribute (for the Mediawiki integration) + adjust for the changed linewidth styles + remove the replacement of \E, \N, \T, \H and \G now that Graph::Easy knows these, too. Allows proper roundtrips of these escapes. * as_html()/as_svg: + setting a colorscheme on the graph and some class color attribute to the color "1" didn't result in the real color, but in "color: 1;" in the generated CSS code * Layouter: + fix endless loop in the bend-optimizer due to a swapped X and Y 2007-03-18 v0.54 Tels 2349 tests * add some examples to bin/graph-easy * fix borders on rounded/circle/ellipse nodes outside groups * fix fill on edge class * Parser/Graphviz: + handle multi-line comments inside attributes * handle an unquoted/unescaped "]" inside (HTML-like) attributes * handle an multi-line comment followed by space as the last part * Attributes: add labelpos (top/bottom) for groups * as_graphviz(): + reinsert the labels on edges 2007-02-19 v0.53 Tels 2342 tests * add --timeout=N to graph-easy and set the default to 120 seconds * handle "\|" inside nodes properly: "[ A \| B | C ]" works now * add MANIFEST.SKIP to enable "make manifest" * remove todos/ * add attribute "comment" for graphs, edges, nodes and groups * add support for hsv() and hsl() colors * add support for transparency (alpha channel) in colors * colors are stored as input (but normalized), fixes the problem that hsv() colors in graphviz were converted to RGB and thus lost precision * add attribute "arrowshape" for edges, allowing triangle, dot, box, line, inv, cross, x and diamond shaped arrows * Base: setting a new subclass did not invalidate the cache * as_html: + use again "position: relative" to shift arrows to the proper place (needs to be done for for most edge parts, tho) + implement a few more missing edge pieces * parser/Graphviz: + groups have slightly different defaults in DOT, so set these: (Thanx Michael Goldshteyn!) + transparent background + center aligned label * as_graphviz: + don't generate needless label attributes + compound=true is the default, so we can omit it + do not needless quote "#" characters + handle fractional fontsizes better, and do not output wrongly "11px" + output correct "labeljust" for groups (Thanx Michael Goldshteyn!) + HSV colors are now preserved (formerly they were converted to RGB) + the alpha channel on colors is no preserved if possible * as_html/as_box_art: + use LOGICAL AND and LOGICAL OR for open up/down arrows + replace open arrows with nicer looking Unicode chars + nodes with shape circle, rounded or ellipse are now rendered correctly when having a different background color (or when inside a group) + don't generate labels for nodes with empty labels (like anon nodes) + default fill is white, so set it in CSS for pages with some backdrop * Graph::Easy objects are now about 4K smaller (min. size was 6.5K on 64bit) 2007-01-28 v0.52 Tels 2305 tests * format INSTALL in POD and add notes about Windows * Group::Cell: cache attributes to avoid overly many attribute() calls * attribute(): do not call class() unless neccessary * Layouter: + cache the boundaries of the cells array for A*, avoid excessive recalculaion of the used area (make big layouts about 30% faster) + fix _optimize_layout() for reversed edges, that finally fixes the garbled HTML output * as_html(): + "vertical-align: center" must be "vertical-align: middle" - argh! + better alignment of arrows on some edge/end pieces + insert missing style="..." in all edge pieces (fixes missing group backgrounds on some edge cells) * remove dead code in Graph::Easy::Node::Empty 2007-01-05 v0.51 Tels 2305 tests * bin/graph-easy: add --parse option * Parser: + fix bug #24133: empty groups were wrongly put on the group stack, thus aquiring some of the following nodes (Thanx to Paul Biggar!) + allow class selector ".foo { ... }" - set attributes on all objects (edges/nodes/groups) of subclass "foo" + allow lists of class selectors like ".foo, node.bar, group { ... }" * as_html: + fix joint W_N_S (Thanx Florian Lindner!) * as_graphviz: + fix wrong output of group fill colors and style (Thanx Paul Biggar!) + workaround the inability of DOT to have attributes for all subclusters * attributes: + setting the attribute "class" on a primary class puts all objects without a specifically set class attributes into that class, so: node { class: red; } node.red { color: green; } colors all nodes without a specific subclass red. 2006-12-17 v0.50 Tels 2273 tests * added bin/graph-easy and make the build process install it into the system This adds the ability to simple run "graph-easy" from anywhere * attributes: + textwrap: the default is inherit, to make setting it on a graph work + allow "textwrap: N;" to wrap text automatically at N characters * Base: + add error_as_html() - return error message as escaped HTML * Group: + add add_nodes() - add multiple nodes to the group * Graph: + add flip_edges($A,$B); - transpose all edges going from A to B + add flow() - return the flow of the graph in degrees * Node: + edges() returns all the edges at that node; fix doc for it + rename grow() to _grow() to make clear it is a private method * Edge: + add edge_flow() - return flow of this edge * modifying an edge via the following methods did not invalidate the layout: + flip(), bidirectional(), undirected(), start_at(), end_at() * add a dummy shape() method for groups and group cells * as_graphviz(): + don't output autolabel, rows, columns or group attributes * as_html(): + output linebreaks as simple "<br>" + support for textwrap: 10; * as_ascii(): + write our own text wrap routine to replace the buggy Text::Wrap + rewrote the border-collapse algorithm, as well as the logic to determine if we need to draw a border. This makes nodes next to each other (like via autosplit, or with origin/offset) work even when their borders are different (like none/solid, dotted/solid etc). (Thanx Nick Jenkins!) * Layouter: + Rewrite the rank assigning code: + for cycles in the graph, the rank assignment code did sort the nodes alphabetically and put the node with the "lowest" in the first rank. Now it uses the order in which the nodes were defined, thus this: [ B ] -> [ C ] --> [ A ] --> [ B ] starts now the layout with "B", instead of "A", as humans expect it. + use a heap instead of ARRAY, so does less work but is more correct + correctly assign user ranks and automatic ranks to nodes + the layouter keeps track of where it placed the first node of a rank, and then places nodes with the same user-set rank in the same column/row + on nodes with more than one edge leading out, where one of the edges has a flow, stop the chain tracking there. This prevents the non-flow edge to be laid out first if its chain is longer than the flow-edge, and thus prevents the non-flow edge from blocking the flow-edge. + self-loops were not tried in the right order, unless the flow at the node was east (Thanx Stéphane Bortzmeyer!) + A*: don't put edge labels on EDGE_CROSS cells as they aren't rendered there (Thanx Stéphane Bortzmeyer!) + sink-nodes (no outgoing edges) get bigger so that there front side is empty, preventing edges going "around" to the front side of the node + paths generated by A* have a tendency to in-ward bends, these are now removed and replaced by a simple corner, if possible * Parser: + improve robustness for alternate line endings (\x0d\x0a vs \x0a) + a space in front of classes (" name { color: red; }") caused an error (Thanx David R. Throop!) + allow escaped ";" in attributes as in "{ label: \;\;; }" + accidentily allowed "[a]--test -->[b]" (no space before edge label) + properly handle non-string attributes like "[A|B] { shape: node|;}" * Parser/Graphviz: + support "setlinewidth(..)" in style attribute for nodes + handle the following color forms correctly: + hexadecimal RGB colors with spaces (like "#AA BB CC") + HSV() colors ("1.0 1.0 1.0" and "0.0, 0.0, 1.0") + colorschemes ("//red" and "/x11/red") + first stab at parsing HTML-like labels + support for "labeljust" on subgraphs * set binmode(STDERR,'utf8') when in debug mode * Group: include a modified add_group() to make nested groups work properly * add attribute "group" for groups * make $self->attribute('group') work on nodes and groups 2006-10-29 v0.49 Tels 2182 tests * fixed Synopsis * add more modules to t/pod_cov.t and t/pod.t * speedup loading: remove _u8() kludge, use utf8 in As_ascii.pm * Node: added get_attribute() as alias for attribute() * Completely rework the attributes code: + attribute() now always returns the default value, and respects inheritance for values that are "inherit". Instead of a hard-coded attribute list, we now simple use 'inherit' for default values. + 'inherit' is a valid colorscheme and edges, (anon) groups, and (anon) nodes have "inherit" as default colorscheme + add "autosplit" and "autojoin" for edges + accept all attributes with a "-" in their name also without the "-" (fontcolor, textwrap etc. are now valid inputs) + make default_attribute() work, even when the object is not in a graph + default_attribute() uses the info in $attributes to avoid double data + add attributes "root" and "rank" for groups + del_attribute() now works for virtual attributes like 'border' or 'size' + add: raw_attribute(), raw_attributes(), get_attributes() * Graph: del_node() didn't deregister a child node from parent * support \; and \' (f.i. to escape single quotes if you want a label "''") * Start working towards a recursive layouter: + separate layout() into layout() (for graph only) and _layout() + call edges_into_groups() before layout + Group: store cells in {_cells} to avoid clash with {cells} from layout + Group: in _layout() set {debug} for groups from parent graph {debug} * Parser: + make magic detection of Graphviz input much more robust + parsing failed silently when there was only one invalid line of input + fix parsing of empty groups and/or anonymous groups + sort attributes before checking them to make tests more predictable * Parser/Graphviz: + a new scope start followed by a non-node (f.i. attributes) crashed the parser. See t/in/dot/5_scope_atr.dot for an example. + recognize (n|ne|e|se|s|sw|w|nw) as compass directions after nodes/ports + recognize numbers as node names (like 0.11, 1.0, 0., 1., etc) + the ID in "graph ID { }" is optional + properly remap headport/tailport for edges + make comment parsing more robust, especially against multiple multiline comments or multiline comments without spaces around them + recognize "setlinewidth(..)" for edge styles * as_txt(): + don't insert superflous newline for empty groups + properly output anonymous and empty groups + output all attribute names w/o the "-", i.e. "fontsize: 80%;" * as_html(): + properly quote "<", ">" and "&" in labels + values of "inherit" work now, even when just set on a class/subclass * as_graphviz(): + output nodes inside groups in sorted order to make tests more predictable + add tests for proper colorscheme support * attribute "autolabel": + allow and prefer/doc to use just "N" for specifying the length + use "…" (ellipse) instead of just three dots (except in as_ascii) + allow up to 99999 characters (was 9999) + change doc to reflect that a set label will be shortened, too 2006-09-13 v0.48 Tels 2020 tests * Parser/Graphviz: + handle attribute "URL" (opposed to "url") + handle attribute "href" for graph/node/subgraph, too + accept "\N" in titles and urls, too + accept "\E" for edges, too (\E is replaced by "Bonn->Berlin" f.i.) + accept "\G", "\T", "\H" in labels/urls/titles, too + parse HTML entities like & in names/labels/titles/urls + fix comment parsing, things like "http://example.com" didn't work * Layout/Repair: + fixup the code that inserts edge pieces + fixup the code that combines long HOR/VER edges in case the edge goes bottom-to-top or right-to-left (the code only expected the other dir) + repair JOINT_E_N_S followed by an edge on the right/bottom side properly * Edge: + fix add_cell() to add a cell _after_ a given cell, not before (this fixes those horrible messed up HTML layouts, esp. involving groups - Thanx to Florian Lindner for testcases and bugging me! :) * as_html: + implemented edge type 1031 (joint south to east/west, starting west) + CSS class names like "node-group" must be "node_group" to fully work (fixes class-attributes on anon nodes etc. not showing up) + fix output of group labels on groups parsed from Graphviz * as_ascii: + render anon nodes, too, since they might have class attributes making them visible (fixed t/in/4_node_edge.txt among others) * as_graphviz: + fix rendering of anon nodes (setlinewidth(0) doesn't work, so use style=filled and a color that equals the node background) + don't output spurious "flow" for nodes + the code to supress edge labels on helper edges was broken 2006-09-03 v0.47 Tels 2013 tests * Edge: add end_at(), the opposite of start_at() * add node shape 'edge' - render node as a horizontal edge piece * add node attribute 'rank' - 'auto', 'same' or positive number * multiple spaces in edge labels were not collapsed together * class names are case-insentive like in CSS, so we lowercase them on input: + as_txt() renders them now unified as lowercase, always + "node.SECOND { label: B; } [ A ] { class: Second; }" works now * as_txt(): edges going to/starting at the first part of an autosplit node are now correctly output: "[A|B] [ AB.0 ] -> [ T ]" now works. * as_html(): arrow-style: filled; did not replace all "v" and "^" arrows * Layouter: + handle _trace_path() called with unplaced src node + adding group cells makes it nec. to adjust size of multicelled nodes + holes torn after splicing-in group cells where not closed when one of the adjacent edge cells was of type EDGE_CROSS * Parser: + make detection of Graphviz code more robust against commented out text * Parser/Graphviz: + a scope end did not properly pop the group_stack, resulting in nodes getting in the wrong group. t/in/dot/6_2_cluster.dot now parses and renders correctly. + "The keywords 'node', 'edge', 'graph', 'digraph', 'subgraph', and 'strict' are case-independent." So we accept that them way. + handle the case of "strict digraph G { a -> b }" properly + edge [ label=foo ] sets these attributes on edges, not nodes + handle "style=dotted" for nodes (as well as "dashed") + a "node [ style=invis ]" followed by "node [ style=filled ]" did not set following nodes to a visible shape + properly remap: "arrowhead", "labeljust", "orientation" + handle label="\N" for nodes + the default arrow style is "filled" + attribute values with a "|" inside them got wrongly split + attribute values with ']' inside quoted strings like label="[P]" fixed + implement parsing records of the form "<p1> A | <p2> B | C"> + "Bonn":"f1" now creates a node "Bonn", not "Bonn:f1", nor does it fail + all boolean attributes are now recognized when appearing without a value + handle string concatenation ("foo" + "" + "bar") + handle line continuation (a '\' as last character on line) 2006-08-13 v0.46 Tels 1943 tests * Output: + add as_debug() and "output: debug" * Layouter: + fix layout with loops where the root node is part of the loop (see t/in/6_split_join_loop.txt for an example) + fix warning about uninitialized in Chain.pm line 423 * Parser: + '\[', '\{', '\>' etc inside an edge label work now: [ A ] -- \[ A \ ] \<\> --> [ B ] + use _unquote() in more places instead of duplicated code + allow rgb(10%,0,0.5) for colors, e.g. fractions and mixes + allow \" in attributes ala 'label: "\"hello\"";' + a comment inside attributes caused the parsing to fail: { color: 3; # this didn't work } * Parser/Graphviz handles the following: + "root" on a graph + "graph G { init -- _run }" (graph vs digraph, \w+ as node) + funky node names: node_0, Köln, "node" etc + "subgraph G { .. }" + scopes as in "{ node [ color=red ] Bonn -> Berlin }" + node list as in "{ a b c } -> u; i -> { j k l }" + digraph/subgraph attributes: "subgraph G { style=filled; }" + node attribute 'label' no longer crashes the parser + attributes without a value ("center truecolor") + attributes w/ or w/o ";" at end, w/ or w/o "quotes" + "subgraph test { } abc -- test" shouldn't add a spurious node "test" + per default, the color scheme is x11 and the graph flows south + "a1 -> a2\na2 -> a3" (no ";" at end of line and no spaces in between) + undirected edges + /* * style * / comments almost everywhere * Parser/Graphviz maps/handles/ignores now the following attributes: + nodes: shape, peripheries, z + graph: ratio, labelloc + edge: f, group, style + all: style, fontsize, color ("0.99 0.8 0.24", #ff00ff00 etc) * as_graphviz(): + output "rank=0" for root node + fix output of headport/tailport for start/end attribute + fix the creation of "invisible" joints to joining/splitting edges: + created helper nodes no longer conflict with existing node names + works for joints as well as splits + suppress arrow heads leading to joint + works in both east and west direction (aka with flipped edges) + start/end: left; (aka relative starting/end side) results in correct tail/headport attribute, too * as_ascii(): + fix rendering of graph labels at bottom + graph labels wider than the graph make output bigger and work now * Attributes: + support for different color schemes + add X11 and ColorBrewer color definitions + add attribute 'colorscheme' for all classes + colors are now stored internally as they were written in the input * License: include apache-style license for Brewer color schemes * Misc: fatal errors produce a stack backtrace only under debug * Node: further fixes flow(): properly cache the return value, reduce C&P 2006-07-23 v0.45 Tels 1811 tests * we dont need inc/Module/AutoInstall.pm (this dist has no dependencies) * require at least Perl 5.8.1 for proper Unicode support * add a Build.PL file * fix bug with shared edges and joints (Thanx Florian Lindner!) * fix reparing of edge joints that are next to a node inside a group * add attribute 'root' for graphs * Layouter: if given root node, start there * fix "no_index" in Makefile.PL to not include spurious "directory"'s * Node: fix flow() * as_ascii: fix corners with start/end pieces (add spacing, to make consistent with all other start/end pieces) * as_boxart: fix rendering of corner pieces (especially for joints) * as_html: render invisible edges truly invisible * adding a node with the name '0' and retrieving it via node('0') works now * Parser/Graphviz: + 'Bonn' is a valid node name, too ([A-Z][a-z]+) + handle /* style comments */ 2006-04-17 v0.44 Tels 1801 tests * new/changed attribute: + add 'group' for nodes + only allow /[a-z][a-z0-9_]+/i for classnames as documented * Base: warn() uses Carp::carp instead of plain warn() * we no longer need Heap: we have our own el-cheapo Heap class. And it is faster, too, todos/big.txt takes 3.6s vs. 4.2s with v0.43 and Heap::Binary * Layouter fixes: + a short vertical edge starting from a multicell node was rendered as horizontal instead + an edge sharing an end point with another was generated incorrectly if (one of) the other edge(s) was running alongside the starting node (e.g. where we could immidiately create a joint and be done) + likewise for edges splitting up, if a shared edge runs alongside a node, then a joint will be created there immidiately + fix bidirectional edges with a joint as start piece + Don't warn on edges with no cells (on joints, under debug) about being unable to find path + improve placement for nodes where their edges share an end point + improve placement of nodes for shared edges with labels * cleaned up the POD a bit and added a few remarks and notes * as_ascii: + deal with invisible edges at cross sections * as_html: + implement joint types with start or end points * fix the repair code that handles edges inside groups: + repairs more cases + is faster by reversing the checks and using grep + cut down on copy&paste code * Edge/Cell: fix arrow_count() for undirected and bidirectional edges * Easy.pm: + fix bug in class_styles() ignoring base class name for multiple classes, affected SVG output * Parser/Graphviz: + relax parsing of node names (G, GROOVY etc are valid w/o quotes) + allow "graph [ ... ];" (semicolon after attribute section) + remap attributes: graph: rankdir; edge: minlen, dir 2006-03-19 v0.43 Tels 1755 tests * add attributes: + text-wrap: 'auto': auto wrap label, 'none' is the default + id: unique identifier for each object (usefull for animation later on) + font: comma-separated list of font family names/generic names + format: 'none' (no special processing, default) or 'pod' (enables inline text formatting ala B<bold>, I<italic> etc) * Node.pm: avoid autovivify of ->{edge} in background() and use ->{group} * Edge/Cell.pm: set {group} to edge's group to make things easier * Layout/Path.pm: add some pods for the internal routines * add more .pm files to t/pod_cov.t and t/pod.t * it wasn't possible to set an edge label via a class or subclass: edge { label: yes; } edge.no { label: no; } [ A ] --> [ B ] --> { class: no; } [ C ] * as_html: + table cells inherit some attributes like "color" from the table, this caused all labels to be the same color as the graph if you set a color there: graph { color: red; } ( Cities: [ Bonn ] -- train --> [ Berlin ] ) Likewise for fill, align, font, font-size etc. These are all fixed and tests make sure that this doesn't happen again. + fix CSS + "vertical-align: center;" has to be "vertical-align: middle;" + don't output bogus "flow" attributes + don't output spurious edge class "ha" + merge classes "ve" and "el" into "el" + draw arrows on short hor edges closer to the line + graph and edge labels need to use _label_as_html() to properly quote "<",">" and "&" as well as have support for \n,\r,\c and \l + fill and background color for graphs were swapped + do not combine table cells if the returned cell code contains two successive cells + fix EDGE_S_E + EDGE_START_S * as_graphviz: + add support for edge styles "double" and "double-dash" by emitting "color:color" for the edge * Edge.pm: + add flip() - swap from/to for this edge + remove dead code in flow() and fix POD for it * Layouter: + added a post-layout optimizer + compact edges by combining VER or HOR edge cells into one cell (fixes overly long edge labels stretching the layout) + add a method to grow multiple column/row sizes to accomodate a nec. multi-column cell (node/edge), this creates more compact layouts with multi-cell objects in them. + repairing edges after splicing in groups did not account for joints, the joint and the next piece might not belong to the same edge. Thanx to Florian Lindner for a testcase. + Edge.pm: port() did falsely return ("east","0") for "east" instead of (undef,undef), causing edges with "start: east" to get wrongly split up * Parser: + refactor code to allow easier subclassing + require and use Parser::Graphviz on detection of graphviz code + allow quoted attributes that contain ";" like: 'label: "bar;baz";' * Attributes: + clarify rules when to quote, escape and encode + only decode %xx for xx between 0x20 and 0x7e (to stop exploits) * add Graph::Easy::Parser::Graphviz - parse graphviz text and convert it 2006-02-21 v0.42 Tels 1674 tests * add the missing testfiles to MANIFEST * add a few examples to the doc * remove the static classes "l" (a) and "i" (img) - these can be styled by CSS selectors, so there is no need for them. * as_html: + use unicode chars for filled/closed arrow heads + fix garbled class names on undirected edges (caused by removing "v") + fix output of default left-aligned group labels and add test for that + output "<br \>" instead of "\n <br \>", because due to "whitespace: nowrap;" this did insert spurious spaces before each line break, throwing center-aligned labels off-center. * as_graphviz: + output correct minlen * Parser: + Reverse the removing of trailing/leading "|" in autosplit nodes, introduced in v0.34. [|G|] is " ", "G", " ", and not just "G". The unconditional removal of leading/trailing empty parts meant you could never have an empty trailing part, ever. Fixes also the regression from todos/hash.txt no longer working. Adjust doc to reflect this change. + add more tests for trailing/leading empty parts. * Layout.pm: + typo: {_chain} vs. {chain} (Thanx Jim Cromie) + don't use alarm when under the debugger (Thanx Jim Cromie) 2006-02-05 v0.41 Tels 1670 tests * use Module::Install and bundle it in inc/ * add "point-style: invisible" for very small, invisible nodes - nodes with "shape: invisible;" will still have a minimum size. * add support for '\l', '\r' and '\c' as line breaks to align individual label lines left, right or center * as_ascii: + support proper alignment on edge labels * as_graphviz(): + fix invisible nodes to not show their label + add support for nodes with "shape: point;" * t/graphviz.t: reduce the number of unnec. calls to as_graphviz() * as_html(): + use white-space: nowrap; (not "pre") + add support for \l, \r, \c and don't include text-align twice * Node: + remove the unused and undocumented "noquote" param from as_html() * Graph: + add: add_edge_once(): add an edge from A to B only once (Thanx to Florian Lindner for the suggestion!) 2006-01-29 v0.40 Tels 1652 tests * add attribute: + "invisible" as edge style + "minlen" - for edges (minimum distance in cells) + "align" - alignment for graph, group, edge and node labels * General: + fix Makefile.PL to work with newer YAML + avoid duplicate code when dealing with labels and dimensions + empty attribute-parts on autosplit nodes work now: [ A|B ] { class: |legend; } will put B into class 'legend' and leave the class of A alone [ A|B ] { class: legend|; } will put A into class 'legend' and leave the class of B alone [ A|B ] { class: legend; } will put A *and* B into class 'legend' + _class_styles(): use correct indent instead of hard-coded ' ' * Edge: * start_port()/end_port(): + remove spaces to normalize result + return list in list context * Node: + add nodes_sharing_end(), is_anon() + remove place() from public API (is now _do_place()) + allow spaces in set_attribute('size', '9 , 8') * Node::Cell: + add a group() method to make things like $cell->group() easier * Group: + is also a Graph::Easy, to inherit from it (for nesting) + find right-most cell for label if "align: right" + rename find_label_cell() to _find_label_cell() (is internal use only) * Graph: + add use_class(), and use it instead of hard-coded class names + due to bad caching, one couldn't set the node size as class-attribute + weaken() is costly and O(N), so avoid it whenever possible. Graph creation and destroy are now O(1) again. Creating a graph with N nodes and N-1 edges takes (bench/stress.pl): N 0.39 0.39 | 0.40 0.40 Create Destroy | Create Destroy ----------------------------|------------------------ 4000 0.35 0.23 | 0.31 0.12 8000 0.77 0.68 | 0.61 0.21 16000 1.91 2.26 | 1.19 0.48 32000 9.32 8.02 | 2.32 0.78 * Parser: + add use_class() to use user-provided classes for generated objects + referencing an (empty) part of an autosplit node before the autosplit node itself would create wrongly a stray node + simplify group code ($graph->add_group() does everything nec. already) + store labels for autosplit nodes not in the label attribute, but seperately. This fixes the problem that you couldn't put a node (resulting from an autosplit) into a class and set a label on the class - the node would always show the part name as label. In addition that fixes the long-standing bug that as_txt() would output stray labels for autosplit nodes. + parts in an autosplit node are relative to their preceding part, not to the first part. This enables setting columns/rows/size on parts and the other parts will flow/adjust properly. + set the basename property on the first part in an autosplit node, enabling proper as_txt() output of it. * Layouter: + implement the same spacing rules between nodes for end points as 0.39 did for start points: edges joining up to a node work now + only use 3 cells space when there are more than one node sharing the same port (was "more than zero") + fix a problem when placing nodes without an edge object resulting in a crash + repair edges outside of groups, as well as edges that end at a corner + multi-celled nodes in spliced layouts (e.g. with group fillers) did not preserve their filler-cells correctly * as_txt(): + clean up nodes after output so that a following as_graphviz() does work * as_graphviz(): + fix links involving groups: lhead/ltail were swapped + generate shorter invisible node names ("A,south,0" vs "A,south,0,0") + do not create invisible joint-nodes when there is only one edge sharing the start/end port + make "shape: rounded" work (needs "style=rounded,...") + support label alignments on groups * as_ascii(): + round corners for shape: rounded (using ' ', instead of using unicode characters by accident) + support for aligned labels (left|right|center) * as_boxart(): round corners for bold/wide/broad etc using spaces * as_html(): + make space below edges smaller, to create more compact layout + a color on a group was ignored, resulting always in black group labels + finally align the vertical arrows with the edge lines. In Opera 8.5 under Linux the "v" now gets cut off, but this seems a browser bug. Sorry, some of the Opera users! + support label alignments (center|left|right) + when a class attribute specified the font-size, don't include it at at the td (node|edge|caption etc) again + edge { arrow-style: none; } did not remove the arrows from edges + group cells need ' ' for IE to show the border - I hate IE bugs :-( * error() is now fatal by calling _croak() - no longer do errors in attributes go undetected and cause havoc later on. You can use $object->no_fatal_errors() (on any object) to supress this. 2006-01-08 v0.39 Tels 1603 tests * Graph: fix merge_nodes() to delete node B from group if part of one * fix Group: + del_edge(): delete edge on id, not non-existant name + del_node() & del_member(): drop all edges mentioning the node + optimize edges() and nodes() in scalar context * Graph: improve handling of "|" in attributes (especially labels) * Graph: del_node() and del_edge() also remove these from their group * Edge: + add start_at(), start_port(), end_port() + fix POD start * Node: add nodes_sharing_start(), shared_edges(), has_predecessors() * add source_nodes() and predecessorless_nodes() (Thanx to Florian Lindner for the suggestion!) * as_html(): add "white-space: pre" to the CSS to prevent wrapping of labels with spaces in them * as_graphviz(): + implement edge joints (shared start/end port) via invisible joints + handle attributes like labels with embedded newlines + output edges from/to groups (subgraphs/clusters) * Layouter: + allow sharing of end ports (making edges join up on the way to the target node) + min distance between node is 3, not 2, when there are edges that share a start point (to make room for the joints) + Layouter: place nodes with a shared start point in the same column/row than the others (to render tree-like layouts automatically) * multiple attributes for autosplit nodes were broken, when: + the attribute did not have a strict check (link, f.i.) + there was a newline after the node ala "[A|B]\n { fill: red|green;} + the attribute did have a fixed list of words (like "shape") (Due to Ron Newmann's question I stumbled over these bugs - thanx! * add support for "shape: img;" (Thanx to Ron Newmann for the suggestion!) + as_html: use "<img...>" + as_graphviz: use "<<TABLE .. <IMG...>>" 2006-01-01 v0.38 Tels 1571 tests * include the missing 4_flow.txt test files * Group.pm: fix POD * Parser: attributes on nodes in a node list apply to all nodes in the list up to this point. So: [ Bonn ] { color: blue; }, [ Berlin ] { fill: black; } will fill both nodes black, but only color "Bonn" blue. * remove unused arrow-code on hor-selfloops * as_ascii: draw bidirectional self-loops with two arrows (Thanx Dan Horne!) * Layout: fix bidirectional edges created by A* (multi-celled nodes) and the straigh-path shortcut (more than one cell distance) (Thanx to Dan Horne again) * Layout: ignore links from/to groups, do them after splicing in group filler cells * Attributes: remove direction() and add: + _direction_as_number() + _flow_as_number() + _flow_as_side() + _direction_as_side() * Graph: remove the unused and wrong angle() method * Graph: allow add_edge('Node', 'Node2', 'edge label'); * Edge: add flow() and port() * Node: add flow(), angle() * check POD coverage of Graph::Easy::Attributes and As_ascii * fix the flow model: correctly handle absolut (south/180/east/90/down etc) and relative (left/right/front/forward/back), as well as global and local flow on a per node/edge basis * as_txt(): output the original flow/start/end attribute instead of a converted value ('down' vs 180, 'left' vs XX) * Base: add _croak() method * Node: growing nodes with edge end/start point restrictions did make the node one to big on each restricted side * use constant instead of home-grown "sub CONSTANT() { 1; }" * as_graphviz(): output correctly nodes in a group w/o attributes and edges * as_graphviz(): support correctly most node border and edge styles, even in combination with fillcolor. Thanx to Ryan Schmidt! 2005-12-27 v0.37 Tels 1533 tests - Sing, sing, sing the birthday song... * correctly handle '\\n' in labels/titles * Node::Cell: allow 'x' and 'y' as arguments * Layout: repair split multi-celled nodes after inserting group fillers * Layout: insert short filler edge pieces on spliced edge for group layouts (fixes "holes" at the start and end of edges in ascii layouts with groups) * Layout: set smaller minimum-size for group cells at non-corners * Layout: grow group fillers to close gaps between nodes in things like: "( Group: [ A ] [ B ] )" * Parser: handle "([A]->[B])" (allow empty group name) * Parser: don't set group name of '0' to '' * Parser: handle "] -> ( ..." (edge from node to group) * Parser: handle ") -> ( ..." (edge from group to group) * Parser: "...) -> [ A ]" is a group-to-node edge, not node to node * add attribute "flow" for edges (per-edge flow) 2005-12-18 v0.36 Tels 1516 tests * Parser: add support for node lists on the right-hand side: [ Bonn ], [ Berlin ] -> [ Potsdam ], [ Halle ] * allow "inherit" for colors * Node: fix edges_at_port() to skip edges not starting/ending here * Group: fix add_member() and del_member() for edges * Group: fix add_node() * Node: fix background() * Graph: fix default fill for groups in HTML * Graph: add_group(): return $group, not $self * Layouter: + stop chain-tracker at nodes that are grandchildren of the current node + grow nodes based on used/free ports (fixes shared ports) + grow nodes only in one direction if they have 'rows' or 'columns' + shuffle self-loop directions according to graph/node flow + A*: no longer cross edges along their flow line (leading to corrupted layouts with multiple crossings in a row) + convert edge joiners to joints for shared edges * render joints in as_ascii, as_boxart and as_html * as_ascii: render no arrows on undirected edges (esp. for selfloops, corners and cross sections) * as_html: fix edge color if only "label-color" is set * as_html: fix self-loop on east side * as_html: render no arrows for undirected edges * as_graphviz: output headport/tailport for edges with "start" or "end" (this doesn't seem to work in "dot" at all, sadly :-( * as_graphviz: output groups as clusters (subgraphs) * do multiple edges from A to B,C,D etc in the order of "B,C,D" instead of pseudo-random order 2005-12-10 v0.35 Tels 1483 tests * Node: fix parent() * allow "$graph->add_group('Name');" * render group labels * close edge gaps created by inserting group filler cells * group label font-size defaults to 0.8 * fix doc about default font-size (0.8 for edge/group labels, not 0.75) * as_html: fix borders of groups without a special class/border * as_html: fix content and background of nodes with "shape: point;" * as_html: fix group backgrounds (especially with edges and class/non-class attributes) * as_html: don't need to put text/font attributes on empty (group) cells * as_html: fix shapes like "circle" on nodes with a link * remove relicts of old group handling code in Nodes/Group Cells * use more isa() instead of ref() checks for class names * "class { label: ...; }" was ignored and did not change the label * allow: "start: south , 1;" (space before ",") * add directions "left", "right", "front" and "back for "start"/"end" (these are flow-dependend, unlike "south" etc) * add attribute 'autotitle: link;' * add attribute 'autolabel: name;' and "autolabel: name, N;" * make "autotitle: none;" work * document and test that "autotitle: name" on edges will fall back to the edge label * document and test that a present title will override "autotitle" * implement edge-joints: + A* will use edges for start cells if they share the same port 2005-12-04 v0.34 Tels 1437 tests * as_html: align arrow heads better with the edge line (still breaks sometimes, espc. with edge labels) * as_html: implement missing edge corner piece E->S * as_html: implement selfloop edge pieces * as_html: fix crossings of different edge styles * as_html: putting a "fill" or "border" on an node with a link did put these styles on the link, not on the table cell. * as_ascii()/as_boxart(): render group borders * as_graphviz(): fix arrows on undirected/bidirectional edges * A*: compute the working space and disregard cells outside this area This prevents the run-away situation in case no path can be found. * A*: did not close crossings on edges, getting it stuck in an add-del-add cycle until it hit the hard limit of 10000 steps, thus never finding the path. * A*: find less bendy paths (no inward bend corners anymore) * fix a bug in trying placements of nodes, where the node would be registered on all potential places, instead of only the final selected one. In HTML, the node would thus appear multiple times in the output. * Graph: streamline sorted_nodes(); * Edge: keep edge cells in sorted order (from start to end), and allow add_cell() to insert a cell into the list (to repair edge-splitting from the group-padding code). Also allows rendering cells in order. * Edge: has_ports(): return true if start or ending port of the edge are restricted in some way (attributes 'start' and 'end') * Nodes relative to multi-celled nodes did not take into account the size of the origin node (when the offset was positive in X and/or Y dir.). * add Graph:Easy::Base as base class for common code for all objects * add Graph:Easy::Layout::Chain for internal use in the Layouter * completely rewrite the chain-tracking code, and the code that generates layouts based on the chain info. Handles now bidirectional edges, forwad/backward loops and is generally more robust. * Layouter: grow nodes by 2 in each direction, instead of 1. This fixes layouts with star-shaped auto-grown multi-celled nodes (try to say this three times fast...) where you have on node at the center. Since nodes are never placed next to each other, growing the node by 1 did not make enough space for all adjacent nodes. * use diag() instead of printing to STDERR in tests * edge crossings with a start/end point had the type check wrong and thus could lead to the styles being switched around (HOR vs. VER) * Graph: add svg_information() * add attribute "basename" to autosplit nodes to make referencing easier * add edge attributes: 'start' and 'end' - node starting/ending port to specific node ports for specific layouts * create Empty nodes for autosplit nodes again (regression) * trailing/leading empty parts on autosplit nodes are always removed, so "[ |A|B| ]" equals [ A|B ] * move _prepare_grid() to Graph::Easy::Layout::Grid and load it on demand * autosplit nodes only get an unique ID when their basename doesn't already exist, so this now works: [ A|B ] { basename: one; } [ C|D ] { basename: two; } [ one.1 ] -> [ two.0 ] 2005-11-13 v0.33 Tels 1337 tests * simplify group code: Nodes can only be in one group at a time * Groups: edges with their to/from nodes in the same group are also added to that group * as_html: put correct group class on edge cells, so that they inherit the background from the group * as_html: if nec., output CSS for anon nodes * as_html: implement missing corner pieces (W->S, S->W etc) * as_graphviz: use "setlinewidth()" to simulate bold, broad and wide borders * Node: add size() * add a few more examples to the attributes in the doc (for manual) * A*: for each start point: calculate distance to each stop point and use the lowest one. Fixes tricky layouts with multi-celled nodes. * Layouter: implement direction shuffling for multi-celled nodes * Layouter: fix code calculating edge end/start points to handle corners * update the check for As_svg's VERSION in Makefile.PL * Easy.pm: don't needlessy import non-existing GROUP_MAX (Thanx to PPI!) * Layouter bugfixes: + paths with one bend did not check the corner-cell for being already occupied, leading to obscuring nodes/edges on that place + failing to find a bend path horizontal->vertical, the code tried next vertical->horizontal, but did not remove the already tested parts, leading to stray edges pieces (poss. obscuring other parts) * defining an "offset" and "origin" on an autosplit node caused an endless loop in the layouter: [ 1 ] --> [ 2|3 ] { origin: 1; offset: 2,3; } * Layouter: autosplit nodes w/o edges, and chained nodes caused endless loops when placing them. Examples that did hang the layouter: + "[1]->[2] [3|4]" + "[1]->[2] [3] [4] { origin: 3; offset: 1,0; }" + "[1]->[2] [3|4|5]" * Parser: referencing a part of an autosplit node before the autosplit node did result in endless loops or crashes. "[1] -> [23.0] [2|3]" works now correctly. * Parser: accept "size: 1, 2;" as valid (spaces around ",") * Parser: "[1],[2] --> [3]" should result in two edges to [3] (from 1 AND 2) * as_txt: "size: X,Y" did make columns and rows appear swapped in output * as_txt: output "size 1,2;" instead of "rows: 2; columns: 1;", except when only rows is defined * as_txt: quote special chars [";%\x00-\x1f] in attributes on output * Graph: added as_boxart_html_file(), _border_attributes_as_html(), anon_nodes() * Graph: rename border_attributes() to split_border_attributes() * added Edge styles: "broad" (0.5em), "wide" (1em), "bold-dash" * added Node border-styles: "broad", "wide" and "bold-dash" * better support for border-styles in HTML * support borders on anon nodes ala "[ ] { border-style: dotted; }" * Nodes with "shape: invisible;" are at least 3 chars wide (was: 5) * use Carp::confess() instead of Carp::croak() to aid debugging * setting "{ border: broad red; }" will set the style ("broad") and color ("red"), but leave the width alone * use isa('Graph::Easy::...') instead ref() to check for Node/Edge * Edge::Cell: add arrow_count() 2005-11-06 v0.32 Tels 1291 tests * as_boxart(): + use "v" and "^" instead of 0x2227 and 0x2228 for arrows (because most fonts seem to not have these characters) + border/edge styles "dot-dash" & "dot-dot-dash" use midot instead of "." + fix horizontal pieces of dot-dash borders * as_ascii()/as_boxart(): + align point-shaped nodes with edge lines + edge-pieces with style "dot-dot-dash" must be at least 6 characters wide to properly show the full "..-" style * as_graphviz(): fix handling of special characters like [;"] in attributes * Parser: accept "offset: 1, 2" (space around ",") as valid * Graph: prevent adding of Nodes with no name * Graph: can add nodes with name '0' * Graph: added del_node(), del_edge(), merge_nodes() * Graph: edge(X,Y) returns list of all edges from X to Y in list context * Node: add incoming(), outgoing() * Node/Edge: setting an attribute resets the size cache to force a re-calc (the node should grow/shrink when you change the label) * fix an undef warning in Edge/Cell line 713 * streamline printfb_line() and use it whenever possible * Node: more rebust detection of class in set_attribute (no longer depends on blessed class name) * Node: add attribute "rotate" * preserve cell sizes after as_ascii()/as_boxart() for next call (multiple calls to as_ascii()/as_boxart() in a row did grow node-heights by 3 every iteration) 2005-10-30 v0.31 Tels 1171 tests * require at least Perl 5.8.0 (we need proper Unicode support) * Edge.pm: remove unnec. _formatted_label() * fix examples in t/fun and t/syntax * include newline before first =head1 (Thanx to Ivan Tubert-Brohman!) * fix package name in As_ascii.pm * As_ascii: add _u8() (for box-art) * add: as_boxart(), as_boxart_file(), as_boxart_html() * add examples/as_boxart * examples/as_*: input utf8 from STDIN * Parser: do a binmode ':utf8' on input files * support "graph { output: boxart; }" * add some X<> keywords for POD indexing * Graph: you can call get_attribute('attribute'), too (the class is now optional and defaults to 'graph' if missing) * Graph: you can do set_attribute('attribute','value'), too. * added attributes: + edgeclass for groups + font-size for graphs, groups, nodes and edges + text-style for graphs, groups, nodes and edges + arrow-style: none for edges * clarify that "label-color" falls back to "color" if unspecified * setting "edge { style: X; }" changes the style for all solid edges to X * Node: add parent() - return parent object (group or graph) * Parser: throw correct error message for unknown attribute names * horizontal straight, non-short edges were missing their arrows * as_txt(): fix output of undirected edges (" -- " vs. " -- <") * as_txt(): "origin" and "offset" attributes were missing in output * handle a label of "0" correctly * as_html(): render edge cross sections with the correct styles and colors * as_html(): do not output invalid CSS definitions for edges (style, label link, title etc) * as_html(): setting a color on a node with a link works correctly now * as_html(): 'autolink' and 'link' on edges correctly create links * as_html(): 'title' on edges inserts a mouse-over title on the edge label * as_html(): remove trailing empty <tr></tr> pairs * as_html(): put the "empty-cells" definition inside table.graph * as_html(): create links on graph label (caption) if requested * as_html(): include the graph label into the table, this looks more consistent than the <caption>, which placed it floating above or below the graph * as_html(): repaired the output of group cells (see t/fun/0131.txt) * as_ascii(): do not draw border for nodes with "shape: none;" * as_graphviz(): nodes with shape none work now correctly * as_graphviz(): color of edge labels falls back to edge color if no label-color is specified (was formerly rendered black) * _css_styles(): inplement $overlay support (add custom attributes) and skip the border-attribute generation if it matches $skip (both fixes are mainly for SVG output) * setting attributes on Edges/Nodes/Groups invalidates a former layout 2005-09-23 v0.30 Tels 1114 tests - Draw, draw, draw, draw the corners... * ascii.t could fail on very slow machines * fix pod/t. and pod_cov.t when the Pod::Test modules are not installed * fix as_ascii_html() to end with "</pre>" * as_html(): do not combine non-empty cells 2005-09-18 v0.29 Tels 1113 tests - Draw, draw, draw, draw the corners... * add attribute "label-pos" to class "graph" * remove only quotation marks in attributes when they are both at the start and end of the text. So "label: 2';" will work correctly now. * as_html(): work-around for IE not displaying edge corner pieces * as_html(): put caption below graph if label-pos: bottom * as_html(): use remap_attributes() * as_html(): correctly output nodes with "shape: none;" * as_html(): encode ' in links to %27 * as_html(): do not combine cells with a set border (this produces incorrect output) * as_html_file(): set correct document charset, and <title> tag * as_ascii(): output graph label, below or above, according to label-pos * as_graphviz(): remap border-styles, add support for border-style: double * as_graphviz(): handle attributes in node, edge and group classes, too * as_graphviz(): handle graph attributes: label-pos, label * as_graphviz(): skip over graph attributes: gid, autolink etc * as_graphviz(): fix output of groups * as_graphviz(): fix output of graph border and style: none * as_txt(): do not list nodes twice in output of groups with 'nodeclass' * Graph: set_attributes() also goes via set_attribute(), e.g. properly checking arguments, handling "gid", decomposing "border" into border-(style|color|width) etc. * default background attribute is '' * drop node shapes: egg (looks ugly, and impossible to support except in graphviz and SVG) doubleoctagon (already done by "shape: octagon; border-style: double") * optimize Graph::Easy::Group::Cell::_set_type, makes complex layouts with groups about 10% faster overall * remove unnec. routines in Graph::Easy::Group::Cell * streamline $graph->groups() in list context * Layout: Nodes are not placed next to each other (unless offset: is set) * Layout: consider HOR and VER edge pieces running alongside a node as potential start fields with penalty * Edge/Cell: export EDGE_NO_M_MASK * Node: add backgroud() routine that finds out the real background (for groups etc) * add POD tests via Test::POD (t/pod.t) * add POD coverage tests via Test::POD::Coverage (t/pod_cov.t) * 100% POD coverage for Nodes, Groups, Edges and Graph::Easy * move build/ dir to Graph::Easy::Manual 2005-09-05 v0.28 Tels 1058 tests - Offset, offset, offset, offset the position... * fix LIMITATIONS section in POD * fix POD in Graph::Easy::Attributes * fix testsuite to match reality * as_html(): fix output for groups * as_html(): combine equal table cells to shorten output (by 10% or so) * as_html(): remap "fill" to "background" for classes (especially "graph") * as_html(): caption gets graph's background as background 2005-09-04 v0.27 Tels 1058 tests - Offset, offset, offset, offset the position... * remove Graph::Easy::Cluster, and overhaul relative-node position code + use "[ B ] { origin: A; offset: 2,0 }" to position A relative to B (no cluster needed anymore) + can position C relative to B, which is relative to A, and so on + works correctly with multi-celled nodes + update doc for this feature * remove "point-style: none" - "shape: invisible;" already does the same * remove "point-style: ring" - is: "point-style: circle; fill: white;" * add "point-style: diamond" * add attribute arrow-style (open, closed, filled) for edges * add attribute "fill" (color inside shape) and change notion of "background" to the color outside the shape (important change for nodes) * add attribute "label-color" for edges * attribute "border-style" was accidentily allowed only as "border-shape" * remove node styles: box, rectangle (were aliases for "rect"), plaintext (was alias for "none") * Save memory: Nodes use 638 (vs 770) bytes, Edges 631 (vs 683) Bytes, this makes a big graph use about 10% less memory (and it is generated in about 6% less time) (measured on 32 bit system) * fix small nits in Graph::Easy::Attributes * define possible values as lists in Graph::Easy::Attributes, removes double definitions in both code and documentation that could get out of sync * examples/common.pl was broken - thanx to Vagn Johansen for the report! * disallow arguments to new(), leave only in: * Node: "label" and "name" * Edge: "label", "name" and "style" * Parser: "debug" * Node::Cell: "node" * remove useless subroutine contains() in Node.pm * build/gen_manual: generate doc for group attributes, too * build/gen_manual: generate color table from definitions in code * build/gen_manual: split attributes into 4 sub pages and add an index * build/gen_manual: include sample graphs * as_graphviz(): quote reserved words: /(strict|(sub)?graph|node|edge)/i * as_graphviz(): quote attribute values with non \w chars * as_graphviz(): support for all flow directions (left, right, top, bottom) * as_graphviz(): fix colors because dot et. al have a different idea on what color "grey" actually is. This is solved by making checks on attributes on Nodes etc. strict (it converts "red" to "#ff0000" on input) and then outputting the raw RGB colors * as_graphviz(): support for bidirectional and arrow-less edges * as_graphviz(): add support for different arrow styles (except filled with different fill color) * as_graphviz(): support anon nodes properly (rendered invisible) * as_graphviz(): correctly handle link, linkbase and autolink attributes * as_graphviz(): use graph ID to construct graph name (usefull for imagemaps) * as_html(): reimplement output for edges with 4x4 table cells, including correct start/end points, label colors. Finally works in all major browsers and looks better than ever :) * as_html(): generate graph-table caption from label attribute * as_html(): correctly handle multi-celled nodes * as_html(): generated links have class 'l' * as_ascii(): handle multi-lined labels on edges correctly * as_ascii(): invisible nodes are 3x3, not 5x3 in minimum size * don't unnec. load "Heap" and "Heap::Fibonacci" * Graph: add as_txt_file() as alias for as_txt() * add weaken() in Edge::Cell to trigger early DESTROY of graphs * move the ASCII code to Graph::Easy::As_ascii and load it on demand * Parser: report wrong attribute names/values better * Parser: allow '$graph = Graph::Easy::Parser->from_text("...")' * Parser: fix parsing of undirected edges with following nodes/attributes * Parser: parse "[ A ]\n { ... }" and "[ A ] { ...\n...}" properly * Parser/Layout: support undirected (arrowless) edges properly * Layout: set proper flags for bidirectional edges (so they are rendered correctly (Thanx to Scott Beuker) * Layout: stop an endless loop in the chain-backtracking code * Layout: set correctly edge label cells on found A* paths * setting the attribute 'origin' and 'offset' on a node works now correctly * attribute are now strictly checked on Nodes, Edges etc, too * fix unitialized warning in Graph::Easy::Layout::Scout * fix broken link to EXAMPLES in POD (Thanx Scott Beuker) * border_attribute(): return no width if style eq 'double' * remap_attributes(): fix handling of 'all' entries for attributes * remap_attributes(): add handling of 'undef' to force attributes 2005-08-21 v0.26 Tels 1007 tests - Speed, speed, speed, speed up the code... Fixed: * as_ascii(): render node labels centered, instead on top-left corner * as_ascii(): on clustered nodes, collapse borders * as_ascii(): output edge labels on VER edge pieces, too * as_ascii(): render end/start points on CROSS edge pieces * as_ascii(): multi-celled nodes only worked when layout was not completed * as_ascii(): crossing between different line styles are rendered better than simple '#': | | | | : H ===+=== ###+### ...!... ~~~+~~~ ---+--- ---H--- | | | | : H * create shorter self-loops (block only 1 cell/1 port vs. 5 cells/2 ports) * add_edge('A','A') works now, and creates only one node object * make node attributes "size", "columns" and "rows" really work * Node: make edges_to() work correctly with self-loops * A*: only cross HOR/VER edges when they have no start/end points nor labels * A*: use Heap::Binary (slightly faster) and inline $elem->fields() call * Layouter: try to place nodes in chains first near their chain parent * Layouter: better fallback strategy upon blocked paths * edges with one bend now correctly have their label on HOR piece * make attribute checks via Graph and Parser "strict", e.g. it will fail on unknown attributes or invalid values * fix parsing nodes after attributes like: "graph { color: red; } [ Bonn ]" * we do not need to create filler cells unless there are groups, this makes generating layouts w/o groups between 10 and 30% faster. Added: * color attributes are now stored as '#ff0000' and output as 'red' or '#ff0000', depending on output format. * Graph: option "strict", on by default, this makes set_attribute() check all attributes and their values strict * add option "flow" to graphs and nodes: general flow of edges * near_places(): takes list of edge types * A*: allow last edge piece to cross a horizontal/vertical edge * build/gen-manual: generate manual page with attribute descriptions from table in Graph::Easy::Attributes automatically 2005-08-12 v0.25 Tels 885 tests - Speed, speed, speed, speed up the code... Added: * $graph->add_edge('A','B') works now and is documented * $graph->add_edge('A','B') in list context returns nodes and edge * $graph->add_cluster('A') works and returns created cluster object * Graph::Easy: timeout(): get/set timeout in seconds for layouter * Layout: calculate chains-of-nodes, and lay out path along these chains longest one first. This greatly detangles many layouts. Fixed: * as_ascii(): Drawing line crossings now works. Really! Believe me! * as_ascii(): labels on horizontal edge pieces are now included * rename "todo/" to "todos/" because case-insensitive OS have problems with "TODO" and "todo/"... * layout: move the alarm(0) to where it belongs and propagate fatal errors to the outside * Layout: calculating positions needs an int() to have nodes not fall between the cell-grid * Node: move the assignment to {id} into new(), so that subclasses like Graph::Easy::Edge etc get a unique ID, too * use Scalar::Util's weaken so that the Node's references back to their graph do not prevent the graph from being freed * Upon DESTROY, reset the appropriate fields in node/edge objects, so they can be reused safely in other graphs * A*: fixed an endless-loop in the path-backtracking code * adding node clusters as well as managing them is now easier * No longer uses Graph. We store nodes/edges in a hash in Graph::Easy object as opposed to the Graph object. In addition we store per-node the edges this node is involved in. This all results in less code, great speed and memory improvements (see bench/no_graph.txt): v0.25 uses only about 50% of the memory as 0.24! Even the testsuite is much much faster, despite the new tests: 0.24: Files=23, Tests=820, 5 secs ( 4.37 cusr + 0.50 csys = 4.87 CPU) 0.25: Files=23, Tests=859, 4 secs ( 2.37 cusr + 0.31 csys = 2.68 CPU) Loading Graph::Easy now takes less time & memory, too (output from top): VIRT RES SHR 0.24: 8252 5712 1444 0.25: 5840 3376 1432 2005-08-07 v0.24 Tels 820 tests - Fix, fix, fix, fix the bugs... Added: * node attribute: "point-style" - set style for nodes with "shape: point" * remappig of attributes can take code-ref for special handling * A*: create crossings, and proper edge end/start pieces, fix bias Fixed: * Graph::Easy POD: add note to add_edge() about multi-edges * correct POD in Graph::Easy::Node::Cell * Graph::Easy::Edge->new( label => 'foo' ); did not work * remap_attributes(): suppress empty attributes (fixes as_graphviz) * as_graphviz: suppress "border-style" on edges and nodes * as_graphviz: properly remap edge attribute "style" * as_graphviz: do not include class as attribute in output * as_graphviz: fix broken comment (graphviz uses //, not #) * as_graphviz: default node settings are included in class "node" * as_graphviz: escape '"' (double quoting char) to avoid issues with node names like '2"'. Also avoid quoting plain node names when possible. * as_graphviz: output nodes with their name, not their label * as_graphviz: multi-edged graphs work now correctly * as_ascii: implement rendering of nodes with "shape: point" * as_ascii: render edges with style 'bold' * as_ascii: properly render edge pieces without gaps * as_ascii: render edges seemless (".-.-.-.-" vs. ".-.--.-.-.-") * as_ascii: render all combinations of edge pieces and start/end points * as_svg_file: did not pass $self to _as_svg(), inducing a fatal failure * Parser: drop support for parsing reversed edges ("<--"), this never worked reliable and made parsing of normal edges with labels much harder and error-prone due to ambigitous cases. * Parser: fix parsing of bidirectional edges * Parser: fix parsing of edges with labels containing "-", "[" etc (edges labels can now contain any character unescaped except ">", which must be escaped with "\" as "\>") * Layouter: weed out positions for a node that are too close to a successor 2005-07-31 v0.23 Tels 765 tests - Fix, fix, fix, fix the bugs... Fixed: * update POD to reflect that the overall placement strategy is now better * warn on broken Graph 0.66 * t/parser.t: sort edges on label to avoid hash key ordering tripping us up * sorted_nodes(): use "name" as second field if a non-unique first field sorting was requested (like sorted_nodes("layer")) * _near_places() works correctly on multi-cell nodes and add a parameter $d to it, so that the distance can be adjusted by the caller. It can also return direction of place from/to node via third paremeter. * Layout/Scout: removed _find_path_u_shaped() (use A* instead) * cleaned up path finding/scoring/handling code * EDGE_SHORT_N vs EDGE_SHORT_S to be 0,1,2,3 in order E,S,W,N * self-loop paths (from A to A) correctly set EDGE_LABEL_CELL on the HOR edge pice * finding paths is now slightly faster: self-loop : 20,000 tries/s (vs. 15,000 tries/s) short edge: 74,000 tries/s (vs. 21,000 tries/s) * Paths with one bend now have always the correct edge piece. * Placement of node with two predecessors in a straigght line was wrong (instead of trying to place the node in the center, it placed it 1/2 the distance to the right/bottom - Thanx to Scott Beuker for providing a testcase!) * as_ascii(): align the different edges better * as_ascii(): the border is now drawn into a framebuffer, this makes it possible for collating borders or to have "border-top: none" * Edge are nown drawn seemless, except these pieces: CROSS S_W N_W In addition, the seems break sometimes on edge styles "- ", "= ", ".-" and "..-" due to non-alignment. * move color checking/converting code to Graph::Easy::Attributes * check border-styles against list of valid names * as_ascii(): added the ability to have different pieces for corners * as_ascii(): border styles 'wave' and 'dot-dot-dash' have now two different vertical characters, rendering these borders now like: *~~~~~~~~* +..-..-..+ { Test { | Test | } Node } : Node : *~~~~~~~~* +..-..-..+ Added: * border-style: double-dash: #= = = = # " Test " " Node " #= = = = # * edge style: double-dash: "= = = >" * Graph::Easy::Attributes and tests for it * Node: is_multicelled() * Graph: is_simple_graph(), debug() * A* implementation to find arbitrary paths (even with multi-celled nodes) 2005-07-13 v0.22 Tels 686 tests - Fix, fix, fix, fix the bugs... Fixed: * Edge/Parser: edge style '~~' (wave) * Layouter: assign layers, process nodes sorted by layers (fixes the '"[B] [A]->[B]" being laid out different from "[A]->[B]"' bug) * Edge.pm: remove needless new() * Makefile.PL check for As_svg was borked Added: * Node.pm: add columns(), rows(), grow(), connections() * Layout.pm: add _assign_layers() * Easy.pm: sorted_nodes() takes 1 or 2 optional field names to sort on * _near_places() moved into Graph::Easy::Node, make it multi-cell aware * started laying down the foundation for multi-celled nodes support in Path.pm/Scout.pm 2005-07-10 v0.21 Tels 605 tests - Fix, fix, fix, fix the bugs... * As_graphviz: default nodes to filled, fontsize 11 * As_graphviz: quote attribute values if nec. * As_graphviz: remap attribute names (background => fillcolor etc) * As_graphviz: output edge attributes * As_txt: use remap_attribute() * Easy: add remap_attributes(), as_svg_file(), as_ascii_file() * Easy: fix POD for add_node(), rem doc about add_vertex(), vertices() * Easy: add_node() returns newly added node and copes with ("name") 2005-06-25 v0.20 Tels 589 tests - Fix, fix, fix, fix the bugs... * put SVG code into a seperate package, see there for CHANGES * add some details to POD * border-style "dotdashed" renamed to "dot-dash" * added border and edge-styles: "dot-dot-dash", "wave" * make "border" set the attributes border-width, border-style, border-color * as_txt(): output "border" as it used to, even though only /border-(style|width|color)/ exist now * as_txt(): sort attributes by name on output to make it more predictable * as_ascii(): render border styles: double, wave, dot-dash, dot-dot-dash * as_ascii(): render edge styles: wave, dot-dash, dot-dot-dash * as_html(): fix overly big circles on labels with line breaks * as_html(): fix edge style on edges with labels * Easy: add_edge() returns the newly added edge * Parser: handle edge styles "~~" (wave), ".-" (dot-dash) and "..-" (dot-dot-dash) * Parser: recognize all official W3C color names from SVG spec as input * Parser: reject invalid colors and node shapes * Parser: allow 'Graph::Easy::Parser->from_file(...);' * Parser: improve POD * Node: streamline attribute() and title() 2005-06-12 v0.19 Tels 573 tests - Shape, shape, shape, shape the nodes... * moved manual and Pod2HTML to Graph::Easy::Manual to avoid dependency on Pod::Simple * Graph/Easy.pm: add seed(), randomize(), as_ascii_html() * Graph/Easy.pm: rem stray debug print in add_edge() * fix POD about limitations, multi-edges are possible since v0.18 * Parser: allow GLOB and similiar things as file to from_file() * Easy: add/del of clusters/groups invalidates the current layout * as_ascii(): fix output of nodes with "border: none;" * as_ascii(): implement border "dashed;", "dotdashed" and "bold" * Easy: add _framebuffer(): generate empty framebuffer for as_ascii() * Easy: make _framebuffer() twice as fast Renamed to Graph::Easy: 2005-06-11 v0.18 Tels 573 tests - Shape, shape, shape, shape the nodes... * renamed from Graph::Simple to Graph::Easy * small cleanups (bumping $VERSIONS, remove unnec. error() routines etc) * fix =begin graph/end graph in doc/manual/* * Graph::Easy::Pod2HTML and examples/pod2html support outputformat, especially "html" and "src,html" * fix Graph::Easy::Pod2HTML to work almost (the graphs still appear somewhat out-of-order in the output...) * forgot to include doc/manual/index.pod * add one more test for ASCII rendering of autosplit nodes (with hole) * Parser: do not create empty nodes on autosplit clusters * re-factor code to allow multi-edges (more than one edge from A to B) * add tests for multiedges * call "_correct_size_$format()" if possible * As_svg: add _correct_size_svg() for Nodes and Edge/Cells * As_svg: correctly center text for node labels * As_svg: draw hor/ver lines for edges * As_svg: draw arrow heads for edges * As_svg: include a comment with edge type and source/target node * As_svg: fix position/size for nodes with shape diamond/circle/ellipse * _prepare_layout() expands cells sizes to maximum column/row size * Node: add shape() 2005-05-29 v0.17 Tels 559 tests - Edge, edge, edge, edge towards the node... * rename examples/txt2ascii to examples/as_ascii * fix examples/as_ascii to actually work * add examples/as_html * amend INSTALL, README * add complete manual (doc/manual/*) and doc/gen_manual to generate HTML version of the manual * add Graph::Simple::Pod2HTML * include tests for different node shapes * suppress attribute "shape" in HTML output * as_html: for "shape: invisible", output empty table cell * as_html: for shape rounded and circle, use moz-rounded-border * as_html: treat "\n" in edge labels properly * as_svg: handle shape: invisible * as_svg: fix output for "shape: rounded;" * as_svg: fix general output to be valid * as_txt: output [ name ], not wrongly [ label ] * as_ascii: node size depends on label, not name * as_ascii: handle multiline labels correctly * as_ascii: avoid framebuffer underruns on certain layouts * added Graph::Simple::Cluster - list of relatively positioned nodes * Node : added add_to_cluster(), origin(), cluster(), relpos(), place(), dimensions() * Node : allow label => 'label' to be passed to new() * Node : move as_graphviz() to As_graphviz.pm * Node : _correct_w() to _correct_size() and pass it the format name (ascii, svg etc) * Simple: amended LIMITATIONS in pod * Simple: added output_format(), output(), cluster(), clusters(), add_clusters() * Parser: support clusters and autosplit nodes like "[ A | | B || C ]" * Parser: remove multiple spaces in node names to normalize them * Parser: fix parse_error() and add tests for that * Layout: make layouter more robust (better placement of nodes) * Layout: finally find paths with one bend properly * Layout: move pathfinding code to Graph::Simple::Layout::Scout * Layout: move path management code to Graph::Simple::Layout::Path * Layout: find path loops (from one node back to same node again) * Layout: find U-shaped paths * Edge : add styles for EDGE_x_y (x = N/S, y = E/W) 2005-05-17 v0.16 Tels 426 tests - Make, make, make, make the invisible nodes... * forgot to include As_svg.pm * Simple.pm: Note other formats in NAME and SYNOPSIS sections * As_svg: small cleanups 2005-05-16 v0.15 Tels 422 tests - Make, make, make, make the invisible nodes... * load the As_*() modules on-demand * fixed NAME in As_graphviz * add basic support for node shapes * Node: add del_attribute(), as_svg(), title() * Parser: check attributes more closely * Parser: support [] for anonymous, invisible nodes * added Graph::Simple::Node::Anon * Graph: move code to prepare layout from as_ascii() to _prepare_layout() since it can be reused by as_svg() * as_html: deal better with empty graphs * as_graphviz: default node shape is rectangular * Node: successor/predecessor did corrupt nodes w/o a graph * Node: add _reset_id() for testsuite * require Graph 0.65 2005-04-11 v0.14 Tels 354 tests - Lay, lay, lay, lay out the edges... * add experimental graphviz output * require Graph 0.61 2005-02-12 v0.13 Tels 345 tests - Group, group, group, group the nodes... * shorten CSS considerable (especially for graphs with groups) * fix CSS for Opera/Firefox for vertical edges * fix node-borders for Firefox/Opera by inserting dummy cells at each row 2005-02-07 v0.12 Tels 345 tests - Group, group, group, group the nodes... * register groups with the graph when adding them * Parser: parse attributes on groups like: "( Group [ Node ] ) { ... }" * Parser: parse attributes on edges like: "[ 1 ] -> { ... } [ 2 ]" * Group: handle attribute "nodeclass" - put nodes into that class * Node: handle attribute "group" - puts node into that group * Node: query "node.class" from graph before trying just "node" * Node: convert literal "\n" in label/name to "<br>" * Layout: padd around all cells with filler cells (to make group backgrounds really work) * Layout: cleanup on group output * as_txt(): output attributes of a group * add Graph::Simple::Node::Cell, Graph::Simple::Group::Cell * moved Graph::Simple::Path to Graph::Simple::Edge::Cell * Graph: shorten HTML output by combining table cells with colspan=X * Graph: use CSS child selectors to shorten classnames (and remove class='fill' or class='' from output) * Graph: as_html(): fix IDs on labels, lines etc * Graph: fix layout code for edges with labels: no longer needs position: relative, allows better copy&paste and is not garbled on scrolling * syntax.pl: support custom template name * simplifiy and generalize t/simple.t * always run test files under -w * don't use warnings (for older perls) * remove needless routines in subclasses of "Node" * use Test::Differences if t/ascii.t fails 2005-01-31 v0.11 Tels 294 tests - Close, close, close, close the ugly holes... * Parser: add parsing of groups like in "( Groupname [ Node ] )" * Parser: fix for "[1] -> [2] -> [3] { ... }" to not lose attributes on 3 * Parser: can now handle "[1], [2], [3] -> [4]" correctly * Parser: cut out rendundant parsing code * Graph: add group(), add_group(), del_group(), groups() * Graph: fix css(): 'link', 'label' and 'title' do not need to appear in CSS * Graph: suppress default attributes from groups, too * Graph: close "holes" in "arrows" (edges) with line-height & letter-spacing * Graph: inherit background from page, no border as default * Graph: as_html() - remove trailing empty tags for shorter output * Node: add group(), groups(), add_to_groups() * Node: rename as_txt_node() to as_pure_txt(), document it * Node: add label() and the handling of the label-attribute * Node: support "label" as property in autolink/autotitle * Node: as_txt(): no need to quote ":" and " " in attribute values * Node: as_html(): quote " " in URLs * Move as_txt() to As_txt.pm to reduce code size * Group: keep nodes indexed by their name * Group: fix as_txt() to quote group name, and include nodes * Group: fix: add_nodes() did include the group itself by accident * include examples/base.css for output of syntax.pl * fix examples/syntax.tpl to have a non-scrolling menu * add command-line dirnames to syntax.pl * include t/fun/ - fun with graph-examples * added Graph::Simple::Path and refactored Edge code - an edge now only knows as_txt(); The other two, as_html() and as_ascii() are handled by Path.pm (for each element of the edge seperately) * more syntax examples 2005-01-24 v0.10 Tels 248 tests - Color, color, color, color the pretty graph... * added more stress/syntax tests * fix parser to not treat colors in attributes as comments (like in "color: #808080") * use a pre-compiled regexp to match edges (cut down code-duplication) * parser now handles successfully node chains like: [ Ulm ] -> [ Lahn ] -> [ Bonn ] -> [ Trier ] [ Stein ] -> [ Wahns ] * Simple.pm: fix edges() in list context to actually work * require Graph v0.55 (to be safe) * Node: add attributes_as_txt(), as_txt_node() to allow $graph->as_txt() to output node attributes only once * Graph: as_txt(): output node attributes only once * Graph: add set_attribute() * examples/syntax.pl: generate headlines from comments in input, add footer * Graph: support attributes: linkbase, autolink, autotitle, link, title * Graph: as_html(): output title attributes, as well as links 2005-01-12 v0.09 Tels 147 tests - Place, place, place, place the pesky nodes... * Layout: try to place node with only one predecessor around the predecessor * add a stress test with a star-shaped graph (5 nodes) * add a syntax test with newlines and one without any spaces * Parser: accumulate left-over fragments to parse examples broken by newlines * Parser: use compiled regexps for nodes, attributes etc * Parser: finally handle trailing attributes on nodes * Node: add set_attributes() for setting multiple ones at once * Node: setting attribute "class" will set subclass instead * add tests for parsing: [ node ] { attribute: value; } [ node ] { attribute: value; } -> [ node2] { attribute: value } * fix edges going to the left * tests for quoting in node names 2005-01-10 v0.08 Tels 133 tests - Fix, fix, fix, fix the stupid bugs... * grapher tries to place nodes without any incoming edges into first column (this fixes all the examples which have groups of not-connected nodes) * in HTML, use monospaced font for edges (prepare for box-art) * Layout: set error if layout stage fails * Edge: add EDGE_N_E, EDGE_N_W, EDGE_N_E_W etc for corner pieces * Edge: add clear_cells() * make _trace_path() more general by having _trace_straight_path() creating the edge types * Node: as_html(): no longer outputs 'style="border: none"' for class="edge" * Node: include attributes in as_txt() as well as classes (for subclasses) * Node: add class() * Graph: always register nodes/edges with the graph * Graph: as_txt() includes attributes (unless they are the defaults) * Node: postpone calculation of 'w' (called by as_ascii() from Graph) * add a stress test with a bend edge 2005-01-09 v0.07 Tels 124 tests * Parser: ignore empty lines * Parser: unquote all special chars, not only the first * Node: quote node name in as_txt() * Parser: _parse_attributes(); * Graph: added attribute(), set_attributes() * Graph: added id() to set/get a unique ID per graph * Graph: default ID is '', CSS code contains ID in class names * Parser: teach it to parse CSS and call $graph->set_attributes() * Edge: output name in as_txt() * Layout: use strict, fix _trace_straight_path() * t/layout.t: add tests for _trace_straight_path() * Layout: simplify _trace_path() by using _trace_straight_path() * Layout: can trace straight edges occupying multiple cells 2005-01-08 v0.06 Tels 94 tests * fix version in Layout * Edge: add EDGE_START, EDGE_END, EDGE_HOR, EDGE_VER, EDGE_CROSS * Edge: add cell_type(), add_cell() * fix pod with "=end graph" (vs. "=end") sections * fix error message in t/ascii.t when reading file fails * fix parser: did not add edge if both nodes already existed * Layout: add _trace_straight_path() - trace a path horizontal/vertical * Layout: add _remove_path() - remove a traced path * tests: add t/layout.t, t/syntax/* * add examples/syntax.pl, examples/syntax.tpl to create automatic syntax test page for website * Parser: add reset() and call it from from_text() * Parser: handle quoted characters in node names * Parser: handle comments 2005-01-03 v0.05 Tels 87 tests * fix special paragraphs in pod to use =begin instead of =for * typos in pod * some more clarifications for LIMITATIONS * move the layout code to Graph::Simple::Layout * add a testcase with two not-connected node-pools in one graph * amend README, INSTALL 2005-01-03 v0.04 Tels 84 tests * change code to work with Graph 0.50 * require Graph 0.50 * added: Group.pm -- a group of nodes * added: examples/html.pl, examples/common.pl and TODO * tests: use strict;, add t/edge.t * Parser: document the different edge directions and styles * Parser: construct edges with the proper style * Parser: allow '..>' style * Node: + document successorts()/predecessors(), as_txt(), as_html() + implement as_html($tag) * Graph: + _trace_path() can also trace short, straight paths upwards + as_txt() uses $edge->as_txt() to render edge + as_ascii() generates proper output for edges with the right style + as_ascii() no longer generates trailing whitespace on lines + add css(), as_html_page(), html_page_footer(), html_page_header() + extend documentation, fix typos, add list of limitations Renamed to Graph::Simple: 2004-12-29 v0.03 Tels 52 tests * is a subclass of Graph::Directed, so add it as prereq again :o) * Graph::Layout::Simple::Parser - parse graph from text * Graph::Layout::Simple::Edge - an edge between two nodes * Graph: added node() - find node by name * Graph: add quite some more documentation * Graph, Node: added as_txt() * Node: added incoming(), and track incoming edges from other nodes * added t/parser.t - test text parser * added t/ascii.t - parse various text files to ascii 2004-12-28 v0.02 Tels 20 tests * added a stack-based, backtracking layout routine, that stuffs nodes and paths between them into cells * Simple.pm: as_ascii: deal with cell-layout and convert this to ASCII * Simple.pm: add doc stub for METHODS * Simple.pm: add score(), as_XXX() do layout if it hasn't done before * Node.pm: revised code, and better parameter handling * no longer needs Graph::Directed or Graph::Layout::Aesthetic as prereq * added examples/ascii.pl * forgot to add graph.t to MANIFEST 2004-12-27 v0.01 Tels 18 tests * original version; created by h2xs 1.23 with options -A -X -n Graph::Layout::Simple -b 5.6.1 --skip-autoloader ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������