Graph-Easy

 view release on metacpan or  search on metacpan

lib/Graph/Easy.pm  view on Meta::CPAN

  # 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 careful 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

lib/Graph/Easy.pm  view on Meta::CPAN

  '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;



( run in 1.055 second using v1.01-cache-2.11-cpan-39bf76dae61 )