1 2# $Id$ 3eval "exec perl -Ss $0 $@" 4 if 0; 5 6# A graphing preprocessor for GNU pic / troff package. 7# Hacked into existence by Larry McVoy (lm@sun.com now lm@sgi.com). 8# Copyright (c) 1994 Larry McVoy. GPLed software. 9# 10# Input format is like that of Xgraph, i.e., sets of X Y pairs, 11# divided up by blank lines and titled with a "title. Like so 12# 13# 1 1 14# 2 2 15# "straight slope 16# 17# 4 4 18# 1 4 19# "straight down 20# 21# Optional "quartile" data input format. 22# The drawing is ----- o ---, with the lines being from y1..y2, y4..y5, 23# and the mark at y3. 24# 25# x y1 y2 y3 y4 y5 26# x y1 y2 y3 y4 y5 27# x y1 y2 y3 y4 y5 28# 29# Optional input (superset of Xgraph) is like so: 30# 31# %T Graph title in +4 point font 32# %X X axis title and/or units in +2 point font 33# %Y Y axis title and/or units in +2 point font 34# %P Page title in +4 point font 35# %fakemax-X <value> force graph to be that big 36# %fakemax-Y <value> force graph to be that big 37# %fakemin-X <value> force graph to be that big 38# %fakemin-Y <value> force graph to be that big 39# 40# Options: 41# -lm implies -big -below -grid -close 42# -rev reverse X/Y data sense (and titles) 43# -below put data set titles below the graph rather than to the right 44# -close no extra space around the data 45# -qline connect the quartile center points 46# -grid grid :-) 47# -halfgrid Grid lines where the major ticks are 48# -nobox no box around whole graph 49# -big make the graph take the whole page 50# -slide make the graph fit in my slides 51# -small make the graph be small so you can do a lot of them. 52# -notitle no Title label 53# -nolabels no X/Y/Title labels 54# -nodatal no dataset labels 55# -nomarks no marks on the graphs. 56# -nolines no lines connecting the marks (don't use w/ -nomarks :-) 57# -k print (absolute) values larger than 1000 as (value/1000)K 58# -grapheach graph each data set separately 59# -br_title start a new graph at each title. 60# -nospace no .sp at top of picture 61# -ts time series, X axis is implied. 62# -hist produce a histogram graph 63# 64# Hacks :-) 65# -xk multiply X input by 1024. 66# -xm multiply X input by 1024*1024. 67# -logx take the log base 2 of X input 68# -logy take the log base 2 of Y input 69# -cut add cut marks so that image croppers dont crop too close 70# 71# Much thanks to James Clark for providing such a nice replacement for 72# the Unix troff package. Thanks to the Xgraph folks for providing 73# inspiration. Thanks to Declan Murphy for math :-) 74# Thanks to noone for floating point numbers, they suck dog doo. 75# There are lots of hacks in here to deal with rounding errors. 76# 77# TODO: 78# All of the option parsing done manually. 79# A filter option to print ranges of the data? 80# A way to do each data set in it's own graph. 81# All of the other xgraph options? 82# For Adam, that butthead, an option to sort the labels such that they 83# are in the same order as the right endpoints of the data sets. 84 85&init; 86&autosize; 87&pic; 88exit; 89 90# init - slurp in the data and apply any transformations. 91sub init 92{ 93 # Lint for the options. 94 $qline = $ts = $close = $nolines = $thk1 = $thk2 = $k = $notitle 95 = $thk1_5 = $xm = $grid = $nospace = $lm = $hist = 0 if 0; 96 97 if ($grapheach) { $grapheach = 1; $cut = 0; } else { $grapheach = 0; } 98 if ($halfgrid) { $halfgrid = 1; } else { $halfgrid = 0; } 99 if ($hist) { $nobox = 1; $nolabels = 1; $close = 1; $nolines = 1; } 100 if ($lm) { $big = $below = $grid = $close = 1; } 101 102 # Accept %options=value on the command line. 103 while ($ARGV[0] =~ /^%/) { 104 $_ = $ARGV[0]; 105 s/=/ /; 106 push(@lines, "$_\n"); 107 shift(@ARGV); 108 } 109 110 # OK, sometimes we get 111 # %T title 112 # %X X axis, etc. 113 # 114 # "data set 1 115 # 116 # And this messes up the numbering later on. So we carefully dump the 117 # whitespace between the control and data. 118 while (<>) { 119 last if /^\s*$/; 120 push(@lines, $_); 121 last if /^"/; 122 last if /^\d/; 123 } 124 push(@lines, <>); 125 $fake = ""; 126 $items = 0; 127 $stat_sum = 0; 128 $min = 1.7E+308; 129 $max = 2.2E-308; 130 foreach (@lines) { 131 if (/^"?%fake/) { 132 $fake = $_; 133 s/"?%fakemax-//; 134 s/"?%fakemin-//; 135 @_ = split; 136 $_ = "$_[1] $_[1]"; 137 } elsif (/^%hist\s/) { 138 @_ = split; 139 shift(@_); 140 ($hist_bsize, $hist_low, $hist_high) = @_; 141 next; 142 } else { 143 next if /^\s*["%#]/; 144 next if /^\s*$/; 145 } 146 if ($ts) { 147 $_ = "$items $_"; 148 } 149 $items++; 150 @_ = split; 151 if ($xk) { 152 $_[0] = $_[0] * 1024; 153 } elsif ($xm) { 154 $_[0] = $_[0] * 1024 * 1024; 155 } 156 if ($logx) { 157 $_[0] = &logbase(2, $_[0]); 158 } 159 if ($yk) { 160 $_[1] = $_[1] * 1024; 161 } elsif ($ym) { 162 $_[1] = $_[1] * 1024 * 1024; 163 } 164 if ($logy) { 165 $_[1] = &logbase(2, $_[1]); 166 } 167 if ($rev) { 168 $_ = "$_[1] $_[0]"; 169 $y = $_[0]; 170 } else { 171 $_ = "$_[0] $_[1]"; 172 $y = $_[1]; 173 } 174 $stat_sum += $y; 175 $max = $y if ($y > $max); 176 $min = $y if ($y < $min); 177 push(@y, $y); 178 if ($fake =~ /[XY]/) { 179 # XXX - reverse? What should it do? 180 if ($fake =~ /fakemax-X/) { 181 $fakemax_X = $_[0]; 182 } elsif ($fake =~ /fakemax-Y/) { 183 $fakemax_Y = $_[1]; 184 } elsif ($fake =~ /fakemin-X/) { 185 $fakemin_X = $_[0]; 186 } elsif ($fake =~ /fakemin-Y/) { 187 $fakemin_Y = $_[1]; 188 } 189 $_ = $fake; 190 $fake = ""; 191 } 192 } 193 194 # Do some statistics. 195 @s = sort(@y); 196 if ($items & 1) { 197 $stat_median = $s[($items + 1)/2]; 198 } else { 199 $i = $items / 2; 200 $stat_median = ($s[$i] + $s[$i+1]) / 2; 201 } 202 $stat_avg = $stat_sum/$items; 203 $stat_avgdev = $stat_var = 0; 204 # $stat_skew = $stat_curt = 0; 205 foreach $_ (@lines) { 206 next if /^\s*["#%]/; 207 next if /^\s*$/; 208 @_ = split; 209 $stat_var += ($_[1] - $stat_median) ** 2; 210 $tmp = $_[1] - $stat_median; 211 $stat_avgdev += $tmp > 0 ? $tmp : -$tmp; 212 } 213 $stat_var /= $items - 1; 214 $stat_stddev = sqrt($stat_var); 215 $stat_avgdev /= $items; 216 if ($ts) { 217 printf STDERR "N=$items min=$min max=$max med=%.2f avg=%.2f stddev=%.2f avgdev=%.2f\n", 218 $stat_median, $stat_avg, $stat_stddev, $stat_avgdev; 219 } 220 221 # Diddle this to create different marks. 222 @marks = ( 223 '[ "\s+2\(bu\s0" ]', 224 '[ "\(sq" ]', 225 '[ "\(*D" ]', 226 '[ "\s+2\(pl\s0" ]', 227 '[ "\(*F" ]', 228 '[ "\s+2\fB\(mu\fP\s0" ]', 229 '[ circle rad .035 fill 0 ]', 230 '[ box ht .07 wid .07 fill 1 ]', 231 '[ "\(dd" ]', 232 ); 233 $nmarks = $#marks + 1; 234 $nomark = '[ box invis ht .05 wid .05 ]'; 235 236 $first_title = 1; 237 238 if ($nospace) { 239 $graphspace = "0"; 240 } elsif ($small) { 241 $graphspace = ".15i"; 242 } elsif ($medium) { 243 $graphspace = ".20i"; 244 } else { 245 $graphspace = ".25i"; 246 } 247 248 if ($small) { 249 $marks[0] = '[ circle rad .007 fill 1 ]'; 250 $PS = 10; 251 $ft = "B"; 252 $tick = .1; 253 } elsif ($medium) { 254 $PS = 11; 255 $ft = "HB"; 256 $tick = .1; 257 } elsif ($slide) { 258 $ft = "HB"; 259 $PS = 11; 260 $tick = .15; 261 } else { 262 $ft = "CB"; 263 $PS = 12; 264 $tick = .15; 265 } 266 $thk = .75; 267 $thk = 1 if $thk1; 268 $thk = 1.5 if $thk1_5; 269 $thk = 2 if $thk2; 270 $thk = .2 if $thk_2; 271 $gthk = .25; 272 $gthk = 1 if $gthk1; 273 $gthk = .75 if $gthk_75; 274 $gthk = .5 if $gthk_5; 275 $lineinvis = $nolines ? "invis" : ""; 276} 277 278# Calculate min/max to autosize the graph. 279sub autosize 280{ 281 foreach $_ (@lines) { 282 next if /^\s*["#%]/; 283 next if /^\s*$/; 284 @_ = split; 285 if ($#_ == 1) { 286 $Ymax = $Ymin = $_[1]; 287 } elsif ($#_ == 5) { # Quartile plot 288 $Ymax = $Ymin = $_[1]; 289 for ($i = 2; $i <= 5; ++$i) { 290 $Ymax = $_[$i] if ($Ymax < $_[$i]); 291 $Ymin = $_[$i] if ($Ymin > $_[$i]); 292 } 293 } else { 294 die "Data format error: $_\n"; 295 } 296 if (!defined $xmin) { 297 $xmin = $_[0]; 298 $xmax = $_[0]; 299 $ymin = $Ymin; 300 $ymax = $Ymax; 301 } 302 else { 303 $xmin = $_[0] if ($xmin > $_[0]); 304 $xmax = $_[0] if ($xmax < $_[0]); 305 $ymin = $Ymin if ($ymin > $Ymin); 306 $ymax = $Ymax if ($ymax < $Ymax); 307 } 308 } 309 310 # Handle fake max 311 if (defined($fakemax_X) && $fakemax_X > $xmax) { 312 $xmax = $fakemax_X; 313 } 314 if (defined($fakemax_Y) && $fakemax_Y > $ymax) { 315 $ymax = $fakemax_Y; 316 } 317 if (defined($fakemin_X) && $fakemin_X < $xmin) { 318 $xmin = $fakemin_X; 319 } 320 if (defined($fakemin_Y) && $fakemin_Y < $ymin) { 321 $ymin = $fakemin_Y; 322 } 323 if ($hist) { 324 $xmax += $hist_bsize; 325 } 326 warn "n=$items xmin=$xmin xmax=$xmax ymin=$ymin ymax=$ymax\n" if $debug; 327 ($xlower, $xupper, $xtick) = &tick($xmin, $xmax, $logx ? 2 : 10); 328 ($ylower, $yupper, $ytick) = &tick($ymin, $ymax, $logy ? 2 : 10); 329 if ($ymax + $ytick*.45 < $yupper) { 330 $yupper -= $ytick; 331 $ypartial = $ymax - $yupper; 332 } else { 333 $ypartial = 0; 334 } 335 $xn = int(.9 + ($xupper - $xlower) / $xtick); 336 $yn = int(.9 + ($yupper - $ylower) / $ytick); 337 $xlower = sprintf("%.6f", $xlower); # really ugly cast 338 $xupper = sprintf("%.6f", $xupper); # really ugly cast 339 $xtick = sprintf("%.6f", $xtick); # really ugly cast 340 $xn = sprintf("%.0f", $xn); # really ugly cast 341 $ylower = sprintf("%.6f", $ylower); # really ugly cast 342 $yupper = sprintf("%.6f", $yupper); # really ugly cast 343 $ytick = sprintf("%.6f", $ytick); # really ugly cast 344 $yn = sprintf("%.0f", $yn); # really ugly cast 345} 346 347# Since I had to go rethink it, here's the explanation: 348# 349# log base e 10 = X implies e**x = 10 350# e ** (v * x) = (e ** x) ** v 351# since e ** x == 10, that implies e ** (v * x) is 10 ** v 352# Capeesh? 353sub expbase 354{ 355 local($base, $val) = @_; 356 357 exp($val * log($base)); 358} 359 360sub logbase 361{ 362 local($base, $val) = @_; 363 364 if ($val == 0) { 365 return 0; 366 } 367 if ($val < 0) { 368 die "Input: $_: can't take log of negative value: $val\n"; 369 } 370 log($val) / log($base); 371} 372 373# Figure out the tick marks. 374# XXX - the log stuff is not quite right. 375sub tick 376{ 377 local($min, $max, $base) = @_; 378 local($delta, $adj, $lower, $upper, $tick); 379 380 $delta = $max - $min; 381 $tick = int(&logbase(10, $delta)); 382 $tick = &expbase(10, $tick - 1); 383 if ($delta / $tick > 10) { 384 if ($base == 10) { 385 if (($delta / (2 * $tick)) > 15) { 386 $adj = 10; 387 } elsif (($delta / (2 * $tick)) > 10) { 388 $adj = 5; 389 } else { 390 $adj = 2; 391 } 392 } else { 393 $adj = 2; 394 } 395 } else { 396 $adj = 1; 397 } 398 $tick *= $adj; 399 400 # Go figure out the endpoints. This is O(log10(n)) where N is the 401 # number of ticks from 0 to the min. 402 $lower = 0; 403 for ($i = 10e99; $i > 0; $i = int($i/$base)) { 404 $fudge = $i * $tick; 405 $bound = $min + $fudge * .00001; 406 407 # Sometimes it's too big 408 while ($lower > $bound) { 409 $lower -= $fudge; 410 } 411 412 # Sometimes it's too small 413 while (($lower + $fudge) <= $bound) { 414 $lower += $fudge; 415 } 416 } 417 418 if ($base == 2) { 419 if ($tick < 1) { 420 $tick = 1; 421 } else { 422 $tick = sprintf("%.0f", $tick); 423 } 424 $lower = sprintf("%.0f", $lower); 425 } 426 for ($upper = $lower; $upper < $max - $tick * .00001; $upper += $tick) { 427 } 428 if ($base == 2) { 429 $upper = sprintf("%.0f", $upper); 430 } 431 # If you don't like your end points on the border then do this. 432 unless ($close) { 433 if ($min - $lower < .1 * $tick) { 434 $lower -= $tick; 435 } 436 if ($max - $upper < .1 * $tick) { 437 $upper += $tick; 438 } 439 } 440 ($lower, $upper, $tick); 441} 442 443# Spit out the pic stuff. 444# The idea here is to spit the variables and let pic do most of the math. 445# This allows tweaking of the output by hand. 446sub pic 447{ 448 if ($k) { 449 $print = 'sprintf("%.0fK", j/1000)'; 450 } else { 451 $print = 'sprintf("%.0f", j)'; 452 } 453 if ($grid || $halfgrid) { 454 $nogrid = "dotted"; 455 } else { 456 $nogrid = "invis"; 457 } 458 if ($nobox) { 459 $nobox = "invis"; 460 } 461 $log_x = $logx ? "logx = 1" : "logx = 0"; 462 $log_y = $logy ? "logy = 1" : "logy = 0"; 463 if ($big) { 464 print ".sp .5i\n.po .5i\n"; 465 if ($below) { 466 $ysize = 7; 467 } else { 468 $ysize = 9; 469 } 470 if ($nodatal) { 471 $xsize = 7; 472 } else { 473 $xsize = 6; 474 } 475 } elsif ($small) { 476 $ysize = 1.75; 477 $xsize = 1.75; 478 } elsif ($medium) { 479 print ".po .52i\n"; 480 $ysize = 1.9; 481 $xsize = 2.05; 482 } elsif ($slide) { 483 print ".sp .35i\n"; 484 $xsize = 4.5; 485 $ysize = 4.1; 486 } else { 487 print ".sp 1i\n"; 488 $ysize = 5; 489 $xsize = 5; 490 } 491 &graph; 492 493 # Mark the data points 494 @datasets = (); 495 for ($sub = 0; $sub <= $#lines; $sub++) { 496 $_ = $lines[$sub]; 497 if (/^\s*$/) { # end of data set 498 &data($set++); 499 if ($grapheach) { 500 &titles; 501 if ($small) { 502 if ($set == 4) { 503 print ".sp -11i\n"; 504 print ".po 3.5i\n"; 505 } elsif ($set == 8) { 506 print ".sp -11i\n"; 507 print ".po 6i\n"; 508 } 509 } else { # ??? 510 if ($set == 4) { 511 print ".sp -11i\n"; 512 print ".po 3.15i\n"; 513 } elsif ($set == 8) { 514 print ".sp -11i\n"; 515 print ".po 5.8i\n"; 516 } 517 } 518 519 if ($sub < $#lines) { 520 &graph; 521 } 522 } 523 next; 524 } 525 if (/^"?%fake/) { # Skip this 526 next; 527 } 528 if (/^"?%T\s+/) { # Title specification 529 # Spit out the last graph at next title. 530 if ($br_title && $graphs++ > 0) { 531 &titles; 532 if ($graphs == 5) { 533 print ".sp -11i\n"; 534 print ".po 3.5i\n"; 535 } elsif ($graphs == 9) { 536 print ".sp -11i\n"; 537 print ".po 6i\n"; 538 } 539 &graph; 540 } 541 s/^"?%T\s+//; 542 chop; 543 $Gtitle = $_; 544 next; 545 } 546 if (/^"?%X\s+/) { # X axis title specification 547 s/^"?%X\s+//; 548 chop; 549 $Xtitle = $_; 550 next; 551 } 552 if (/^"?%Y\s+/) { # Y axis title specification 553 s/^"?%Y\s+//; 554 chop; 555 $Ytitle = $_; 556 next; 557 } 558 if (/^"?%P\s+/) { # Page title specification 559 s/^"?%P\s+//; 560 chop; 561 $Ptitle = $_; 562 #VERBOSE warn "Pt: $Ptitle\n"; 563 next; 564 } 565 if (/^"/) { # Data set title 566 s/^"//; 567 chop; 568 $dataset = $_; 569 push(@datasets, "$dataset"); 570 next; 571 } 572 push(@data, $_); 573 } 574 unless ($grapheach) { 575 &data($set++); 576 &titles; 577 } 578 if (defined($Ptitle)) { 579 print ".po 1i\n.sp -12i\n.ps 20\n.ce 1\n"; 580 print "$Ptitle\n"; 581 print ".po 1i\n.sp -12i\n.sp 10.4i\n.ps 20\n.ce 1\n"; 582 print "$Ptitle\n"; 583 } 584} 585 586# Draw the titles and finish this graph. 587sub titles 588{ 589 # Do X/Y titles, if any. 590 unless ($nolabels) { 591 $Xtitle = defined($Xtitle) ? $Xtitle : "X"; 592 $Ytitle = defined($Ytitle) ? $Ytitle : "Y"; 593 if ($rev && $first_title) { 594 $tmp = $Xtitle; 595 $Xtitle = $Ytitle; 596 $Ytitle = $tmp; 597 } 598 print "\n# Xaxis title.\n"; 599 print "\"\\s+4$Xtitle\\s0\" rjust at O.se - (0, .6)\n"; 600 601 print "\n# Yaxis title ($Ytitle)\n.ps +2\n"; 602 $tmp = $Ytitle; 603 while (length($tmp) > 0) { 604 $tmp =~ s/(.)//; 605 print "\"$1\" "; 606 } 607 print "\\\n at O.w - (.75, 0)\n.ps\n"; 608 609 } 610 611 # Do the graph title, if any. 612 $Gtitle = defined($Gtitle) ? $Gtitle : "Pic Graph"; 613 if ($grapheach) { 614 $Gtitle = $datasets[$#datasets]; 615 print "\n# Graph title.\n"; 616 print "\"$Gtitle\" at O.n + (0, .1)\n"; 617 } 618 619 if ($br_title) { 620 print "\n# Graph title.\n"; 621 print "\"\\s+2$Gtitle\\s0\" at O.n + (0, .1)\n"; 622 } 623 624 unless ($nolabels || $notitle) { 625 print "\n# Graph title.\n"; 626 if ($big) { 627 print "\"\\s+8$Gtitle\\s0\" at O.n + (0, .3)\n"; 628 } else { 629 print "\"\\s+4$Gtitle\\s0\" at O.n + (0, .3)\n"; 630 } 631 } 632 633 if ($cut) { 634 $cutthick = .75; 635 print "\n# Cut marks\n"; 636 print "move to O.n + 0,.65; line thick $cutthick right .1\n"; 637 print "move to O.w - 1,0; line thick $cutthick down .1\n"; 638 print "move to O.e + .35,0; line thick $cutthick down .1\n"; 639 } 640 641 # Do the dataset titles. 642 $i = 0; 643 unless ($nodatal) { 644 print "\n# Title.\n"; 645 if (!$grapheach) { 646 print ".ft R\n" if ($slide); 647 for ( ; $i <= $#datasets; $i++) { 648 print $marks[$i % $nmarks]; 649 if ($below) { 650 print " at O.sw - (0, .75 + $i * vs)\n"; 651 } else { 652 print " at O.ne + (.25, - $i * vs)\n"; 653 } 654 print 655 "\"$datasets[$i]\" ljust at last [].e + (.1, 0)\n"; 656 } 657 if ($cut) { 658 print "\nmove to O.s - 0,.75 + $i * vs\n"; 659 print "line thick $cutthick right .1\n"; 660 } 661 print ".ft\n" if ($slide); 662 } 663 } 664 665 # Finish up. 666 print "]\n.ft\n.ps\n.PE\n"; 667 668 # Do the statistics 669 if ($stats) { 670 $i++; 671 $min = sprintf "%.4f", $min; 672 $max = sprintf "%.4f", $max; 673 $stat_median = sprintf "%.4f", $stat_median; 674 $stat_avg = sprintf "%.4f", $stat_avg; 675 $stat_stddev = sprintf "%.4f", $stat_stddev; 676 $stat_avgdev = sprintf "%.4f", $stat_avgdev; 677 print <<EOF; 678.ps 12 679.vs 14 680.ft CB 681.po +.7i 682.TS 683c s 684l r. 685Statistics 686= 687min $min 688max $max 689median $stat_median 690average $stat_avg 691stddev $stat_stddev 692avgdev $stat_avgdev 693.TE 694.po -.7i 695.ft 696.ps 697.vs 698EOF 699 } 700 701 $first_title = 0; 702} 703 704sub graph 705{ 706 if ($hist) { $hist = 1; } else { $hist = 0; } 707 print ".sp ${graphspace}\n"; 708 print <<EOF; 709.PS 710.ps $PS 711.vs 11 712.ft $ft 713[ 714# Variables, tweak these. 715 xtick = $xtick # width of an X tick 716 xlower = $xlower # where the xtick start 717 xupper = $xupper # upper range of graph 718 xn = $xn # number of ticks to do 719 ytick = $ytick # width of an Y tick 720 ylower = $ylower # where the ytick start 721 yupper = $yupper # upper range of graph 722 yn = $yn # number of ticks to do 723 xsize = $xsize # width of the graph 724 ysize = $ysize # height of the graph 725 yscale = ysize / (yupper - ylower) # scale data to paper 726 xscale = xsize / (xupper - xlower) # scale data to paper 727 tick = $tick # distance towards numbers 728 gthk = $gthk # thickness of grid lines 729 thk = $thk # thickness of data lines 730 grapheach = $grapheach # doing lotso little ones? 731 halfgrid = $halfgrid # fewer grid lines 732 qthk = 2.0 # thickness of quartile lines 733 vs = .15 # works for 10 point fonts 734 hist = $hist # histogram 735 ypartial = $ypartial # Y spillerover 736 $log_x # 1 if x data is log base 2 737 $log_y # 1 if y data is log base 2 738 739# Draw the graph borders and tick marks 740 O: box $nobox thick 2 ht ysize wid xsize 741 if (hist) then { 742 # The box was invisible, draw the three sides 743 # The partial part i sbecause we are just too big. 744 line thick 2 from O.sw to O.se 745 line thick 2 from O.sw to O.nw + 0,ypartial*yscale 746 line thick 2 from O.se to O.ne + 0,ypartial*yscale 747 xgridlen = xsize + tick/2 748 } else { 749 xgridlen = xsize 750 } 751 if (ysize < 2.5) then { 752 ysp = -.15 753 xsp = -.2 754 tick = tick * .75 755 } else { 756 ysp = -.2 757 xsp = -.25 758 } 759 j = ylower 760 t = tick * .5 761 for i = 0 to yn by 1 do { 762 ys = j - ylower 763 g = ys * yscale 764 # Draw the ticks to the numbers on the Y axis 765 line thick gthk from O.sw + (-tick, g) to O.sw + (0, g) 766 if (hist) then { 767 line thick gthk from O.se + (tick, g) to O.se + (0, g) 768 } 769 # Grid line across at same level as number ticks 770 line $nogrid thick gthk from O.sw + 0,g to O.sw + xsize,g 771 if (i < yn) then { 772 y2 = (ys + (ytick / 2)) * yscale 773 if (!halfgrid) then { 774 # Grid line across between number ticks 775 line $nogrid thick gthk from \\ 776 O.sw + (-t, y2) to O.sw + (xgridlen, y2) 777 } 778 } 779 if (logy == 1) then { 780 tmp = 2 ^ j; 781 if (tmp >= 1024*1024) then { 782 tmp = tmp / (1024*1024) 783 sprintf("%.0fM", tmp) at O.sw + ysp,g-.02 784 } else { if (tmp >= 1024) then { 785 tmp = tmp / 1024 786 sprintf("%.0fK", tmp) rjust at O.sw + ysp,g-.02 787 } else { 788 sprintf("%.0f", tmp) rjust at O.sw + ysp,g-.02 789 }} 790 } else { if (yupper - ylower > 999) then { 791 $print rjust at O.sw + ysp, g - .02 792 if (hist) then { $print ljust at O.se + -ysp,g-.02 } 793 } else { if (yupper - ylower > 10) then { 794 sprintf("%.0f", j) rjust at O.sw + ysp, g - .02 795 if (hist) then { 796 sprintf("%.0f", j) ljust at O.se + -ysp,g-.02 797 } 798 } else { if (yupper - ylower > 1) then { 799 sprintf("%.1f", j) rjust at O.sw + ysp, g - .02 800 sprintf("%.1f", j) rjust at O.sw + ysp, g - .02 801 } else { if (yupper - ylower > .1) then { 802 sprintf("%.2f", j) rjust at O.sw + ysp, g - .02 803 if (hist) then { 804 sprintf("%.2f", j) ljust at O.se + -ysp,g-.02 805 } 806 } else { 807 sprintf("%.3f", j) rjust at O.sw + ysp, g - .02 808 if (hist) then { 809 sprintf("%.3f", j) ljust at O.se + -ysp,g-.02 810 } 811 }}}}} 812 j = j + ytick 813 } 814 j = xlower 815 even = 0 816 for i = 0 to xn by 1 do { 817 even = !even 818 doit = !grapheach || xn > 9 || even 819 xs = j - xlower 820 g = xs * xscale 821 line thick gthk from O.sw + (g, -tick) to O.sw + (g, 0) 822 if (!hist) then { 823 line $nogrid thick gthk from O.sw + g,0 to O.sw + g,ysize 824 } 825 if (i < xn) then { 826 x2 = (xs + (xtick / 2)) * xscale 827 if (!halfgrid && !hist) then { 828 line $nogrid thick gthk from O.sw+x2,-t to O.sw+x2,ysize 829 } 830 } 831 if (logx == 1) then { 832 tmp = 2 ^ j; 833 if (tmp >= 1024*1024) then { 834 tmp = tmp / (1024*1024) 835 if (doit) then { 836 sprintf("%.0fM", tmp) at O.sw + g,xsp 837 } 838 } else { if (tmp >= 1024) then { 839 tmp = tmp / 1024 840 if (doit) then { 841 sprintf("%.0fK", tmp) at O.sw + g,xsp 842 } 843 } else { 844 if (doit) then { 845 sprintf("%.0f", tmp) at O.sw + g,xsp 846 } 847 }} 848 } else { if (xupper - xlower > 999) then { 849 $print at O.sw + g, xsp 850 } else { if (xupper - xlower > 10) then { 851 sprintf("%.0f", j) at O.sw + g, xsp 852 } else { if (xupper - xlower > 1) then { 853 sprintf("%.1f", j) at O.sw + g, xsp 854 } else { if (xupper - xlower > .1) then { 855 sprintf("%.2f", j) at O.sw + g, xsp 856 } else { 857 sprintf("%.3f", j) at O.sw + g, xsp 858 }}}}} 859 j = j + xtick 860 } 861EOF 862 # Add some statistics. 863 if ($stats) { 864 print "line from O.sw + 0,(yscale * ($stat_avg - $ylower)) " . 865 "to O.se + 0,(yscale * ($stat_avg - $ylower))\n"; 866 print "\"average\" at last line.e + .2,0 ljust\n"; 867 print "line from O.sw + 0,(yscale * ($stat_median - $ylower)) " . 868 "to O.se + 0,(yscale * ($stat_median - $ylower))\n"; 869 print "\"median\" at last line.e + .2,0 ljust\n"; 870 $tmp = $stat_median + $stat_avgdev; 871 print "line from O.sw + 0,(yscale * ($tmp - $ylower)) " . 872 "to O.se + 0,(yscale * ($tmp - $ylower))\n"; 873 print "\"+ avgdev\" at last line.e + .2,0 ljust\n"; 874 $tmp = $stat_median - $stat_avgdev; 875 print "line from O.sw + 0,(yscale * ($tmp - $ylower)) " . 876 "to O.se + 0,(yscale * ($tmp - $ylower))\n"; 877 print "\"- avgdev\" at last line.e + .2,0 ljust\n"; 878 } 879} 880 881sub data 882{ 883 local($mark) = int(int($_[0]) % int($nmarks)); 884 885 print "\n# DATASET: $dataset, MARK $mark\n"; 886 $first = 1; 887 foreach $d (@data) { 888 next if $d =~ /^\s*"/; 889 next if $d =~ /^\s*#/; 890 next if $d =~ /^\s*$/; 891 @_ = split(/[ \t\n]+/, $d); 892 $x = sprintf("%.6g", $_[0]); 893 $y = sprintf("%.6g", $_[1]); 894 if ($#_ == 1) { 895 if ($hist) { 896 print "box fill .25 " . 897 "ht yscale * ($y - ylower) " . 898 "wid $hist_bsize * xscale " . 899 "with .sw at O.sw + " . 900 "xscale * ($x - xlower),0\n"; 901 } elsif ($nomarks && ($grapheach || !$first)) { 902 print $nomark . " at O.sw + \\\n\t" . 903 "(xscale * ($x - xlower), " . 904 "yscale * ($y - ylower))\n"; 905 } else { 906 print $marks[$mark] . 907 " at O.sw + \\\n\t" . 908 "(xscale * ($x - xlower), " . 909 "yscale * ($y - ylower))\n"; 910 } 911 if (!$hist && $first != 1) { 912 print "line $lineinvis thick thk from " . 913 "2nd last [].c to last [].c\n"; 914 } 915 $first = 0; 916 } elsif ($#_ == 5) { # Quartile graph 917 # Draw the lower line 918 print "x = xscale * ($_[0] - xlower)\n"; 919 print " line thick qthk from \\\n\t" . 920 "O.sw + x, yscale * ($_[1] - ylower) to\\\n\t" . 921 "O.sw + x, yscale * ($_[2] - ylower)\n"; 922 # Draw the mark 923 print " $marks[$mark]" . " at O.sw + \\\n\t" . 924 "x, yscale * ($_[3] - ylower)\n"; 925 # Draw the upper line 926 print " line thick qthk from \\\n\t" . 927 "O.sw + x, yscale * ($_[4] - ylower) to\\\n\t" . 928 "O.sw + x, yscale * ($_[5] - ylower)\n"; 929 # Connect the lines? 930 if ($qline) { 931 if ($first != 1) { 932 print "line thick thk from " . 933 "2nd last [].c to last [].c\n"; 934 } 935 } 936 $first = 0; 937 } 938 } 939 # Put a mark on the end point 940 if ($nomarks && !$nodatal && !$first && !$grapheach) { 941 print $marks[$mark] . 942 " at O.sw + \\\n\t" . 943 "(xscale * ($x - xlower), " . 944 "yscale * ($y - ylower))\n"; 945 } 946 @data = (); 947} 948