#  Copyright (c) 1997-2007
#  Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
#  http://www.math.tu-berlin.de/polymake,  mailto:polymake@math.tu-berlin.de
#
#  This program is free software; you can redistribute it and/or modify it
#  under the terms of the GNU General Public License as published by the
#  Free Software Foundation; either version 2, or (at your option) any
#  later version: http://www.gnu.org/licenses/gpl.txt.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#-----------------------------------------------------------------------------
#  $Project: polymake $$Id: visual.rules 7578 2007-01-21 22:48:26Z gawrilow $

package Visual::Surface;

use Struct(
   [ '@ISA' => 'Visual::PointSet' ],
   '$Surface',
   [ '@Facets' => 'check_points(#%)', default => 'croak("Facets missing")' ],
   '@FacetNeighbors',
   [ '$FacetLabels' => 'unify_labels(#%)' ],
   [ '$FacetColor' => 'unify_decor(#%)', merge => '$this->merge_decor(#%)', default => '$Visual::Color::facets' ],
   [ '$FacetStyle' => 'unify_decor(#%)', merge => '$this->merge_decor(#%)' ],
   [ '$EdgeColor' => '$Visual::Color::edges' ],
   '$EdgeStyle',
   [ '$Closed' => 'undef' ],
);

sub new {
   my $self=&Visual::PointSet::new;
   $self;
}

declare %decorations=( %Visual::Polygon::decorations, FacetLabels => enum('hidden') );

# color of faces
custom $face_color = "200 200 0";

# color of vertices
custom $vertex_color = "200 0 0";

# thickness of arrows in morse matching
custom $morse_matching_arrow_size = 1;

# color of critical faces of morse matching
custom $critical_face_color = "100 0 255";

# size of critical vertices
custom $critical_vertex_size = 5;

#size of critical edges
custom $critical_edge_size = 4;

object Surface;

# category: Visualization
# Visualize the surface as given in @see GEOMETRIC_REALIZATION.
# return: Visual::Surface

user_method VISUAL(%Visual::Surface::decorations) {
  my ($this, $decor)=@_;
  visualize( new Visual::Surface(
				 Surface => $this,
				 Name => $this->name,
				 Vertices => [ convert_to_float($this->GEOMETRIC_REALIZATION) ] ,
				 VertexLabels => $this->lookup("VERTEX_LABELS"),
				 Facets => $this->FACETS_CYCLIC,
				 FacetColor => $Visual::Surface::face_color,
				 VertexColor=> $Visual::Surface::vertex_color,
				 $decor
				 ));
}

package Visual::Surface;

# Add the @see Surface.MORSE_MATCHING to the visualization of the Surface.
user_method MORSE_MATCHING(%Visual::Surface::decorations) {
  my ($this, $decor)=@_;
  my $surface = $this->Surface;
  my @hd = @{$surface->MORSE_MATCHING};
  my @vertices = convert_to_float($surface->GEOMETRIC_REALIZATION);
  my $dim = 0;
  my @dims = split(/\s+/,$hd[0]);
  shift @dims;
  shift @hd;
  my @graph;
  my $i = 0;
  my %arrow_colors;
  my @colors = ("200 0 0","100 0 255","0 0 255");
  my @barycenters;
  foreach my $line (@hd) {
    ++$dim if($i+1 >= $dims[$dim]);
    chomp $line;
    my ($face, $edges) = ($line =~ m/^<?\(\{(\d+(?:\s+\d+)*)\}\s+{((?:\(\d+\s+[01]\)\s*)+)?}\)$/);
    if($face eq "") { $barycenters[$i++]="0 0 0"; next; }
    my @face = split(/\s+/,$face);
    $barycenters[$i] = barycenter(@vertices[@face]);
    my @edges = ($edges =~ m/\(\d+\s+[01]\)/g);
    foreach my $edge(@edges) {
      my ($to_node, $matched) = ($edge =~ m/\((\d+)\s+([01])\)/);
      if($matched) {
	$graph[$i] = "{$to_node}";
	$arrow_colors{"$i $to_node"} = $colors[$dim];
	last;
      } else {
	$graph[$i] = "{}";
      }
    }
    ++$i;
  }
  $this->FacetStyle ||= "transparency 0.5";
  my $mmatching = new Visual::Graph(
				    Name => "MORSE_MATCHING of " . $surface->name,
				    Graph => \@graph,
				    Coord => \@barycenters,
				    Directed => 1,
				    NodeStyle => "hidden",
				    EdgeColor => \%arrow_colors,
				    EdgeStyle => "thickness $Visual::Surface::morse_matching_arrow_size",
				    $decor
				   );
  compose($this,$mmatching);
}

# Add faces with optional different graphical attributes
# args: "PROPERTY_NAME" or [ Faces ]
user_method FACES($ %Visual::Polygon::decorations) {
   my ($self, $faces, $facet_decor)=@_;
   if (!ref($faces)) {
      $faces=$self->Surface->give($faces);
   }

   my (%edge_decor, %point_decor);
   if (!exists $facet_decor->{FacetColor} && !exists $facet_decor->{FacetStyle}) {
      $facet_decor->{FacetColor}=$Visual::Surface::critical_face_color;
   }

   $edge_decor{EdgeColor}=delete $facet_decor->{EdgeColor} || $Visual::Surface::critical_face_color;
   $edge_decor{EdgeStyle}=delete $facet_decor->{EdgeStyle} || "thickness 6";

   $point_decor{VertexColor}=delete $facet_decor->{VertexColor} || $Visual::Surface::critical_face_color;
   $point_decor{VertexStyle}=delete $facet_decor->{VertexStyle} || "thickness 7";

   $edge_decor{VertexStyle}="hidden";
   $facet_decor->{EdgeStyle}="hidden";
   $facet_decor->{VertexStyle}="hidden";

   my $labels=$self->Surface->lookup("VERTEX_LABELS");

   compose($self, vis_faces($faces, $self->Surface, $labels, $facet_decor, \%edge_decor, \%point_decor));
}

sub vis_faces($$$$$$) {
   my ($facets, $surface, $vertex_labels, $face_decor, $edge_decor, $point_decor)=@_;

   my $coord = $surface->GEOMETRIC_REALIZATION;
   map {
      my @face = /\d+/g;
      my @params=( Name => $vertex_labels ? join(" ", @{subset($vertex_labels, @face)}) : "@face",
		   Vertices => subset($coord, @face),
		   VertexLabels => $vertex_labels ? subset($vertex_labels, @face) : \@face,
		 );
      if ($#face >= 2) {
	my $i = 0;
	while( !(${$surface->FACETS}[$i] eq "$_") ) {
	  ++$i;
	}
	my $cyclic_facet = ${$surface->FACETS_CYCLIC}[$i];
	my $i = 0;
	my %face_map;
	map { $face_map{$_} = $i++ } @face;
	$cyclic_facet = join(" ",map { $face_map{$_} } $cyclic_facet =~ m/\d+/g);
	new Visual::Polygon( @params, $face_decor, Facet => $cyclic_facet );
      } elsif ($#face == 1) {
	 #it's only an edge
	 new Visual::Graph( Graph => [ "{1}", "{0}" ], @params, $edge_decor );

      } else {
	 #it's a singular point
	 new Visual::PointSet( @params, $point_decor );
      }

   } @$facets;
}

sub barycenter(@) {
  no integer;
  my @vertices = @_;
  my @barycenter;
  foreach my $vertex (@vertices) {
    my @vertex = split(/\s+/,$vertex);
    for( my $j = 0; $j <= $#vertex; ++$j) {
      $barycenter[$j] += $vertex[$j];
    }
  }
  map { $_ /= (scalar @vertices) } @barycenter;
  return join(" ",@barycenter);
}

object Surface;

# category: Visualization
# Visualize the @see HASSE_DIAGRAM of a polyhedron as a multi-layer graph.
# option: seed => random seed value for the node placement
# return: Visual::SurfaceLattice

user_method VISUAL_FACE_LATTICE(%Visual::Lattice::decorations, { seed => undef }) {
   my ($this, $decor, $seed)=@_;
   my ($dims, @graph)=@{$this->HASSE_DIAGRAM};
   $graph[0] =~ s/^\s*<//;  pop @graph;
   my ($bottom_node, $top_node)= $graph[0] =~ /\Q({}\E/ ? (0, $#graph) : ($#graph, 0);

   my @vertex_labels = Visual::Lattice::prepare_vertex_labels($this, "VERTEX_LABELS");
   my @labels=("")x@graph;

   my $n=1;
   foreach (@graph[1..$#graph-1]) {	# unless top/bottom node
      s/\{(.*?)\}\s+//;  my $label=$1;
      if (@vertex_labels) {
	 $label =~ s/(\d+)/$vertex_labels[$1]/g;
      }
      $labels[$n]=$label;
      ++$n;
   }

   $labels[$top_node]=$labels[$bottom_node]=" ";
   $graph[$top_node]="";

   visualize( new Visual::SurfaceLattice(
      Name => "Face lattice of ".$this->name,
      Surface => $this,
      Graph => \@graph,
      top_node => $top_node,
      Directed => 1,
      ArrowStyle => 0,
      NodeLabels => \@labels,
      NodeColor => { $top_node => "0 0 0" },
      NodeStyle => sub { $_[0]==$top_node && "hidden" },
      EdgeStyle => sub { ($_[0]->incident_nodes)[1]==$top_node && "hidden" },
      Coord => hd_embedding($this, "HASSE_DIAGRAM", %$seed),
      $decor
   ));
}


package Visual::SurfaceLattice;

use Struct(
   [ '@ISA' => 'Visual::Lattice' ],
   '$Surface'
);

# Add the @see Surface.MORSE_MATCHING to the visualization of the face lattice of the surface.
user_method MORSE_MATCHING(%Visual::Lattice::decorations) {
   my ($this, $decor)=@_;
   $decor->{EdgeColor} ||= "255 0 0";
   $this->add_matching($this->Surface->MORSE_MATCHING, $decor);
   visualize($this);
}

# Add distinguished faces with different graphical attributes @c NodeColor and @c NodeStyle
# args: "PROPERTY_NAME" or [ Faces ]
user_method FACES($ %Visual::Lattice::decorations) {
   my ($self, $faces, $decor)=@_;
   if (!ref($faces)) {
      $faces=$self->Surface->give($faces);
   }
   $decor->{NodeColor} ||= $Visual::Surface::critical_face_color;
   $self->add_faces($self->Surface->HASSE_DIAGRAM, $faces, $decor);
   visualize($self);
}


# Local Variables:
# mode: perl
# c-basic-offset:3
# End:
