#############################################################################
# output the graph as Scalable Vector Graphics
#
# (c) by Tels 2004-2005.
#############################################################################

package Graph::Simple::As_svg;

use vars qw/$VERSION/;

$VERSION = '0.01';

#############################################################################
#############################################################################

package Graph::Simple;

sub EM () { 14; }

use strict;

sub _as_svg
  {
  my ($self, $options) = @_;

  # convert the graph to a textual representation

  $self->layout() unless defined $self->{score};

  my ($rows,$cols,$max_x,$max_y,$cells) = $self->_prepare_layout('svg');

  my $txt;

  if ($options->{standalone})
    {
    $txt .= <<EOSVG
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
        "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">

EOSVG
;
    }

  my $mx = $max_x * EM + EM * 2;
  my $my = $max_y * EM * 2 + EM * 2;

  $txt .= 
     "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"$mx\" height=\"$my\" x=\"0\" y=\"0\">"
    ."\n<!-- Generated by Graph::Simple $Graph::Simple::VERSION" .
	    " at " . scalar localtime() . " -->\n";

  # generate the class attributes first
  # XXX TODO
  ## 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++;
  #  }

  foreach my $n (@$cells)
    {
    # exclude filler cells
    if ($n->{w} != 0 && $n->{h} != 0)
      {
      # get position from cell
      my $x = $cols->{ $n->{x} };
      my $y = $rows->{ $n->{y} };
#      print STDERR "# $n->{x} => $x, $n->{y} => $y\n";

      $txt .= $n->as_svg($x,$y) . "\n";	# output node
      }
    }

  $txt . "</svg>";			# finish
  }
 
1;

=pod

=head1 NAME

Graph::Simple::As_svg - Output graph as Scalable Vector Graphics (SVG)

=head1 SYNOPSIS

	use Graph::Simple;
	
	my $graph = Graph::Simple->new();

	my $bonn = Graph::Simple::Node->new(
		name => 'Bonn',
	);
	my $berlin = Graph::Simple::Node->new(
		name => 'Berlin',
	);

	$graph->add_edge ($bonn, $berlin);

	print $graph->as_svg();

	# prints something like:


=head1 DESCRIPTION

C<Graph::Simple::As_svg> contains just the code for converting a
L<Graph::Simple|Graph::Simple> object to a SVG text.

=head1 EXPORT

Exports nothing.

=head1 SEE ALSO

L<Graph::Simple>.

=head1 AUTHOR

Copyright (C) 2004 - 2005 by Tels L<http://bloodgate.com>

See the LICENSE file for information.

=cut

package Graph::Simple::Node;

sub as_svg
  {
  my ($self,$x,$y) = @_;

  my $name = $self->{att}->{label}; $name = $self->{name} unless defined $name;

  my $em = Graph::Simple::EM;		# multiplication factor chars * em = units (pixels)

  # the attributes of the element we will finally output
  my $att = $self->_svg_attributes($em,$x,$y);
  
  my $shape = $att->{shape};				# rect, circle etc
  delete $att->{shape};

  # set a potential title
  my $title = $self->title();
  $att->{title} = $title if $title ne '';

  my $att_line = '';				# attributes as text (cur line)
  my $att_txt = '';				# attributes as text (all)
  foreach my $e (sort keys %$att)
    {
    $att_line .= " $e=\"$att->{$e}\"";
    if (length($att_line) > 75)
      {
      $att_txt .= "$att_line\n"; $att_line = '';
      }
    }
  $att_txt .= "$att_line";
  $att_txt =~ s/\n\z//;		# avoid a "  >" on last line
 
  my $elem = $name;
  if ($self->isa('Graph::Simple::Edge'))
    {
    $elem = "edge";
    }

  my $svg = "<!-- $elem -->\n";
  
  $svg .= "<$shape$att_txt \/>\n" unless $shape eq 'invisible'; 

  ###########################################################################
  # include the label/name/text
 
  my $label = $self->label();
  my $xt = $x * $em + length($label) * $em / 2;
  my $yt = $y * $em + $em * 2;
  my $color = $att->{color} || 'black';

  $svg .= "<text x=\"$xt\" y=\"$yt\" style=\"font-size:$em\" fill=\"$color\">$label</text>\n\n";

  $svg;
  }

sub _svg_attributes
  {
  # Return a hash with attributes for the node, like "x => 1, y => 1, w => 1, h => 1"
  # Especially usefull for shapes other than boxes.

  my ($self,$em,$x,$y) = @_;

  my $att = {};

  my $shape = $self->attribute('shape') || 'rect';
  $shape =~ s/^(rectangle|box)\z/rect/;			# rectangle, box => rect

  if ($shape eq 'circle')
    {
    $att->{cx} = $x * $em;
    $att->{cy} = $y * $em;
    $att->{r} = $self->{w} > $self->{h} ? $self->{w} : $self->{h};
    $att->{r} /= 2;
    $att->{r} *= $em;
    }
  elsif ($shape eq 'ellipse')
    {
    $att->{cx} = $x * $em;
    $att->{cy} = $y * $em;
    $att->{rx} = $self->{w} * $em / 2;
    $att->{ry} = $self->{h} * $em / 2;
    }
  else
    {
    if ($shape eq 'rounded')
      {
      # round corners by 10%
      $att->{ry} = '10%';
      $att->{rx} = '10%';
      $shape = 'rect';
      }
    # XXX TODO: other shapes than rectangles like polygon etc
    $att->{x} = $x * $em;
    $att->{y} = $y * $em;
    $att->{width} = $self->{w} * $em;
    $att->{height} = $self->{h} * $em;
    }
  $att->{shape} = $shape;

  my $border = $self->attribute('border') || '';
  $border =~ s/(solid|dotted|dashed)//g;
  my $border_style = $1 || 'solid';
  $border =~ s/^([\d\.]+(px|em|%))//g;
  $att->{"stroke-width"} = $1 || '1px';
  $border =~ s/\s//g;
  $border = $self->attribute('border-color') || '' if ($border eq '');
  $att->{stroke} = $border if $border ne '';
  
  my $background = $self->attribute('background') || '';
  $att->{fill} = $background if $background ne '';

  $att;
  }

# XXX TODO:
# use <line ...> for Graph::Simple::Edge
