# latex command for undetermined number of parameters

I am trying to use a smart way to typeset some diagrams as these single stranded diagrams.

But I would like to do two more things, one is to allow n of them to be stacked together as

and be able to draw multi-stranded diagrams as

Now I already have the command for single-stranded diagrams as

\newcommand{\Planar}[3]{
\begin{tikzpicture}[->,>=stealth'] % if arrow is ture, add these commands
\draw[color=red] (0,0) rectangle (#1*0.3+0.3,0.7); % draw the outer box according to number of dots
\newcounter{dotnumber}
\foreach \dot in {1,...,#1}{ % draw the dots
\filldraw (0.3*\dot,0) circle [radius=1pt];
\filldraw (0.3*\dot,0.7) circle [radius=1pt];
}
\draw (0.3*#2,0) .. controls +(0,0.2) and +(0,-0.2) .. (0.3*#3,0.7); % draw the line
\end{tikzpicture}
}


But I am looking for something like this

\Planar[number_of_dots][number_of_levels][first_strand_starting][first_strand_ending][...][last_strand_starting][last_strand_ending][secondlevelstrands...]


That produces the above diagrams with commands as

\Planar[3][1][1]...,
\Planar[3][level=2][2][2][2][3]
|this is the number of levels
\Planar[3][1][2][3][1][3][3]
|this is the starting dot of the first line


I know that this is probably a huge macro and my way of defining the command is hideous, but I would appreciate at least some directions of where to go next.

• Welcome! You can just use pgf keys for that. Then the syntax will be \Planar{ndots=7,nlevels=5,...}, where each key can have an initial/default value. – user194703 Mar 2 '20 at 19:02
• – AndréC Mar 3 '20 at 2:12

Here is start. Rather than using levels I would define a single pic (see section 18.2 of the tikz manual), that draws one planar diagram and then you can stack them. The first argument of the pic should give the number of dots on the bottom line and the second argument should be a comma separated list of x/y pairs for the strings. This way you can use:

   \begin{tikzpicture}
\draw (0,0) pic{planar=[3]{1/1}};
\draw (3,0) pic{planar=[3]{1/2}};
\draw (6,0) pic{planar=[3]{1/3}};
\end{tikzpicture}


to draw the diagrams

To stack the diagrams you then put two pics on "top" of each other, so that

   \begin{tikzpicture}
\draw(0,0) pic{planar=[3]{2/2}};
\draw(0,0.7) pic{planar=[3]{2/3}};
\end{tikzpicture}


produces:

Finally, here is an example with multiple strings and four dots: the code

   \begin{tikzpicture}
\draw(0,0) pic{planar={4}{1/2, 3/1, 3/3}};
\end{tikzpicture}


produces (the non-planar diagram):

I haven't added a command for the labels, partly because I don't immediately see a good syntax for this and partly because when I draw such diagrams I never label the strings:)

Here is the full code:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}
\tikzset{pics/planar/.style 2 args = {
code = {
\draw[color=red] (0,0) rectangle (#1*0.3+0.3,0.7);
\foreach \dot in {1,...,#1}{ % draw the dots
\filldraw (0.3*\dot,0) circle [radius=1pt];
\filldraw (0.3*\dot,0.7) circle [radius=1pt];
}
% draw the lines
\foreach \x/\y in {#2}
\draw[->,>=stealth](0.3*\x,0) .. controls +(0,0.2) and +(0,-0.2) .. (0.3*\y,0.7);
}
}
}

\begin{document}

\begin{tikzpicture}
\draw (0,0) pic{planar={3}{1/1}};
\draw (2,0) pic{planar={3}{1/2}};
\draw (4,0) pic{planar={3}{1/3}};
\end{tikzpicture}

\begin{tikzpicture}
\draw(0,0) pic{planar={3}{2/2}};
\draw(0,0.7) pic{planar={3}{2/3}};
\end{tikzpicture}

\begin{tikzpicture}
\draw(0,0) pic{planar={4}{1/2, 3/1, 3/3}};
\end{tikzpicture}

\end{document}


As you see I have really just repackaged the code from the OP. Personally, I would replace 0.3 and 0.7 above with 0 and 1 and then rescale the whole environment as this is likely to make your life a little easier in the long run.

Edit

Here is a "fancier" \PlanarDiagram macro that accepts a comma separated list of coma separated x/y-pairs to produce products of the corresponding diagrams. For example, the code

\PlanarDiagram(5){{1/2}}                  \quad
\PlanarDiagram(4){{1/2, 3/1, 3/3}, {1/1, 2/3, 3/3}}


produces the output

Notice that there is an optional argument, which is enclosed in (...), that gives the number of dots. This another optional argument that you can use to configure the underlying tikzpicture1 environment and the pic. So, for example,

\PlanarDiagram[scale=0.5, draw=blue](4){{1/2, 3/1, 3/3}, {1/1, 2/3, 3/3}}


produces:

Here is the updated code:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}
\tikzset{pics/planar/.style 2 args = {
code = {
\draw[color=red] (0,0) rectangle (#1*0.3+0.3,0.7);
\foreach \dot in {1,...,#1}{ % draw the dots
\filldraw (0.3*\dot,0) circle [radius=1pt];
\filldraw (0.3*\dot,0.7) circle [radius=1pt];
}
% draw the lines
\foreach \x/\y in #2
\draw[->,>=stealth](0.3*\x,0) .. controls +(0,0.2) and +(0,-0.2) .. (0.3*\y,0.7);
}
}
}

\usepackage{xparse}
\NewDocumentCommand\PlanarDiagram{ O{} D(){3} m }{%
\begin{tikzpicture}[#1]
\foreach \diag [count=\c] in {#3} {
\draw(0,\c*0.7) pic[#1]{planar={#2}{\diag}};
}
\end{tikzpicture}%
}

\begin{document}

\PlanarDiagram(4){{1/2, 3/1, 3/3}, {1/1, 2/3, 3/3}}

\PlanarDiagram[scale=0.7, draw=blue](4){{1/2, 3/1, 3/3}, {1/1, 2/3, 3/3}}

\end{document}

• Wow that's super great work! It's kind of overwhelming using the tikz library in the beginning but your answer is a great example for me to work on! Thanks a lot! – water liu Mar 3 '20 at 16:43

As an exercise, I took a crack at this with metapost. The syntax is:

planar(n,m)("connections")


where n is the number of points per level, m is the number of levels, and connections is a list of the form

level 1 arrows | level 2 arrows | level 3 arrows |...


where the arrows within each level are given as a comma separated list of ordered pairs. For example

planar(3,1)("(1,1),(2,3),(3,2)");


produces

and

planar(3,2)("(1,1),(2,3),(3,2)|(3,1),(1,3),(2,2)");


produces

Code:

\documentclass{article}
\usepackage{luamplib}
\mplibforcehmode
\begin{document}
\begin{mplibcode}
ux:=1cm; % horizontal scale
uy:=2cm; % vertical scale
ds:=.15*ux; % dot size

def planar(expr pts,levels)(text connections)=
clearxy; save k,l,n;
x=(pts+1)*ux; y=levels*uy; % max x, max y
for i=0 upto levels:
draw (origin--(x,0)) shifted (0,i*uy) withcolor red; % draw horizontal bars
for j=1 upto pts: drawdot (j*ux,i*uy) withpen pencircle scaled ds; endfor; % draw dots
endfor;
draw origin--(0,y) withcolor red; % draw left vertical bar
draw (x,0)--(x,y) withcolor red; % draw right vertical bar
l=length(connections); n=k=0;
for i=0 upto l:
if (substring(i,i+1) of connections="|") or (i=l): % find separators
for p=scantokens(substring(k,i) of connections): % iterate through list up to separator
drawarrow ((xpart p)*ux,n*uy){up}..{up}((ypart p)*ux,(n+1)*uy)
cutafter fullcircle scaled (ds+1) shifted ((ypart p)*ux,(n+1)*uy);
endfor;
k:=i+1; % pickup after separator
n:=n+1; % increase level
fi;
endfor;
enddef;

beginfig(0);
planar(3,2)("(1,1),(2,3),(3,2)|(3,1),(1,3),(2,2)");
endfig;
\end{mplibcode}
\end{document}

• Wow this website never stops surprising me! This is a very elegant implementation as well! Thanks! – water liu Mar 3 '20 at 23:02