1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
|
# Copyright (c) 1997-2024
# Ewgenij Gawrilow, Michael Joswig, and the polymake team
# Technische Universität Berlin, Germany
# https://polymake.org
#
# 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.
#-------------------------------------------------------------------------------
package Postscript::Gale;
use Math::Trig;
###########################################################################
# for both dimensions 1 and 2
my $common_procs = <<".";
% color x y ->
/Circle {
newpath
$point_radius 0 360 arc
dup 0 gt
% white
{
gsave 1 setgray fill grestore stroke
pop
} {
1 add 0.5 mul setgray % -1 => black, 0 => 50% grey
fill
0 setgray
} ifelse
} bind def
% [ colors ] x y ->
/Points {
gsave
translate
dup length 1 eq
{
0 get 0 0 Circle % lone circle
} {
dup length 360 exch div % angle between centers
exch { $point_radius 0 Circle dup rotate } forall
pop
} ifelse
grestore
} bind def
% string ->
/Label {
gsave
1 setgray 3.5 setlinewidth 1 setlinecap 1 setlinejoin
dup false charpath stroke
grestore show
} bind def
/min { 2 copy gt { exch } if pop } bind def
/max { 2 copy lt { exch } if pop } bind def
% x y string -> x' y'
/Loop {
3 dict begin
/label exch def /y exch def /x exch def
0 x y Circle
x $text_spacing sub y $point_radius add $text_spacing add moveto
label show
label stringwidth pop $point_radius 5 mul max x add y
end
} def
.
###########################################################################
# for dimension 1 only
my $dim1_procs = <<".";
% angle ->
/Arrow {
gsave
rotate
0 0 moveto
big_radius 0 lineto stroke
big_radius arrow_length sub arrow_radius sub 0 arrow_radius arrow_angle neg arrow_angle arc
big_radius 0 lineto
closepath fill
grestore
} def
% << x_out y_out x_in y_in >> label ->
/ArrowLabel {
exch begin
x_out x_in ge
{ % right side
dup stringwidth pop dup x_out add
% ... width x_right
big_radius le { x_out y_out moveto pop } { neg x_in add y_in moveto } ifelse
} { % left side
dup stringwidth pop neg x_out add
% ... x_left
dup big_radius ge { y_out moveto } { pop x_in y_in moveto } ifelse
} ifelse
show
end
} def
.
###########################################################################
sub loop_box_height() { 6*$point_radius + $fontsize + $text_spacing }
###########################################################################
use Polymake::Struct (
[ new => '$' ],
[ '@ISA' => 'Element' ],
[ '$locked' => '0' ],
[ '$Gale' => '#1' ],
'$big_radius',
);
sub new {
my $self=&_new;
my $G=$self->Gale;
my $dim=$G->dim;
$self->marginLeft=$self->marginRight=$self->marginBottom=2*$point_radius;
$self->marginTop=$self->marginBottom + $fontsize + $text_spacing;
$self->marginTop += loop_box_height + $Hmargin/2 if @{$G->loops};
if ($dim==1) {
$self->big_radius= min( $Wpaper-2*$Wmargin, $Hpaper-2*$Hmargin )/2;
$self->marginBottom += $Hmargin + 2*$self->big_radius;
}
# We sort the keys to have consistent output for all perl versions (especially >=5.18)
foreach my $p (values %{$G->different_x_y}) {
my ($x, $y)=@{$G->points->[$p->[0]]};
assign_min_max($self->minX, $self->maxX, $x);
assign_min_max($self->minY, $self->maxY, $y) if $dim==2;
}
$self->minY=$self->maxY=0 if $dim==1;
return $self;
}
###########################################################################
sub labels {
my $G=shift;
$G->VertexLabels
? (map { $G->VertexLabels->($_) } @_)
: ()
}
sub draw {
my ($self, $page)=@_;
my $G=$self->Gale;
my $dim=$G->dim;
my $code="";
if ($dim==2) {
# connect the pairs of vertices of the same color with dashed lines
$code .= "gsave [1 4] 0 setdash 0.5 setgray\n";
foreach my $bw ($G->whites, $G->blacks) {
# We sort the keys to have consistent output for all perl versions (especially >=5.18)
my @pts=map { scalar($page->transform(@$_)) } sort keys %$bw;
foreach my $i (0 .. $#pts-1) {
foreach my $j ($i+1 .. $#pts) {
$code .= "newpath $pts[$i] moveto $pts[$j] lineto stroke\n";
}
}
}
$code .= "grestore\n";
# for each facet find a triple of b/w points not belonging to it and connect them with a solid line
foreach my $line (@{$G->gale_lines}) {
$code .= draw_poly_line(map { scalar($page->transform(@{$G->points->[$_]})) } @$line) . "stroke\n";
}
} else { # dim==1
# draw a big circle
$code .= <<".";
gsave
left_edge right_edge add 2 div big_radius $Hmargin add translate
newpath 0 0 big_radius 0 360 arc stroke
.
# draw the arrows and the labels
# We sort the keys to have consistent output for all perl versions (especially >=5.18)
foreach my $angle (sort keys %{$G->different_angles}) {
my $pts = $G->different_angles->{$angle};
my ($sin, $cos) = (sin($angle), cos($angle));
# label box corner outside the big circle
my $x_out = ($self->big_radius + $text_spacing) * $cos;
my $y_out = ($self->big_radius + $text_spacing) * $sin;
# alternative label box corner inside the big circle (near the point of an arrow wing)
my $x_in_off = -$arrowheadlength/2;
my $y_in_off = $arrowheadwidth/2 + $text_spacing;
$y_in_off = -$y_in_off if $sin*$cos < 0;
my $x_in = ($self->big_radius + $x_in_off) * $cos - $y_in_off * $sin;
my $y_in = ($self->big_radius + $x_in_off) * $sin + $y_in_off * $cos;
if ($angle<0) {
$y_out -= $fontsize; $y_in -= $fontsize;
}
my $a=rad2deg($angle);
my $label = join(",", labels($G, @$pts));
$_=sprintf("%.3f",$_) for ($a, $x_out, $y_out, $x_in, $y_in);
$code .= <<".";
$a Arrow
<< /x_out $x_out /y_out $y_out /x_in $x_in /y_in $y_in >> ($label) ArrowLabel
.
}
# draw a single horizontal line above
$code .= <<".";
grestore
left_edge y1 moveto right_edge y1 lineto stroke
.
}
# draw the points as bunches of black and/or white circles
while (my ($v, $pts)=each %{$G->different_x_y}) {
my ($x,$y)=$page->transform(@$v);
my $xy= $dim==1 ? sprintf("%.3f y1",$x) : sprintf("%.3f %.3f",$x,$y);
$code .= "[ " . join(" ", @{$G->colors}[@$pts]) . " ] $xy Points\n";
my $x_off = -$text_spacing;
my $y_off = $point_radius + $text_spacing;
$x_off -= $point_radius if $#$pts>0;
$y_off += $point_radius if $#$pts>1;
my $label = join(",", labels($G, @$pts));
$code .= "$xy moveto $x_off $y_off rmoveto ($label) Label\n\n";
}
# draw the loop points in a separate box at the top of the page
if (@{$G->loops}) {
my $h_box = loop_box_height;
my $y_box = $page->canvas_height + $Hmargin/2;
my $x_loops = $page->marginLeft + 4*$point_radius;
my $y_loops = $y_box + 4*$point_radius;
$code .=
"$x_loops $y_loops\n" .
join("", map { "($_) Loop\n" } labels($G, @{$G->loops})) .
<<".";
pop $Wmargin sub $point_radius sub
$Wmargin exch $y_box exch $h_box rectstroke
.
}
$page->code .= $code;
if ($dim == 1) {
my $d=$arrowheadlength * $arrowheaddent;
$page->dict->{arrow_length}=$arrowheadlength * (1-$arrowheaddent);
$page->dict->{arrow_radius}=($arrowheadwidth**2 / (8 * $d)) + $d/2;
$page->dict->{arrow_angle}=rad2deg(atan($arrowheadwidth * $d / ($arrowheadwidth**2 /4 - $d**2)));
$page->dict->{big_radius}=$self->big_radius;
$page->dict->{left_edge}=$Wmargin;
$page->dict->{right_edge}=$Wpaper - $Wmargin;
$page->dict->{y1}=2*($Hmargin+$self->big_radius);
}
}
###########################################################################
package Postscript::Page;
sub addGale {
my ($self, $Gale)=@_;
$self->title ||= $Gale->Title;
push @{$self->elements}, new Postscript::Gale($Gale);
$self->procsets->{'Gale::common'}=$common_procs;
if ($Gale->dim == 1) {
$self->procsets->{'Gale::dim1'}=$dim1_procs;
}
}
1;
# Local Variables:
# c-basic-offset:3
# End:
|