#  Copyright (c) 1997-2004
#  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,v 1.15 2004/11/23 20:58:05 gawrilow Exp $


package Visual::Polytope;

use Struct(
   [ '@ISA' => 'Visual::PointSet' ],

   '$Polytope',
   [ '@Facets' => 'check_points( #% )', 'croak("Facets missing")' ],
   '@FacetNeighbors',
   [ '$FacetLabels' => 'unify_labels( #% )' ],
   [ '$FacetColor' => 'unify_decor( #% )', '$Visual::Color::facets' ],
   [ '$FacetStyle' => 'unify_decor( #% )' ],
   '$EdgeColor',
   '$EdgeStyle',
   [ '$Closed' => 'undef' ],
);

sub new {
   my $self=&Visual::PointSet::new;
   if (!defined $self->Closed) {
      $self->Closed= $self->Polytope->DIM==3;
   }
   $self;
}

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


# args: [ LPobject ]
# Illustrate the behavior of a linear objective function on the polytope.
# Draw the facets contained in @see MAXIMAL_FACE and @see MINIMAL_FACE in
# distinguished colors (by default, red and yellow).
#
# The optional parameter @a LPobject is introduced for the future use, when
# it will be possible to store several objective function in a @c Polytope object.

user_method MIN_MAX_FACE {
   my ($this, $lp)=@_;
   $lp ||= $this->Polytope;
   my ($max_filter, $min_filter)=map {
      my $re=$_ ? "\\s* (?:\\{\\s*)? " . join("\\s+", /\d+/g) . " \\s* (?:\\}\\s*)?" : "-";
      qr{ ^ $re $ }x
   } ($lp->MAXIMAL_FACE, $lp->MINIMAL_FACE);
   my ($min, $max)=(-1, -1);
   my $i=0;
   foreach my $facet (@{$this->Polytope->VERTICES_IN_FACETS}) {
      if ($max<0 && $facet =~ $max_filter) {
	 $max=$i;
	 last if $min>=0;
      } elsif ($min<0 && $facet =~ $min_filter) {
	 $min=$i;
	 last if $max>=0;
      }
      ++$i;
   }
   if ($min>=0 || $max>=0) {
      $this->FacetColor=sub { $_[0]==$min
			      ?  $Visual::Color::min :
			      $_[0]==$max
			      ?  $Visual::Color::max
			      :  $Visual::Color::facets
			};
   }
   visualize($this);
}


# args: [ LPobject ]
# Illustrate the behavior of a linear objective function on the polytope.
# Color the vertices according to the values of the objective function
# (@see Polytope.VERTEX_COLORS)
# 
# The optional parameter @a LPobject is introduced for the future use, when
# it will be possible to store several objective function in a @c Polytope object.

user_method VERTEX_COLORS {
   my ($this, $lp)=@_;
   $lp ||= $this->Polytope;
   $this->VertexColor=$lp->VERTEX_COLORS;
   visualize($this);
}

# args: [ LPobject ]
# Illustrate the behavior of a linear objective function on the polytope.
# Superpose the drawing with the directed graph induced by the objective function.
#
# The optional parameter @a LPobject is introduced for the future use, when
# it will be possible to store several objective function in a @c Polytope object.

user_method DIRECTED_GRAPH {
   my ($this, $lp)=@_;
   $lp ||= $this->Polytope;
   my $DG=new Visual::Graph(
      Name => "DIRECTED_GRAPH of " . $this->Name,
      Graph => $lp->DIRECTED_GRAPH,
      Coord => $this->Vertices,
      NodeStyle => "hidden",
      Directed => 1,
   );
   visualize($this, $DG);
}

# Draw the edges of the @see TRIANGULATION_BOUNDARY.
# The facets are made transparent.

user_method TRIANGULATION_BOUNDARY {
   my $this=shift;
   $this->FacetStyle="hidden";
   $this->EdgeStyle="thickness 3";
   my $P=$this->Polytope;
   visualize("Triangulation Boundary of ".$P->name, $this,
	     new Visual::Graph(
		Name => $this->Name."-inner-edges",
		Graph => $P->TriangulationBoundaryInnerEdges($P->GRAPH),
                Coord => $this->Vertices,
                NodeStyle => "hidden",
                EdgeStyle => "thickness 1",
	     ));
}
##################################################################################################

object Polytope;

# category: Visualization
# Visualize a polytope as a solid object (when 2-d or 3-d), or as a Schlegel diagram (4-d).
# return: Visual::Polytope

user_method VISUAL(%Visual::Polytope::decorations) {
   my ($this, $decor)=@_;
   my $d=$this->AMBIENT_DIM;
   if ($d<=3) {
      if (!$this->BOUNDED) {
	 die "polytope ", $this->name, " is not BOUNDED and therefore cannot be visualized\n";
      }
      visualize( new Visual::Polytope(
         Polytope => $this,
         Name => $this->name,
         Vertices => [ $this->dehomogenize($this->VERTICES) ],
         VertexLabels => $this->lookup("VERTEX_LABELS"),
         Facets => $this->VIF_CYCLIC_NORMAL,
         FacetNeighbors => $this->NEIGHBOR_FACETS_CYCLIC_NORMAL,
         FacetLabels => $this->lookup("FACET_LABELS"),
         $decor,
      ));
   } elsif ($d==4) {
      $this->SCHLEGEL($decor);
   } else {
      die "no visualization method known for $d-dimensional polytope\n";
   }
}

# category: Visualization
# Visualize the dual polytope as a solid 3-d object. The polytope must be @see BOUNDED and @see CENTERED.
#
# Returns: @c Visual::Polytope

user_method VISUAL_DUAL(%Visual::Polytope::decorations) {
   my ($this, $decor)=@_;
   my $d=$this->AMBIENT_DIM;
   if ($d<=3) {
      if (!$this->BOUNDED) {
	 die "polytope ", $this->name, " is not BOUNDED and therefore cannot be visualized\n";
      }
      if (! $this->CENTERED) {
	 die "polytope ", $this->name, " is not CENTERED, its dual cannot be visualized\n";
      }
      visualize( new Visual::Polytope(
         Polytope => $this,
         Name => "dual of ".$this->name,
         Vertices => [ $this->dehomogenize($this->FACETS) ],
         VertexLabels => $this->lookup("FACET_LABELS"),
         Facets => $this->FTV_CYCLIC_NORMAL,
         FacetNeighbors => $this->NEIGHBOR_VERTICES_CYCLIC_NORMAL,
         FacetLabels => $this->lookup("VERTEX_LABELS"),
	 $decor,
      ));

   } else {
      die "no visualization method known for the dual of a $d-dimensional polytope\n";
   }
}

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

my @simplex_faces_3d=([ "{0 2 1}", "{0 1 3}", "{0 3 2}", "{1 2 3}" ],	# +
		      [ "{0 1 2}", "{0 3 1}", "{0 2 3}", "{1 3 2}" ]);	# -
my @simplex_faces_2d=([ "{0 1 2}" ],	# +
		      [ "{0 2 1}" ]);	# -
my @simplex_neighbor_faces=([ "{1 2 3}", "{2 0 3}", "{0 1 3}", "{1 0 2}" ],	# +
			    [ "{2 1 3}", "{0 2 3}", "{1 0 3}", "{0 1 2}" ]);	# -

# category: Visualization
# Visualize the @see TRIANGULATION of a 3-dimensional polytope.
#
# <b>Hint:</b> Use the method <i>Method -> Effect -> Explode Group of Geometries</i>
# of @see external.JavaView for better insight in the internal structure.
#
# return: Visual::Polytope

user_method VISUAL_TRIANGULATION(%Visual::Polytope::decorations) {
   my ($this, $decor)=@_;
   my $d=$this->AMBIENT_DIM;
   if ($d > 3) {
      die "don't know how to visualize the triangulation of a $d-d polytope\n";
   }
   my $skeleton=$this->VISUAL( VertexStyle => "hidden",
			       FacetStyle => "hidden",
			     );

   my @signs = map { ($_ < 0)+0 } split /\s+/, $this->TRIANGULATION_SIGNS;	# 0: positive, 1: negative
   my $vertex_labels= delete $decor->{VertexLabels} || $skeleton->VertexLabels;
   undef $vertex_labels if !is_array($vertex_labels);
   my $simplex_faces=  $d==3 ? \@simplex_faces_3d : \@simplex_faces_2d;

   my @simplices=map {
      my @points = /\d+/g;
      my ($name) = /(.*)$/;
      my $s=shift @signs;
      my @params=( Name => $this->name."-$name",
		   Polytope => $this,
		   Vertices => subset($skeleton->Vertices, @points),
		   Facets => $simplex_faces->[$s],
		   VertexLabels => ref($vertex_labels) ? subset($vertex_labels, @points) : $vertex_labels || \@points,
		   $decor
		 );
      if ($d==3) {
	 push @params, FacetNeighbors => $simplex_neighbor_faces[$s];
      }
      new Visual::Polytope(@params);

   } @{$this->TRIANGULATION};

   visualize("Triangulation of ".$this->name, $skeleton, \@simplices);
}

##########################################################################
#
#  An awful hack until C++ data structures are available directly
#
package Graph_with_lookup;
sub has_edge {
   my ($self, $from, $to)=@_;
   $$self->[$from] =~ /\b$to\b/;
}
sub get_number_nodes {
   my $self=shift;
   return scalar(@$$self);
}

object Polytope;

# The input Graph describes the edges which are to exclude from the result
method TriangulationBoundaryInnerEdges {
   my ($this, $Graph)=@_;
   $Graph=bless \($_[1]), "Graph_with_lookup" unless is_object($Graph);
   my @inner_edges_graph=map { [ ] } 0..$Graph->get_number_nodes-1;
   foreach (@{$this->TRIANGULATION_BOUNDARY}) {
      foreach my $simplex (/\{ [^{}]+ \}/xg) {
	 foreach my $edge (all_subsets_of_k(2, $simplex =~ /\d+/g)) {
	    if (! $Graph->has_edge(@$edge)) {
	       push @{$inner_edges_graph[$edge->[0]]}, $edge->[1];
	       push @{$inner_edges_graph[$edge->[1]]}, $edge->[0];
	    }
	 }
      }
   }
   $_=[ num_sorted_uniq( sort { $a <=> $b } @$_ ) ] for @inner_edges_graph;
   return \@inner_edges_graph;
}

# category: Visualization
# Visualize the @see TRIANGULATION_BOUNDARY of the polytope.
# return: Visual::Polygons

user_method VISUAL_TRIANGULATION_BOUNDARY(%Visual::Polygon::decorations) {
   my ($this, $decor)=@_;
   if ($this->AMBIENT_DIM != 3) {
      die "don't know how to visualize the triangulation boundary of a ", $this->AMBIENT_DIM, "-d polytope\n";
   }
   visualize( new Visual::Polygons(
      Name => "Triangulation Boundary of ".$this->name,
      Vertices => [ $this->dehomogenize($this->VERTICES) ],
      VertexLabels => $this->lookup("VERTEX_LABELS"),
      $decor,
      map { map { new Visual::Polygon( Facet => $_ ) } /\{ [^{}]+ \}/xg } @{$this->TRIANGULATION_BOUNDARY}
   ));
}


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