bargraph 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. # $Id$
  2. eval 'exec perl -Ss $0 "$@"'
  3. if 0;
  4. # A simple bargraph preprocessor for GNU pic / troff package.
  5. # Hacked into existence by Larry McVoy (lm@sun.com now lm@sgi.com).
  6. # Copyright (c) 1994 Larry McVoy. GPLed software.
  7. #
  8. # TODO
  9. # Make this work with sideways graphs.
  10. #
  11. # Input format is:
  12. #
  13. # 3 foo bar
  14. # 9 bigger foo
  15. # "Silly example
  16. #
  17. # and output is
  18. #
  19. # bigger
  20. # foo
  21. # +----------+
  22. # | |
  23. # foo | |
  24. # bar | |
  25. # +----------+ | |
  26. # | | | |
  27. # +----------+ +----------+
  28. # -------------------------------
  29. # 3 9
  30. #
  31. # Silly example
  32. #
  33. # Input options:
  34. # specifier value default
  35. # %ps <point size> 10
  36. # %ft <font> HB
  37. # %labelgap <space in inches between fill labels> 1.5
  38. # %xsize <size of graph width in inches> 7
  39. # %ysize <size of graph height in inches> 6
  40. # %Title n|s <Bargraph title> none
  41. # %titleplus <increase in points of titlesize> 0
  42. # %label%d <label name> none
  43. # %boxpercent <100% means columns touch> 75
  44. # %worse up|down n|w|e|s|nw|ne|sw|se - idiot arrow
  45. # %better up|down n|w|e|s|nw|ne|sw|se - idiot arrow
  46. # %fakemax <pretend one data point was this big>
  47. #
  48. # The data can be optionally followed by a %fill%d that gets turned into
  49. # the fill value (darkness) for that bar of the bar graph. The default
  50. # fill value is whatever pic defaults to.
  51. # The %label control is used to provide a legend for the different fill
  52. # values.
  53. #
  54. # Command line options:
  55. #
  56. # -big make the x/y defaults be 7.5 inches, crank up title size, and
  57. # don't put a spacer at the top.
  58. # -nobox do not put an outline box around the bargraph.
  59. #
  60. # -sideways
  61. # do the bars towards the right.
  62. #
  63. # Much thanks to James Clark for providing such a nice replacement for
  64. # the Unix troff package.
  65. @lines = <>; # sluuuuuuuuuuuurp
  66. $titleplus = 2;
  67. $bottomplus = 0;
  68. $fill = "fillval";
  69. $SP = ".sp 1i";
  70. $PO = "0i";
  71. # All of these can be set in the graph with %xxx value
  72. $ps = 10;
  73. $ft = "CB";
  74. $xsize = 4;
  75. $ysize = 6;
  76. $boxpercent = 75;
  77. $labelgap = 1.5;
  78. if ($nobox) {
  79. $invis = "invis";
  80. } else {
  81. $invis = "";
  82. }
  83. if ($big) {
  84. $slide = 0;
  85. $xsize = 7.5;
  86. $ysize = 7.5;
  87. $SP = "";
  88. $titleplus = 4;
  89. $bottomplus = 2;
  90. # XXX - you may need to screw with this.
  91. $xsize -= 3.75 if ($sideways);
  92. }
  93. if ($slide) {
  94. $big = 0;
  95. $xsize = 6.5;
  96. $ysize = 4.20;
  97. $SP = ".sp .75i";
  98. $PO = ".23i";
  99. $titleplus = 2;
  100. $bottomplus = 0;
  101. # XXX - you may need to screw with this.
  102. $xsize -= 2.2 if ($sideways);
  103. }
  104. $vs = $ps + 1;
  105. # Calculate max to autosize the graph.
  106. foreach $_ (@lines) {
  107. next if /^\s*#/;
  108. next if /^\s*$/;
  109. if (/^\s*"/) {
  110. ($title = $_) =~ s/\s*"//;
  111. chop($title);
  112. push(@title, "\"\\s+$titleplus$title\\s0\"");
  113. next;
  114. }
  115. if (/^\s*%/) {
  116. &control(0);
  117. push(@control, $_);
  118. next;
  119. }
  120. @_ = split;
  121. if (!defined $maxdata) {
  122. $maxdata = $_[0];
  123. } else {
  124. $maxdata = $_[0] if ($maxdata < $_[0]);
  125. }
  126. push(@data, $_);
  127. }
  128. foreach $_ (@control) {
  129. &control(1);
  130. }
  131. $n = $#data + 1;
  132. $tps = $ps + $titleplus;
  133. $tvs = int($tps * 1.2);
  134. print <<EOF;
  135. $SP
  136. .po $PO
  137. .ft $ft
  138. .ps $ps
  139. .vs $tvs
  140. .ce 100
  141. EOF
  142. foreach $_ (@title_n) {
  143. print;
  144. }
  145. # Spit out the pic stuff.
  146. # The idea here is to spit the variables and let pic do most of the math.
  147. # This allows tweeking of the output by hand.
  148. print <<EOF;
  149. .ce 0
  150. .vs
  151. .PS
  152. .ps $ps
  153. .vs $vs
  154. [
  155. # Variables, tweek these.
  156. fillval = .12 # default fill value boxes
  157. xsize = $xsize # width of the graph
  158. ysize = $ysize # height of the graph
  159. n = $n
  160. boxpercent = $boxpercent / 100
  161. gap = xsize / n * (1 - boxpercent)
  162. maxdata = $maxdata
  163. yscale = ysize / maxdata
  164. xscale = xsize / maxdata
  165. # Draw the graph borders
  166. O: box invis ht ysize wid xsize
  167. EOF
  168. # line thick 2 from O.sw - (0, .1) to O.se - (0, .1)
  169. #foreach $_ (@control) {
  170. # &control(1);
  171. #}
  172. # boxwid = xsize / n * boxpercent
  173. if ($sideways) {
  174. print "boxht = ysize / n * boxpercent\n";
  175. # Each data point.
  176. for ($i = 0; $i <= $#data; $i++) {
  177. $_ = $data[$i];
  178. @_ = &getfill;
  179. print "box fill $fill wid $_[0] * xscale " .
  180. "with .nw at O.nw - (0, gap /2 + $i * (ysize/n))\n";
  181. $value = shift(@_);
  182. # XXXXXXX
  183. if ($_[$#_] =~ /secs/) {
  184. #print "\"@_\" ljust at last box.e + .1,0\n";
  185. $units = pop(@_);
  186. $each = pop(@_);
  187. print "\"\\s+1$value\\s0, @_,\\ \\s+1$each $units\\s0\" ljust at last box.e + .1,0\n";
  188. } else {
  189. print "\"\\s+2$value\\s0 @_\" ljust at last box.e + .1,0\n";
  190. }
  191. }
  192. } else {
  193. print "boxwid = xsize / n * boxpercent\n";
  194. # Each data point.
  195. for ($i = 0; $i <= $#data; $i++) {
  196. $_ = $data[$i];
  197. @_ = &getfill;
  198. print "box fill $fill ht $_[0] * yscale " .
  199. "with .sw at O.sw + (gap /2 + $i * (xsize/n), 0)\n";
  200. $value = shift(@_);
  201. @_ = &fmt(@_);
  202. #warn "V=$value\nT=@_\n";
  203. # Make the bar titles
  204. for ($j = $#_; $j >= 0; $j--) {
  205. print "\t\"$_[$j]\" at last box.n + (0, .05 + .12 * $j)\n";
  206. }
  207. print "\t\"\\s+$bottomplus$value\\s0\" at last box.s - (0, .30)\n";
  208. }
  209. }
  210. # Labels, if any
  211. if ($#labels > -1) {
  212. print "\n# Labels.\n";
  213. print "[\n boxwid = .35; boxht = .18; y = .10; x = -.03; ";
  214. print "labelgap = $labelgap\n";
  215. $first = 1;
  216. foreach $_ (@labels) {
  217. print " [ B: box fill $_[0]; ";
  218. shift(@_);
  219. print "\"@_\" ljust at B.e + (y, x) ]";
  220. if ($first == 1) {
  221. $first = 0;
  222. print "\n";
  223. } else {
  224. print " \\\n\twith .w at last [].e + (labelgap, 0)\n";
  225. }
  226. }
  227. print "] with .nw at O.sw - (0, .6)\n";
  228. }
  229. $invis = "invis" if $sideways;
  230. print <<EOF;
  231. ]
  232. box $invis wid last [].wid + .5 ht last [].ht + .5 with .nw at last [].nw + (-.25, .25)
  233. move to last [].nw + 0,.25
  234. line thick 2 right 7
  235. move to last [].sw - 0,.25
  236. line thick 2 right 7
  237. .PE
  238. .ft
  239. .ps
  240. .vs
  241. .po
  242. EOF
  243. print <<EOF;
  244. .po .5i
  245. .ft $ft
  246. .ps $ps
  247. .vs $tvs
  248. .sp .5
  249. .ce 100
  250. EOF
  251. foreach $_ (@title_s) {
  252. print;
  253. }
  254. print <<EOF;
  255. .po
  256. .ft
  257. .ps
  258. .vs
  259. .ce 0
  260. EOF
  261. exit 0;
  262. sub fmt
  263. {
  264. local(@args);
  265. local(@ret);
  266. # XXX - this assumes that # is not used anywhere else in the
  267. # label line.
  268. $_ = "@_";
  269. s/\\ /#/g;
  270. @args = split;
  271. foreach $_ (@args) {
  272. s/#/ /g;
  273. }
  274. $len = 0;
  275. foreach $_ (@args) {
  276. $len = length($_) if (length($_) > $len);
  277. }
  278. $len += 2;
  279. $word = shift(@args);
  280. while ($#args > -1) {
  281. if (length($word) + length($args[0]) < $len) {
  282. $word .= " $args[0]";
  283. shift(@args);
  284. } else {
  285. push(@ret, $word);
  286. $word = shift(@args);
  287. }
  288. }
  289. push(@ret, $word);
  290. reverse(@ret);
  291. }
  292. # Eat some control information
  293. #
  294. sub control
  295. {
  296. local($pass) = $_[0];
  297. if ($pass == 0) {
  298. s/.*%//;
  299. chop;
  300. }
  301. @_ = split;
  302. if ($_[0] =~ /[Ww]orse$/ || $_[0] =~ /[Bb]etter$/) {
  303. return if ($pass == 0);
  304. if ($#_ != 2) {
  305. die "bad control: $_\n";
  306. return;
  307. }
  308. ($label, $dir, $where) = @_;
  309. print "\n# Idiot arrow\n";
  310. print "[\tarrow thick 10 wid .5 ht .4 $dir 1.15\n";
  311. print "\t\"\\s+9$label\\s0\" ";
  312. if ($dir eq "up") {
  313. print "at last arrow.s - (0, .25)\n";
  314. } elsif ($dir eq "down") {
  315. print "at last arrow.n + (0, .25)\n";
  316. } else {
  317. die "bad control: $_\n";
  318. }
  319. print "] with .$where at O.$where ";
  320. if ($where eq "n") {
  321. print "- (0, .5)\n";
  322. } elsif ($where eq "ne") {
  323. print "- (.5, .5)\n";
  324. } elsif ($where eq "e") {
  325. print "- (.5, 0)\n";
  326. } elsif ($where eq "se") {
  327. print "- (.5, -.5)\n";
  328. } elsif ($where eq "s") {
  329. print "+ (0, .5)\n";
  330. } elsif ($where eq "sw") {
  331. print "+ (.5, .5)\n";
  332. } elsif ($where eq "w") {
  333. print "+ (.5, 0)\n";
  334. } elsif ($where eq "nw") {
  335. print "+ (.5, -.5)\n";
  336. } else {
  337. die "bad control: $_\n";
  338. }
  339. print "\n";
  340. } elsif ($_[0] =~ /Title/) {
  341. # XXX - I haven't fixed this for -sideways
  342. return if ($pass == 0);
  343. if ($_[1] eq "n") {
  344. shift(@_); shift(@_);
  345. push(@title_n, "\\s+$titleplus@_\\s0\n");
  346. } elsif ($_[1] eq "s") {
  347. shift(@_); shift(@_);
  348. push(@title_s, "\\s+$titleplus@_\\s0\n");
  349. } else {
  350. die "bad control: $_\n";
  351. }
  352. } elsif ($_[0] =~ /ps/) {
  353. $ps = $_[1];
  354. } elsif ($_[0] =~ /ft/) {
  355. $ft = $_[1];
  356. } elsif ($_[0] =~ /xsize/) {
  357. $xsize = $_[1];
  358. } elsif ($_[0] =~ /ysize/) {
  359. $ysize = $_[1];
  360. } elsif ($_[0] =~ /titleplus/) {
  361. $titleplus = $_[1];
  362. } elsif ($_[0] =~ /boxpercent/) {
  363. $boxpercent = $_[1];
  364. } elsif ($_[0] =~ /labelgap/) {
  365. $labelgap = $_[1];
  366. } elsif ($_[0] =~ /label/) { # has to be after labelgap
  367. return if ($pass == 0);
  368. $_[0] =~ s/label//;
  369. if (length($_[0]) > 0) {
  370. $fill = $_[0];
  371. } else {
  372. $fill = "fillval";
  373. }
  374. push(@labels, "@_");
  375. } elsif ($_[0] =~ /fakemax/) {
  376. if (!defined $maxdata) {
  377. $maxdata = $_[1];
  378. } else {
  379. $maxdata = $_[1] if ($maxdata < $_[1]);
  380. }
  381. } else {
  382. die "bad control: $_\n";
  383. }
  384. }
  385. # Look for a %fill[val], eat it, and set $fill
  386. sub getfill
  387. {
  388. local (@line);
  389. if (/%fill/) {
  390. @_ = split;
  391. foreach $_ (@_) {
  392. if (/%fill/) {
  393. s/%fill//;
  394. if (length($_) > 0) {
  395. $fill = $_;
  396. } else {
  397. $fill = "fillval";
  398. }
  399. } else {
  400. push(@line, $_);
  401. }
  402. }
  403. } else {
  404. $fill = "fillval";
  405. @line = split;
  406. }
  407. @line;
  408. }