# Copyright (c) 1997-2006 # 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::Color; # special TOPAZ blue for an distinguished subcomplex custom $distinguished_subcomplex="147 178 255"; # special TOPAZ red for an distinguished set of faces custom $distinguished_face="231 0 74"; # arrows in MORSE_MATCHING custom $morse_matching_dim_1="255 211 27"; custom $morse_matching_dim_DIM="133 15 255"; # GRAPH in topaz: black custom $topaz_gr="0 0 0"; # GRAPH in mixed_graph : yellow custom $primal_gr="255 255 0"; # DUAL_GRAPH in mixed_graph: blue custom $dual_gr="0 0 255"; # MIXED_GRAPH: black custom $mixed_gr="0 0 0"; package default; custom %mixed_graph_params=( # edge_weight => 1 # increases the ideal length of the mixed edges ); sub vis_facets($$$$$$) { my ($facets, $coord, $vertex_labels, $face_decor, $edge_decor, $point_decor)=@_; $vertex_labels &&= split_labels($vertex_labels); 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 @twofaces = all_subsets_of_k(3,0..$#face); new Visual::Polygons( @params, $face_decor, map { new Visual::Polygon( Facet => "{@$_}" ) } @twofaces ); } 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; } object SimplicialComplex; # category: Visualization # The nodes of the mixed graph are the nodes of the primal @see GRAPH and # the @see DUAL_GRAPH. Additional to the primal and dual edges, there is # an edge between a primal and a dual node iff the primal node represents # a vertex of the corresponding facet of the dual node. property MIXED_GRAPH $type="graph"; $temporary=1; MIXED_GRAPH : FACETS, GRAPH, DUAL_GRAPH, DIM client("t_mixed_graph", $this ,map { ("-$_", $mixed_graph_params{$_}) } keys %mixed_graph_params); # category: Visualization # Visualizes the complex. # # If @see G_DIM < 4, the @see GRAPH and the facets # are visualized using the @see GEOMETRIC_REALIZATION. # # Otherwise, the spring embedder and the @see GRAPH are used to # produce coordinates for the visualization. # # If @see external.JavaView is used to visualize the complex, all faces of # one facet build a geometry in the jvx-file, so you may use # Method -> Effect -> Explode Group of Geometries in the # @see external.JavaView menu. # # option: mixed_graph => boolean # if @c true, use the @see MIXED_GRAPH for the spring embedder # option: seed => random seed value for the string embedder # return: Visual::SimplicialComplex user_method VISUAL(%Visual::Polygon::decorations, %Visual::Graph::decorations, { mixed_graph => 0 }, { seed => undef }) { my ($this, $face_decor, $graph_decor, $options, $seed)=@_; my $coord=do { if ( $options->{mixed_graph} ) { slice(spring_embedding_3d($this, "MIXED_GRAPH", "read-edge-weights", undef, %$seed), 0, $this->N_VERTICES-1); } elsif ($this->exists("GEOMETRIC_REALIZATION") && $this->G_DIM<=3) { [ convert_to_float($this->GEOMETRIC_REALIZATION) ] } else { spring_embedding_3d($this, "GRAPH", %$seed); } }; my $labels=$this->lookup("VERTEX_LABELS"); my $graph= new Visual::Graph( Name => 'GRAPH of '.$this->name, Graph => $this->GRAPH, Coord => $coord, VertexLabels => $labels, VertexColor => $Visual::Color::topaz_gr, $graph_decor ); visualize(new Visual::SimplicialComplex(Name => $this->name, SimplicialComplex=>$this, defaults=>$face_decor, vis_facets($this->FACETS, $coord, $labels, {}, {}, {}), $graph)); } # category: Visualization # Uses the spring embedder to visualize the @see GRAPH. # option: seed => random seed value for the string embedder # return: Visual::Graph user_method VISUAL_GRAPH(%Visual::Graph::decorations, { seed => undef }) { my ($this, $decor, $seed)=@_; visualize( new Visual::Graph( Name => 'GRAPH of '.$this->name, Graph => $this->GRAPH, Coord => spring_embedding_3d($this, "GRAPH", %$seed), NodeLabels => $this->lookup("VERTEX_LABELS"), NodeColor => $Visual::Color::topaz_gr, $decor )); } # category: Visualization # Uses the spring embedder to visualize the @see DUAL_GRAPH. # option: seed => random seed value for the string embedder # return: Visual::Graph user_method VISUAL_DUAL_GRAPH(%Visual::Graph::decorations, { seed => undef }) { my ($this, $decor, $seed)=@_; visualize( new Visual::Graph( Name => 'DUAL_GRAPH of '.$this->name, Graph => $this->DUAL_GRAPH, Coord => spring_embedding_3d($this, "DUAL_GRAPH", %$seed), NodeColor => $Visual::Color::topaz_gr, $decor )); } # category: Visualization # Uses the spring embedder to visualize the @see MIXED_GRAPH. # option: seed => random seed value for the string embedder user_method VISUAL_MIXED_GRAPH(%Visual::Graph::decorations, { seed => undef }) { my ($this, $decor, $seed)=@_; my $coord=spring_embedding_3d($this, "MIXED_GRAPH", "read-edge-weights", undef, %$seed); compose( $this->name, new Visual::Graph( Name => 'GRAPH of '.$this->name, Graph => $this->GRAPH, Coord => slice($coord,0,$this->N_VERTICES-1), NodeLabels => $this->lookup("VERTEX_LABELS"), NodeColor => $Visual::Color::primal_gr, EdgeColor => $Visual::Color::primal_gr, $decor ), new Visual::Graph( Name => 'DUAL_GRAPH of '.$this->name, Graph => $this->DUAL_GRAPH, Coord => slice($coord, $this->N_VERTICES, $this->N_VERTICES+$this->N_FACETS-1), NodeColor => $Visual::Color::dual_gr, EdgeColor => $Visual::Color::dual_gr, $decor ), new Visual::Graph( Name => 'MIXED_GRAPH of '.$this->name, Graph => $this->MIXED_GRAPH, Coord => $coord, NodeColor => $Visual::Color::mixed_gr, EdgeColor => $Visual::Color::mixed_gr, NodeStyle => "hidden", EdgeStyle => "thickness 0.1666667", $decor ) ); } package Visual::SimplicialComplex; use Struct( [ '@ISA' => 'Visual::Container' ], '$SimplicialComplex' ); # Add a subcomplex with optional different graphical attributes # args: "PROPERTY_NAME" or [ Facets ] user_method SUBCOMPLEX($ %Visual::Polygon::decorations, %Visual::Graph::decorations, %Visual::PointSet::decorations) { my ($self, $subcomplex, $facet_decor, $edge_decor, $point_decor)=@_; if (!ref($subcomplex)) { $subcomplex=$self->SimplicialComplex->give($subcomplex); } $facet_decor->{FacetColor} ||= $Visual::Color::distinguished_subcomplex; $edge_decor->{EdgeStyle} ||= "thickness 6"; $edge_decor->{EdgeColor} ||= $Visual::Color::distinguished_subcomplex; $point_decor->{VertexStyle} ||= "thickness 2"; $point_decor->{VertexColor} ||= $Visual::Color::distinguished_subcomplex; my $labels=$self->SimplicialComplex->lookup("VERTEX_LABELS"); compose($self, vis_facets($subcomplex, $self->elements->[-1]->Coord, $labels, $facet_decor, $edge_decor, $point_decor)); } # 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->SimplicialComplex->give($faces); } my (%edge_decor, %point_decor); if (!exists $facet_decor->{FacetColor} && !exists $facet_decor->{FacetStyle}) { $facet_decor->{FacetColor}=$Visual::Color::distinguished_face; } $edge_decor{EdgeColor}=delete $facet_decor->{EdgeColor} || $Visual::Color::distinguished_face; $edge_decor{EdgeStyle}=delete $facet_decor->{EdgeStyle} || "thickness 2"; $point_decor{VertexColor}=delete $facet_decor->{VertexColor} || $Visual::Color::distinguished_face; $point_decor{VertexStyle}=delete $facet_decor->{VertexStyle} || "thickness 2"; $edge_decor{VertexStyle}="hidden"; $facet_decor->{EdgeStyle}="hidden"; $facet_decor->{VertexStyle}="hidden"; my $labels=$self->SimplicialComplex->lookup("VERTEX_LABELS"); compose($self, vis_facets($faces, $self->elements->[-1]->Coord, $labels, $facet_decor, \%edge_decor, \%point_decor)); } # Add the @see SimplicialComplex.MORSE_MATCHING to the visualization of the SimplicialComplex. user_method MORSE_MATCHING(%Visual::Graph::decorations) { my ($this, $decor)=@_; my $complex = $this->SimplicialComplex; my @mm = @{$complex->MORSE_MATCHING}; my $complex_dim = $complex->DIM; my @vertices = @{$this->elements->[-1]->Coord}; my $dim = 0; my @dims = split(/\s+/,$mm[0]); shift @dims; shift @mm; my @graph; my $i = 0; my %arrow_colors; my @colors; my @rgb_1 = split(/\s+/,$Visual::Color::morse_matching_dim_1); my @rgb_DIM = split(/\s+/,$Visual::Color::morse_matching_dim_DIM); for (my $d=0; $d<$complex_dim; ++$d) { my @this_color; for (my $j=0; $j<3; ++$j) { $this_color[$j] = int( ($d*$rgb_1[$j]+($complex_dim-1-$d)*$rgb_DIM[$j])/($complex_dim-1) ); } push(@colors,join(" ",@this_color)); } my @barycenters; foreach my $line (@mm) { ++$dim if ($i+1 >= $dims[$dim]); my $barycenter; chomp $line; my ($face, $edges) = ($line =~ m/^defaults->{FacetStyle} ||= "transparency 0.5"; my $mmatching = new Visual::Graph( Name => "MORSE_MATCHING of " . $this->SimplicialComplex->name, Graph => \@graph, Coord => \@barycenters, Directed => 1, NodeStyle => "hidden", EdgeColor => \%arrow_colors, $decor ); compose($this,$mmatching); } 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 SimplicialComplex; # category: Visualization # Visualize the @see HASSE_DIAGRAM of a simplicial complex as a multi-layer graph. # option: seed => random seed value for the node placement # return: Visual::SimplicialComplexLattice user_method VISUAL_FACE_LATTICE(%Visual::Lattice::decorations, { seed => undef }) { my ($this, $decor, $seed)=@_; my ($dims, @graph)=@{$this->HASSE_DIAGRAM}; $graph[0] =~ s/^\s* "Face lattice of ".$this->name, SimplicialComplex => $this, Graph => \@graph, Directed => 1, top_node => $top_node, ArrowStyle => 0, Mode => "primal", NodeLabels => \@labels, NodeColor => "white", Coord => hd_embedding($this, "HASSE_DIAGRAM", %$seed), $decor ) -> merge( NodeStyle => { $top_node => "hidden" }, EdgeStyle => sub { ($_[0]->incident_nodes)[1]==$top_node && "hidden" } ); visualize($L); } package Visual::SimplicialComplexLattice; use Struct( [ '@ISA' => 'Visual::Lattice' ], '$SimplicialComplex' ); # Add the @see SimplicialComplex.MORSE_MATCHING to the visualization of the face lattice of the simplicial complex. # Decoration options @c EdgeColor and @c EdgeStyle apply to the matched edges only. user_method MORSE_MATCHING(%Visual::Lattice::decorations) { my ($self, $decor)=@_; $decor->{EdgeColor} ||= $Visual::Color::distinguished_face; $self->add_matching($self->SimplicialComplex->MORSE_MATCHING, $decor); visualize($self); } # Add a subcomplex with different graphical attributes # args: "PROPERTY_NAME" or [ Faces ] user_method SUBCOMPLEX($ %Visual::Lattice::decorations) { my ($self, $subcomplex, $decor)=@_; if (!ref($subcomplex)) { $subcomplex=$self->SimplicialComplex->give($subcomplex); } $decor->{NodeColor} ||= $Visual::Color::distinguished_subcomplex; $decor->{EdgeColor} ||= $decor->{NodeColor}; $self->add_subcomplex($self->SimplicialComplex->HASSE_DIAGRAM, $subcomplex, $decor); visualize($self); } # 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->SimplicialComplex->give($faces); } $decor->{NodeColor} ||= $Visual::Color::distinguished_face; $self->add_faces($self->SimplicialComplex->HASSE_DIAGRAM, $faces, $decor); visualize($self); } # Local Variables: # mode: perl # c-basic-offset:3 # End: