reingold@emr.cs.uiuc.edu (Edward M. Reingold) (01/11/91)
I'm interested in tree drawing macros. I have a set that uses PiCTeX and Latex and I know about Bruggermann-Klein and Wood's work. Please respond by email; I do not ordinarily read these notes. -- Professor Edward M. Reingold reingold@cs.uiuc.edu Department of Computer Science (217) 333-6733 University of Illinois at Urbana-Champaign 1304 W. Springfield Ave. Urbana, IL 61801-2987 USA
reingold@emr.cs.uiuc.edu (Edward M. Reingold) (03/03/91)
Some time ago I asked for any existing tree-drawing macros to be sent to me. The response was enthusiastic, but none of what I received filled the bill, so a colleague and I cooked up our own set, based on PicTeX. Here they are; we'd appreciate any comments. --------------------tree.tex for use with PicTeX------------------------------ % Binary tree drawing in LaTeX using the PiCTeX macros. % % Edward M. Reingold (reingold@cs.uiuc.edu) % Nachum Dershowitz (nachum@cs.uiuc.edu) % \typeout{Binary Tree Macros. Released 18 Jan 1991.} % % These macros are in the public domain. You may use them and copy them at % will, provided you retain the authorship information. % % % USAGE: \tree[optional root symbol]{left subtree}{right subtree} % % For example, % % \tree[X] % {\setdots\tree[Z] % {\setsolid\tree[Y]{a}{}} % {\setsolid\tree{c}{d}}} % {\tree % {\tree{}{f}} % {\tree{g}{h}}} % % The root symbol and leaves can be anything you can construct in LaTeX % or PiCTeX. The trees constructed can be used in any context in LaTeX % or PiCTeX. That is, you can have, say, tables of trees of equations. % % % WARNINGS: Do not use the tilde (~) as the first character in any subtree! % Load these macros BEFORE the PiCTeX macros in the documentstyle. % % % PARAMETERS: Feel free to change the following tree drawing parameters; % these parameters can be reset even in the middle of a tree. % \newdimen\subtreesep \subtreesep=10pt % Distance between nonempty subtrees \newdimen\levelsep \levelsep=30pt % Distance between successive levels \def\nodesymbol{$\bullet$} % Default symbol for an internal node % Tree edges connecting to the default node symbol % will go to it's center. Other tree edges will be % chopped off at a node's bounding box. % % % Here's an example that changes the parameters in the middle of the tree: % % \subtreesep=15pt\levelsep=40pt % \tree[\fbox{\subtreesep=5pt\levelsep=13pt\tree[o]{a}{a}}] % {b}{b} % % % You can get triangular subtrees by using \triangle which has the format % % \triangle[optional apex label]{width}{height} % % For example, % % \tree{\triangle[A]{2\subtreesep}{2\levelsep}} % {\tree{\triangle{\subtreesep}{\levelsep}} % {\tree{\fbox{}} % {\fbox{}}}} % % % Don't fiddle with the stuff that follows; it's fairly delicate. % % Working variables % \newdimen\halfsubtreesep % half the subtree separation % \newdimen\leftwd % width of left subtree \newdimen\rightwd % width of right subtree % \newcount\rootbullet % flag indicating if root is the default bullet \newdimen\rootwd % width of root \newdimen\rootht % height of root \newdimen\rootdp % depth of root % \newcount\leftrootbullet % flag indicating if left root is the default bullet \newdimen\leftrootht % height of left subtree's root \newdimen\leftrootwd % width of left subtree's root \newcount\rightrootbullet% flag indicating if right root is the default bullet \newdimen\rightrootht % height of right subtree's root \newdimen\rightrootwd % width of right subtree's root % \newdimen\root % distance of root midpointfrom left edge of tree \newdimen\leftroot % distance of root midpoint of left subtree % from left edge of tree \newdimen\rightroot % distance of root midpoint of right subtree % from left edge of tree % \newcount\leafnode % flag indicating if subtree just placed is a leaf % \newdimen\rootxpos % x-cooordinate of the root midpoint \newdimen\leftrootpos % position of the root of the left subtree \newdimen\rightrootpos % position of the root of the right subtree \newdimen\leftpos % position of the NE corner of the left subtree \newdimen\rightpos % position of the NW corner of the right subtree % \newbox\rootnode % the root node, as placed \newbox\leftsubtree % the left subtree, as placed \newbox\rightsubtree % the right subtree, as placed % \newdimen\xa % (\xa,\ya) = coordinates of the point on the root \newdimen\ya % node at which to connect the line to a child \newdimen\xb % (\xb,\yb) = coordinates of the point on the child \newdimen\yb % at which to connect the line to the parent % \let\ifnextchar=\@ifnextchar% \def\tree{\ignorespaces% \def\tree{\ifnextchar[{\treey}{\treex}}% % \setdimensionmode% \setlinear% % \@ifnextchar[{\treey}{\treex}% }% % \long\def\treex#1#2{\itree{#1}{#2}{\nodesymbol}} % use default node symbol \long\def\treey[#1]#2#3{\itree{#2}{#3}{#1}} % use specified node symbol % \long\def\itree#1#2#3{\ignorespaces % #1=left, #2=right, #3=root % \halfsubtreesep=\subtreesep % Do this calculation for each node so its... \divide\halfsubtreesep by 2 % ...value can vary throughout the tree % \ignorespaces% % % Recursively draw nonempty left subtree % \ifx ~#1~\ignorespaces% \else% \leafnode=1 % Assume left subtree is a leaf \setbox\leftsubtree=\hbox{#1}\ignorespaces \leftwd=\wd\leftsubtree% \leftroot=\root% \leftrootbullet=\rootbullet% \leftrootht=\rootht% \leftrootwd=\rootwd% \ifnum \leafnode=1% \leftroot=\leftwd% \divide\leftroot by 2% \leftrootbullet=0% \leftrootht=\ht\leftsubtree% \advance\leftrootht by \dp\leftsubtree% \leftrootwd=\leftwd% \fi% \fi% % % Recursively draw nonempty right subtree % \ifx ~#2~\ignorespaces% \else% \leafnode=1 % Assume right subtree is a leaf \setbox\rightsubtree=\hbox{#2}\ignorespaces% \rightwd=\wd\rightsubtree% \rightroot=\root% \rightrootbullet=\rootbullet% \rightrootht=\rootht% \rightrootwd=\rootwd% \ifnum \leafnode=1% \rightroot=\rightwd% \divide\rightroot by 2% \rightrootbullet=0% \rightrootht=\ht\rightsubtree% \advance\rightrootht by \dp\rightsubtree% \rightrootwd=\rightwd% \fi% \fi% % % In the case of empty subtrees, give artificial values for those empty % trees so that the later calculations are done properly. % \ifx ~#1#2~\ignorespaces % Both subtrees empty \rightroot=0pt% \leftroot=-\halfsubtreesep% \leftwd=-\halfsubtreesep% \else\ifx ~#1~\ignorespaces % Left subtree empty, right subtree not empty \leftroot=\rightroot% \advance\leftroot by -\subtreesep% \leftwd=-\subtreesep% \else\ifx ~#2~\ignorespaces % Right subtree empty, left subtree not empty \rightroot=\leftroot% \advance\rightroot by -\leftwd% \fi\fi\fi% % % With the subtrees done, now do the root node % \setbox\rootnode=\hbox{#3}\ignorespaces% \global\rootwd=\wd\rootnode% \global\rootht=\ht\rootnode% \global\advance\rootht by \dp\rootnode% \ifx \nodesymbol#3\ignorespaces% \global\rootbullet=1% \else\ignorespaces% \global\rootbullet=0% \fi% % % Find distance of the root midpoint from left edge of the tree % \global\root=\leftroot% \global\advance\root by \rightroot% \global\advance\root by \leftwd% \global\advance\root by \subtreesep% \ifdim \root<\rootwd \global\root=\rootwd \fi% \global\divide\root by 2% % % Indicate this root and all its ancestors are not a leaves % \global\leafnode=0% % % Find positions of the root and those of the roots of the subtrees % \leftrootpos=\leftroot% \advance\leftrootpos by -\leftwd% \advance\leftrootpos by -\halfsubtreesep% % \rightrootpos=\rightroot% \advance\rightrootpos by \halfsubtreesep% % \rootxpos=\leftrootpos% \advance\rootxpos by \rightrootpos% \divide\rootxpos by 2% % \leftpos=0pt% \advance\leftpos by \leftrootht% \divide\leftpos by 2% % \rightpos=0pt% \advance\rightpos by \rightrootht% \divide\rightpos by 2% % % Now the root is a box of width \rootwd and total height \rootht, centered % at (\rootxpos,\levelsep); the root of the left subtree is a box of % width \leftrootwd and total height \leftrootht, centered at % (\leftrootpos,0); the root of the right subtree is a box of width % \rightrootwd and total height \rightrootht, centered at (\rightrootpos,0). % % \beginpicture % \put {\box\rootnode} at {\rootxpos} {\levelsep} % Draw the root % \ifx ~#1~\else % Draw the left subtree \put {\box\leftsubtree} [rt] at {-\halfsubtreesep} {\leftpos} \xa=\rootxpos% \ya=\levelsep% \ifnum\rootbullet=0% \chop{\rootxpos}{\levelsep}{-\rootwd}{\rootht}{\leftrootpos}{0}% {\xa}{\ya}% \fi% \xb=\leftrootpos% \yb=0pt% \ifnum\leftrootbullet=0% \chop{\leftrootpos}{0}{\leftrootwd}{-\leftrootht}{\rootxpos}{\levelsep}% {\xb}{\yb}% \fi% \plot {\xa} {\ya} {\xb} {\yb} /% \fi% % \ifx ~#2~\else % Draw the right subtree \put {\box\rightsubtree} [lt] at {\halfsubtreesep} {\rightpos} \xa=\rootxpos% \ya=\levelsep% \ifnum\rootbullet=0% \chop{\rootxpos}{\levelsep}{\rootwd}{\rootht}{\rightrootpos}{0}% {\xa}{\ya}% \fi% \xb=\rightrootpos% \yb=0pt% \ifnum\rightrootbullet=0% \chop{\rightrootpos}{0}{-\rightrootwd}{-\rightrootht}{\rootxpos}% {\levelsep}{\xb}{\yb}% \fi \plot {\xa} {\ya} {\xb} {\yb} /% \fi% % % Draw the bottom of the triangle, when appropriate. % \ifx#1. \ifx#2. \plot {\leftrootpos} {0pt} {\rightrootpos} {0pt} / \fi\fi% % \endpicture% }% % \long\def\triangle{\ifnextchar[{\triangley}{\trianglex}}% \long\def\trianglex#1#2{\itriangle{#1}{#2}{}} % use empty apex symbol \long\def\triangley[#1]#2#3{\itriangle{#2}{#3}{#1}} % use specified apex symbol \long\def\itriangle#1#2#3{% A triangle #1 wide and #2 high, #3 at apex \subtreesep=#1% \levelsep=#2% \tree[#3]{.}{.}% }% % \newcount\x% Scratch counters used in the computations of \chop \newcount\y% to find the location on the border of a node's bounding \newcount\a% box at which to attach a line aimed at a target point \newcount\b% from the center of the box. \newcount\c% \newcount\d% It would be better to do all these calculation in dimen's \newcount\e% istead of counters, but so many dimen's are used above \newcount\f% that to do so would make running out dimen's very probable. \newcount\g% \newcount\h% Forgive us for not explaining the following computations; they're \newcount\l% based on elementary analytical geometry. % \def\chop#1#2#3#4#5#6#7#8{\ignorespaces% % (#1,#2) = coordinates of center of bounding box % #3 x #4 = width x height of bounding box % (#5,#6) = coordinates of target point % (#7,#8) = coordinates of computed intersection % point % \a=#1\divide \a by 10000% Scale down to prevent arithmetic overflow. \b=#2\divide \b by 10000% \c=#3\divide \c by 10000% \d=#4\divide \d by 10000% \e=#5\divide \e by 10000% \f=#6\divide \f by 10000% % \l=-\f\advance\l by \b% %% \y=-\d% \divide \y by 2% \advance\y by \b% %% \g=\c% \divide \g by 2% \advance\g by \a% %% \x=-\a% \advance\x by \e% \multiply\x by \d% \divide\x by \l% \divide\x by 2% \advance \x by \a% %% \count255=-\a% \advance\count255 by \e% \multiply\count255 by 2% \h=-\c% \multiply \h by \l% \divide \h by \count255% \advance \h by \b% % \ifnum #5>#1% \ifnum\x>\g\else\g=\x\h=\y\fi% \else% \ifnum\x<\g\else\g=\x\h=\y\fi% \fi% \multiply\g by 10000% Scale back up \multiply\h by 10000% \global#7=\g sp% \global#8=\h sp% }% -- Professor Edward M. Reingold reingold@cs.uiuc.edu Department of Computer Science (217) 333-6733 University of Illinois at Urbana-Champaign 1304 W. Springfield Ave. Urbana, IL 61801-2987 USA