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