# 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::Polyhedron;
use Struct (
[ '@ISA' => 'Visual::PointSet' ],
[ '@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',
);
declare %decorations=( %Visual::Polygon::decorations, FacetLabels => enum('hidden') );
package Visual::Polytope;
use Struct (
[ '@ISA' => 'Visual::Container' ],
'$Polytope',
);
method basis_solid { $_[0]->elements->[0] }
##########################################################################################
#
# LP-related supplements
# Illustrate the behavior of a linear objective function on the polytope.
# Draw the facets contained in @see MAXIMAL_FACE and @see MINIMAL_FACE in distinct colors.
#
# option: min => minimal face color (default: yellow)
# option: max => maximal face color (default: red)
user_method MIN_MAX_FACE({ min => $Visual::Color::min, max => $Visual::Color::max }) {
my ($self, $decor)=@_;
my $lp=$self->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 (@{$self->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) {
$self->basis_solid->merge( FacetColor => sub { $_[0]==$min ? $decor->{min} :
$_[0]==$max ? $decor->{max} : undef } );
}
visualize($self);
}
# 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)
user_method VERTEX_COLORS {
my ($self)=@_;
my $lp=$self->Polytope;
$self->basis_solid->VertexColor=$lp->VERTEX_COLORS;
visualize($self);
}
# Illustrate the behavior of a linear objective function on the polytope.
# Superpose the drawing with the directed graph induced by the objective function.
user_method DIRECTED_GRAPH {
my ($self)=@_;
my $lp=$self->Polytope;
push @{$self->elements},
new Visual::Graph(
Name => "DIRECTED_GRAPH of " . $self->Polytope->name,
Graph => $lp->DIRECTED_GRAPH,
Coord => $self->basis_solid->Vertices,
NodeStyle => "hidden",
Directed => 1,
);
visualize($self);
}
##########################################################################################
#
# Triangulation as supplement
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}" ]); # -
# Add the triangulation to the drawing. The facets of the whole polytope become transparent.
#
# You may specify any triangulation of the current polytope.
# Per default, the @see Polytope.TRIANGULATION property is taken.
# (Currently there is only one possible alternative triangulation: @see TRIANGULATION_INT).
#
# Hint: Use the method Method -> Effect -> Explode Group of Geometries
# of @see external.JavaView for better insight in the internal structure.
user_method TRIANGULATION(;$="TRIANGULATION", %Visual::Polyhedron::decorations) {
my ($self, $TR, $decor)=@_;
my $d=$self->Polytope->AMBIENT_DIM;
if ($d > 3) {
die "don't know how to visualize the triangulation of a $d-d polytope\n";
}
my $skeleton=$self->basis_solid;
$skeleton->VertexStyle="hidden";
$skeleton->FacetStyle="hidden";
my ($Points, $Signs);
if ($TR eq "TRIANGULATION") {
$Points=$skeleton->Vertices;
$Signs=$self->Polytope->TRIANGULATION_SIGNS;
} elsif ($TR eq "TRIANGULATION_INT") {
$Points=[ $self->Polytope->dehomogenize($self->Polytope->POINTS) ];
$Signs=$self->Polytope->TRIANGULATION_INT_SIGNS;
} else {
die "Visualization of arbitrary triangulations is not yet implemented\n";
}
my @signs = map { ($_ < 0)+0 } split /\s+/, $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 @simplexes=map {
my @points = /\d+/g;
my ($name) = /(.*)$/;
my $s=shift @signs;
my @params=( Name => $self->Polytope->name."-$name",
Vertices => subset($Points, @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::Polyhedron(@params);
} @{$self->Polytope->$TR};
$self->Name="Triangulation of ".$self->Polytope->name;
push @{$self->elements}, \@simplexes;
visualize($self);
}
# Draw the edges of the @see Polytope.TRIANGULATION_BOUNDARY.
# The facets are made transparent.
user_method TRIANGULATION_BOUNDARY {
my ($self)=@_;
my $P=$self->basis_solid;
$P->FacetStyle="hidden";
$P->EdgeStyle="thickness 3";
$self->Name="Triangulation Boundary of ".$self->Polytope->name;
push @{$self->elements},
new Visual::Graph(
Name => $self->Polytope->name."-inner-edges",
Graph => $self->Polytope->TriangulationBoundaryInnerEdges($self->Polytope->GRAPH),
Coord => $P->Vertices,
NodeStyle => "hidden",
EdgeStyle => "thickness 1",
);
visualize($self);
}
##################################################################################################
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::Polyhedron::decorations) {
my ($this, $decor)=@_;
my $d=$this->AMBIENT_DIM;
if ($d>=2 && $d<=4) {
if (!$this->BOUNDED) {
die "polytope ", $this->name, " is not BOUNDED and therefore cannot be visualized\n";
}
if ($d==4) {
$this->SCHLEGEL($decor);
} else {
my $P= $d==2
? new Visual::Polygon(
Name => $this->name,
Vertices => [ $this->dehomogenize($this->VERTICES) ],
VertexLabels => $this->lookup("VERTEX_LABELS"),
Facet => $this->VIF_CYCLIC_NORMAL->[0],
$decor
)
: new Visual::Polyhedron(
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"),
Closed => 1,
$decor
);
visualize( new Visual::Polytope(Name => $this->name, Polytope => $this, $P));
}
} 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::Polyhedron
user_method VISUAL_DUAL(%Visual::Polyhedron::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";
}
my $P= $d==2
? new Visual::Polygon(
Name => "dual of ".$this->name,
Vertices => [ $this->dehomogenize($this->FACETS) ],
VertexLabels => $this->lookup("FACET_LABELS"),
Facet => $this->FTV_CYCLIC_NORMAL->[0],
$decor,
)
: new Visual::Polyhedron(
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"),
Closed => 1,
$decor,
);
visualize($P);
} else {
die "no visualization method known for the dual of a $d-dimensional polytope\n";
}
}
##########################################################################
#
# 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.
# @b Obsolete; the preferred procedure is to create a @see SimplicialComplex using
# the @see boundary_complex client of the application
# topaz
# and call its @see SimplicialComplex.VISUAL method.
# 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";
}
warn_print( <<'.' );
Method VISUAL_TRIANGULATION_BOUNDARY is obsolete.
The preferred procedure is to create a SimplicialComplex using
the boundary_complex client of the application topaz
and call its VISUAL method.
.
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: