2
\$\begingroup\$

I have some diagrams I'm drawing using tikz and I'd like to make a macro to draw them for me.

I will describe it fully but here are some examples so you can follow along:

Example drawings

The diagram has a number of nodes which we can be split into two groups, "main nodes" and "detour nodes" main nodes are drawn in a column the left and are drawn with solid borders. Detour nodes are drawn in a column to the right with dashed borders. Main nodes are evenly spaced and each is connected to the node above it with a solid line. detour nodes are not necessarily spaced evenly, they are offset by a half step from the main nodes. Each detour node connects via dashed line to the main node closest above it and closest below it if such exists. Detours do not connect to each other.

Each node has a label (in the picture this is always 3 characters) and main nodes labels are to their left while detour nodes labels are to their right.

Method

I'm using the packages xparse and tikz to solve this.

I don't know tex, so when it came to implementing this I had a bit of a tough time. I went with a recursive method since that seemed more natural to me. The main function (\trackdiagram) is passed two arguments containing the labels separated by ;. The first contains the labels for the main nodes and the second for the detour nodes.

This main function then passes off each "list" to a recursive method which builds the column. These methods are both recursive and stateful maintaining a counter to indicate the current position.

\makeatletter
\newcommand\providecounter[1]{
 \@ifundefined{c@#1}{%
 \newcounter{#1}
 }{}
}
\makeatother
\NewDocumentCommand\trackdiagram{mm}{
 \providecounter{mtHeight}
 \providecounter{dtHeight}
 \setcounter{mtHeight}{0}
 \setcounter{dtHeight}{0}
 \begin{tikzpicture}[fill=white,thick,circle,minimum size=2.1em]
\maintrack[#1;]
\detours[#2;]
 \end{tikzpicture}
}
\NewDocumentCommand\maintrack{u{[}u{;}u{]}}{
 \providecounter{previous}
 \setcounter{previous}{\value{mtHeight}}
 \addtocounter{mtHeight}{-1}
 \def\curcell{maincell\arabic{mtHeight}}
 \node[
 draw,
 fill,
 label={left:\textbf{#2}}
 ](\curcell)at(0,\arabic{mtHeight}){};
 \ifnum\value{mtHeight}<-1{%
 \draw(\curcell)node[draw,fill]{}--(maincell\arabic{previous});
 }\else\fi
 \ifx&#3&\else
 \maintrack[#3]
 \fi
}
\NewDocumentCommand\detours{u{[}u{;}u{]}}{
 \providecounter{previous}
 \setcounter{previous}{\value{dtHeight}}
 \addtocounter{dtHeight}{-1}
 \def\curcell{dcell\arabic{dtHeight}}
 \ifx&#2&\else
 \node[
 draw,
 dashed,
 fill,
 label={right:\textbf{#2}}
 ](\curcell)at(.8,\arabic{dtHeight}+0.5){};
 \ifnum\value{dtHeight}<\value{mtHeight}\else
 \draw[dashed](\curcell)--(maincell\arabic{dtHeight});
 \fi
 \ifnum\value{dtHeight}<-1{
 \draw[dashed](maincell\arabic{previous})--(\curcell);
 }\else\fi
 \fi
 \ifx&#3&\else
 \detours[#3]
 \fi
}

Here's how the examples are invoked:

\trackdiagram{UHM;SCR;BAT;INJ;SKD}{AUR;USC;;UNT}
\trackdiagram{SPM;MOV;IFN}{;AKN}
\trackdiagram{TAK;CAM;DOF}{;;BRY;CHU}
\trackdiagram{JMP}{JIF;DAL}

I am not committed to this particular invocation, it is entirely a product of my abilities, not of any particular desire for this particular API. Thus this is perfectly open to review as well.

Review

Because I don't have a background in tex I feel that this probably is pretty autodidactic. There are probably more sensible solutions to a lot of the problems I solved here that someone experienced with latex would use. And this is what I would like to cover with a code-review.

I don't want to use any new packages. So suggestions which involve using fancy new packages are not helpful to me.

asked Jul 3, 2022 at 14:47
\$\endgroup\$
6
  • \$\begingroup\$ The first thing I'd do would be to remove the \providecounter instructions and allocate the counters right away with \newcounter. \$\endgroup\$ Commented Jul 4, 2022 at 21:26
  • \$\begingroup\$ @egreg Why? That seems like it will prevent it from being modular. \$\endgroup\$ Commented Jul 4, 2022 at 22:08
  • \$\begingroup\$ If you need counters, allocate them. On the other hand, I think you might even dispense from them. The definitions are not the best: you should use \SplitArgument rather than the deprecated u argument type. \$\endgroup\$ Commented Jul 4, 2022 at 22:14
  • \$\begingroup\$ @egreg Since there is no way to properly cleanup the counters if I allocate them unconditionally then it crashes on reuse. \$\endgroup\$ Commented Jul 4, 2022 at 22:16
  • \$\begingroup\$ Would you please add some examples of use? \$\endgroup\$ Commented Jul 5, 2022 at 8:30

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.