(c) Copyright 1986-1993 by Joseph Felsenstein and by the University of Washington. Written by Joseph Felsenstein. Permission is granted to copy this document provided that no fee is charged for it and that this copyright notice is not removed.
DRAWTREE and DRAWGRAM are interactive tree-plotting programs that take a tree description in a file and read it, and then let you interactively make various settings and then plot the tree on a laser printer, plotter, or dot matrix printer. In many cases (with IBM PC graphics, with a DEC graphics terminal or with a Tektronix-compatible graphics terminal) you can preview the resulting tree. This allows you to modify the tree until you like the result, then plot the result. DRAWTREE plots unrooted trees and DRAWGRAM plots rooted cladograms and phenograms. On good plotters or laser printers both can produce fully publishable results. On dot matrix printers the results look grainy but are good enough for overhead transparencies or slides for presentations.
These programs are descended from PLOTGRAM and PLOTREE written by Christopher Meacham. I have incorporated his code for fonts and his plotter drivers, and in DRAWTREE have used some of his code for drawing unrooted trees. In both programs I have also included some plotter driver code by David Swofford, Julian Humphries and George D.F. "Buz" Wilson, to all of whom I am very grateful. Mostly, however, they consist of my own code. The font files are printable-character recodings of the public-domain Hershey fonts, recoded by Christopher Meacham.
This document will describe the features common to both programs. The documents for DRAWTREE and DRAWGRAM describe the particular choices you can make in each of those programs. The Appendix to this documentation file contains some pieces of C code that can be inserted to make the program handle another plotting device -- the plotters by Calcomp.
Once you have all these the programs should be fairly self explanatory, particular if you can preview your plots so that you can discover the meaning of the different options by trying them out.
Once you have a compiled version of the appropriate program, say DRAWGRAM, and a file called (say) TREEFILE with the tree in it, and a font file (say FONT2 which you have copied as a file called FONTFILE), all you do is run the program DRAWGRAM. It should automatically read the font and tree files, and will ask you to choose the graphics devices. Then it will let you see the options it has chosen, and ask you if you want to change these. Once you have modified those that you want to, you can tell it to accept those. The program will then allow you to preview the tree on your screen, if you have told it that you have an appropriate graphics screen (the PCDOS and 386 PCDOS versions of the program will automatically adapt to a number of the commonest PC graphics boards, and the Macintosh version will adapt to the Macintosh screen). After previewing the tree, it will ask if you are ready to plot the tree. If you say no, it will once again allow you to change options and will the allow you to preview the tree again, and so on as many times as you want. If you say yes, then it will write a file called (say) PLOTFILE. If this file is copied directly to your plotter or printer, it should result in a beautifully plotted tree. If the final plotting device is a DEC or PCDOS graphics screen, it may not write a plot file but will plot directly on the screen.
Having read the above, you may be ready to run the program. Below you Will find more information about representation of trees in the tree file, on the different kinds of graphics devices supported by this program, and on how to recompile these programs. If you have (for example) a PCDOS system and a compiled version of the program, then all you have to do is have a tree file called TREEFILE, a font file called FONTFILE, and run the program. Then copy PLOTFILE to your plotter or printer.
A D
\ E /
\ C / /
\ ! / /
\ ! / /
B \!/ /
\ o /
\ ! /
\ ! /
\ ! /
\ ! /
\!/
o
!
!
then in the tree file it is represented by the following sequence of printable
characters, starting at the beginning of the file:
(B,(A,C,E),D);The tree ends with a semicolon. Everything after the semicolon in the input file is ignored, including any other trees. The bottommost node in the tree is an interior node, not a tip. Interior nodes are represented by a pair of matched parentheses. Between them are representations of the nodes that are immediately descended from that node, separated by commas. In the above tree, the immediate descendants are B, another interior node, and D. The other interior node is represented by a pair of parentheses, enclosing representations of its immediate descendants, A, C, and E.
Tips are represented by their names. A name can be any string of printable characters except blanks, colons, semcolons, parentheses, and square brackets. In the programs a maximum of 30 characters are allowed for names: this limit can easily be increased by recompiling the program and changing the constant declaration for "nch" at the beginning of the program.
Because you may want to include a blank in a name, it is assumed that an underscore character ("_") stands for a blank; any of these in a name will be converted to a blank when it is read in. Any name may also be empty: a tree like
(,(,,),);is allowed. Trees can be multifurcating at any level (while in many of the programs multifurcations of user-defined trees are not allowed or restricted to a trifurcation at the bottommost level, these programs do make any such restriction).
Branch lengths can be incorporated into a tree by putting a real number, with or without decimal point, after a node and preceded by a comma. This represents the length of the branch immediately below that node. Thus the above tree might have lengths represented as:
(B:6.0,(A:5.0,C:3.0,E:4.0):5.0,D:11.0);These programs will be able to make use of this information only if lengths exist for every branch, except the one at the bottom of the tree.
The tree starts on the first line of the file, and can continue to subsequent lines. It is best to proceed to a new line, if at all, immediately after a comma. Blanks can be inserted at any point except in the middle of a species name or a branch length.
The above description is of a subset of the New Hampshire Standard. For example, interior nodes can have names in that standard, but if any are included the present programs will omit them.
To help you understand this tree representation, here are some trees in the above form:
((raccoon:19.19959,bear:6.80041):0.84600,((sea_lion:11.99700, seal:12.00300):7.52973,((monkey:100.85930,cat:47.14069):20.59201, weasel:18.87953):2.09460):3.87382,dog:25.46154); (Bovine:0.69395,(Gibbon:0.36079,(Orang:0.33636,(Gorilla:0.17147,(Chimp:0.19268, Human:0.11927):0.08386):0.06124):0.15057):0.54939,Mouse:1.21460); (Bovine:0.69395,(Hylobates:0.36079,(Pongo:0.33636,(G._Gorilla:0.17147, (P._paniscus:0.19268,H._sapiens:0.11927):0.08386):0.06124):0.15057):0.54939, Rodent:1.21460); (); ((A,B),(C,D)); (Alpha,Beta,Gamma,Delta,,Epsilon,,,);
The New Hampshire Standard was adopted June 26, 1986 by an informal committee meeting during the Society for the Study of Evolution meetings in Durham, New Hampshire and consisting of James Archie, William H.E. Day, Wayne Maddison, Christopher Meacham, F. James Rohlf, David Swofford, and myself.
Here are the choices, with some comments on each:
Conversion from these formats to others is also possible. The graphics conversion utility HiJaak, from Inset Systems (71 Commerce Drive, Brookfield, Connecticut 06804, (203) 775-5866) is a PCDOS program that converts numerous graphic file formats on input and output. It can read many formats, including the HPGL format and write many formats. It is available from many software discount houses. There is also a public-domain program by Jef Poskanzer called "PBMPLUS" that interconverts many bitmap formats.
COPY/B PLOTFILE PRN:
I would like to add more fonts. The present fonts are recoded versions of the Hershey fonts. They are legally publicly distributable. Most other font families on the market are not public domain and I cannot afford to license them for distribution. Some people have noticed that the Hershey fonts, which are drawn by a series of straight lines, have noticeable angles in what are supposed to be curves, when they are printed on modern laser printers and looked at closely. This is less a problem than one might think since, fortunately, when scientific journals print a tree it is usually shrunk so small that these imperfections (and often the tree itself) are hard to see!
One more font that could be added from the Hershey font collection would be a Greek font. If Greek users would find that useful I could add it, but my impression is that they publish mostly in English anyway.
If you want to write code for other printers, plotters, or vector file formats, this is not too hard. The plotter option "U" is provided as a place for you to insert your own code. Chris Meacham's system was to draw everything, including the characters in the names and all curves, by drawing a series of straight lines. Thus you need only master your plotter's commands for drawing straight lines. In procedure "plotrparms" you must set up the values of variables "xunitspercm" and "yunitspercm", which are the number of units in the x and y directions per centimeter, as well as variables "xsize" and "ysize" which are the size of the plotting area in centimeters in the x direction and the y direction. A variable "penchange" of a user-defined type is set to "yes" or "no" depending on whether the commands to change the pen must be issued when switching between plotting lines and drawing characters. Even though dot-matrix printers do not have pens, penchange should be set to "yes" for them. In PROCEDURE plot you must issue commands to draw a line from the current position (which is at (xnow, ynow) in the plotter's units) to the position (xabs, yabs), under the convention that the lower-left corner of the plotting area is (0.0, 0.0). In procedures "initplotter" and "finishplotter" you must issue commands to initialize the plotter and to finish plotting, respectively. If the pen is to be changed an appropriate piece of code must be inserted in procedure "penchange".
For dot matrix printers and raster graphics matters are a bit more complex. The procedures "plotrparms", "initplotter", "finishplotter" and "plot" still respectively set up the parameters for the plotter, initialize it, finish a plot, and plot one line. But now the plotting consists of drawing dots into a two-dimensional array called "stripe". Once the plot is finished this array is printed out. In most cases the array is not as tall as a full plot: instead it is a rectangular strip across it. When the program has finished drawing in ther strip, it prints it out and then moves down the plot to the next strip. For example, for Hewlett-Packard Laserjets we have defined the strip as 2550 dots wide and 20 dots deep. When the program goes to draw a line, it draws it into the strip and ignores any part of it that falls outside the strip. Thus the program does a complete plotting into the strip, then prints it, then moves down the diagram by (in this case) 20 dots, then does a complete plot into that strip, and so on.
To work with a new raster or dot matrix format, you will have to define the desired width of a strip ("strpwide"), the desired depth ("strpdeep"), and how many lines of bytes must be printed out to print a strip. For example Toshiba P351 printers in graphics mode print strips of dots 1350 bits wide by 24 bits deep, each column of 24 bits printing out as consecutive four bytes with 6 bits each. In that case, one prints out a strip by printing up to 1350 groups of 4 bytes. "strpdiv" is 4, and "strpwide" is 1350, and "strpdeep" is 24. procedure "striprint" is the one that prints out a strip, and has special-case code for the different printers and file formats. For file formats, all of which print out a single row of dots at a time, the variable "strpdiv" is not used. The variable "dotmatrix" is set to "true" or "false" in procedure "plotrparms" according to whether or not "strpdiv" is to be used. procedure "plotdot" sets a single dot in the array "strip" to 1 at position (xabs, yabs). The coordinates run from 1 at the top of the plot to larger numbers as we proceed down the page. Again, there is special-case code for different printers and file formats in that procedure. You will probably want to read the code for some of the dot matrix or file format options if you want to write code for one of them. Many of them have provision for printing only part of a line, ignoring parts of it that have no dots to print.
I would be happy to obtain the resulting code from you to consider adding it to this listing so we can cover more kinds of plotters, printers, and file formats.
APPENDIX 1. Code to drive some other graphics devices. These pieces of code are to be inserted in the places reserved for the "Y" plotter option. The variables necessary to run this have already been incorporated into the programs. A global declaration needed near the front of drawtree.c:
Char cchex[16];
Code to be inserted into procedure plotrparms:
case 'Y':
plotter = other;
xunitspercm = 39.37;
yunitspercm = 39.37;
xsize = 25.0;
ysize = 25.0;
xposition = 12.5;
yposition = 0.0;
xoption = center;
yoption = above;
rotation = 0.0;
break;
Code to be inserted into procedure plot:
Declare these variables at the beginning of the procedure:
long n, inc, xinc, yinc, xlast, ylast, xrel, yrel, xhigh, yhigh, xlow, ylow; Char quadrant;and insert this into the switch statement:
case other:
if (penstatus == pendown)
putc('H', plotfile);
else
putc('D', plotfile);
xrel = (long)floor(xabs + 0.5) - xnow;
yrel = (long)floor(yabs + 0.5) - ynow;
xnow = (long)floor(xabs + 0.5);
ynow = (long)floor(yabs + 0.5);
if (xrel > 0) {
if (yrel > 0)
quadrant = 'P';
else
quadrant = 'T';
} else if (yrel > 0)
quadrant = 'X';
else
quadrant = '1';
xrel = labs(xrel);
yrel = labs(yrel);
if (xrel > yrel)
n = xrel / 255 + 1;
else
n = yrel / 255 + 1;
xinc = xrel / n;
yinc = yrel / n;
xlast = xrel % n;
ylast = yrel % n;
xhigh = xinc / 16;
yhigh = yinc / 16;
xlow = xinc & 15;
ylow = yinc & 15;
for (i = 1; i <= n; i++)
fprintf(plotfile, "%c%c%c%c%c",
quadrant, cchex[xhigh - 1], cchex[xlow - 1], cchex[yhigh - 1],
cchex[ylow - 1]);
if (xlast != 0 || ylast != 0)
fprintf(plotfile, "%c%c%c%c%c",
quadrant, cchex[-1], cchex[xlast - 1], cchex[-1],
cchex[ylast - 1]);
break;
Code to be inserted into procedure initplotter:
case other:
cchex[-1] = 'C';
cchex[0] = 'D';
cchex[1] = 'H';
cchex[2] = 'L';
cchex[3] = 'P';
cchex[4] = 'T';
cchex[5] = 'X';
cchex[6] = '1';
cchex[7] = '5';
cchex[8] = '9';
cchex[9] = '/';
cchex[10] = '=';
cchex[11] = '#';
cchex[12] = '"';
cchex[13] = ''';
cchex[14] = '^';
xnow = 0.0;
ynow = 0.0;
fprintf(plotfile, "CCCCCCCCCC");
break;
Code to be inserted into procedure finishplotter:
case other:
plot(penup, 0.0, yrange + 50.0);
break;