10

I am trying to simplify my work with some latex coding, where the intention is to generate two types of diagrams based on the input. The first type is diagrams with lines only going up

enter image description here

and the second is where the lines are allowed to go back like these (draw with PowerPoint):

enter image description here

I already have the working code for the first type of diagrams here where the diagrams' dots on both top and bottom are numbered from left to right 1 to n. Below is a MWE of the 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(5){{1/2}}                  \quad
   \PlanarDiagram{{1/2,2/2}, {2/3,3/1}}      \quad
   \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}

My hope is that if I number the dots on the top positively from 1 to n and number the dots at the bottom negatively from -1 to -n, I can separate the two types of lines by the commands' input so the command could be like this

\NewPlanarDiagram{{-1/-4,-2/-3,1/2,3/4}} % diagram on the left
\NewPlanarDiagram{{-1/3,-2/-3,1/2,4/4}} % diagram on the right

I could do something like this to the macro in the for loop

\foreach \x/\y in #2
                \ifnum \x < 0 
                    \let \signx=-1 
                \else 
                    \let \signx=1 
                \fi
                \ifnum \y < 0 
                    \let \signy=-1 
                \else 
                    \let \signy=1 
                \fi
                \let \xysign= \multiply\signx by \signy
                \ifnum\xysign<0
                    \draw(0.3*\x,0.35+\signx*0.35) .. controls +(0,0.2) and +(0,-0.2) .. (0.3*\y,0.35+\signy*0.35);
                \else
                    \draw(0.3*\x,0.35+\signx*0.35) .. controls +(0,0.2) and +(0,-0.2) .. (0.3*\y,0.35+\signy*0.35);
                \fi

but there are numerous error messages coming out of it. Any ideas?

10

I would just want to use one pic and upgrade it in a way that the original usage still works. On the technical side, you can introduce a test integer

\pgfmathtruncatemacro{\itest}{(\XX<0)+2*(\YY<0)}

which will assume the values 0, 1, 2 or 3 depending on whether both signs are positive, only the sign of \YY is positive, only the sign of \XX is positive or both are negative, respectively. Then you can work with a simple \ifcase.

\documentclass{article}
\usepackage{tikz}
\newif\ifPlanarDiagamShowLabels
\usetikzlibrary{arrows.meta,bending}
\tikzset{pics/planar diagram/.style={code={
            \tikzset{planar diagram/.cd,#1}%
            \def\pv##1{\pgfkeysvalueof{/tikz/planar diagram/##1}}%
            \draw[/tikz/planar diagram/frame] ({-(\pv{n}+1)*\pv{x}/2},-\pv{y}/2) rectangle 
                ({(\pv{n}+1))*\pv{x}/2},\pv{y}/2);
            \ifPlanarDiagamShowLabels
              \path foreach \XX in {1,...,\pv{n}}
              {({-(\pv{n}+1)*\pv{x}/2+\XX*\pv{x}},-\pv{y}/2)
                   node[circle,fill,inner sep=1pt,label=below:$\XX$] (-b-\XX){}
               ({-(\pv{n}+1)*\pv{x}/2+\XX*\pv{x}},\pv{y}/2)
                   node[circle,fill,inner sep=1pt,label=above:$\XX$] (-t-\XX){}};
            \else
              \path foreach \XX in {1,...,\pv{n}}
              {({-(\pv{n}+1)*\pv{x}/2+\XX*\pv{x}},-\pv{y}/2)
                   node[circle,fill,inner sep=1pt] (-b-\XX){}
               ({-(\pv{n}+1)*\pv{x}/2+\XX*\pv{x}},\pv{y}/2)
                   node[circle,fill,inner sep=1pt] (-t-\XX){}};
            \fi
            \edef\localconnections{\pv{connections}}
             \foreach \XX/\YY in \localconnections{%
             \ifnum\XX=\YY
              \typeout{Loops are not implemented (yet).}
             \else 
              \pgfmathtruncatemacro{\itest}{(\XX<0)+2*(\YY<0)}
              \ifcase\itest % both >0
               \draw[planar diagram/arrow] (-t-\XX) to[out=-90,in=-90] (-t-\YY);
              \or % \YY >0
               \draw[planar diagram/arrow] (-b\XX) to[out=90,in=-90] (-t-\YY);
              \or % \XX >0
               \draw[planar diagram/arrow] (-t-\XX) to[out=-90,in=90] (-b\YY);
              \or % both <0
               \draw[planar diagram/arrow] (-b\XX) to[out=90,in=90] (-b\YY);
              \fi
             \fi
            }
    }},planar diagram/.cd,n/.initial=5,x/.initial=0.3,y/.initial=0.7,
    show labels/.is if=PlanarDiagamShowLabels,frame/.style={},
    connections/.initial={1/1},arrow/.style={-{Stealth[bend]}}
}

\begin{document}
\begin{tikzpicture}
 \path (0,0) pic[scale=2]{planar diagram={n=4,
 arrow/.style={thick,cyan},frame/.style={draw=red},
 connections={-1/-4,-2/-3,1/2,3/4}}}
 (5,0)  pic[scale=2]{planar diagram={n=4,arrow/.style={thick,cyan},
 connections={-1/3,-2/-3,1/2,-4/4}}}
 (0,-3) pic[scale=2]{planar diagram={n=5,
 connections={-1/3,-2/4,-3/1,-4/2,-5/5}}};
\end{tikzpicture}
\end{document}

enter image description here

| improve this answer | |
8

The main problem in the proposed loop is that \let doesn't work this way: the right-hand side must be a single token. You could have used \def or \pgfmathsetmacro. Second problem: your two alternative code paths are identical. In the same spirit as what you wrote, I'd use something like this for the loop:

\foreach \x/\y in #2 {
  \pgfmathtruncatemacro{\signx}{\x < 0 ? -1 : 1}
  \pgfmathtruncatemacro{\signy}{\y < 0 ? -1 : 1}
  \pgfmathsetmacro{\myFactor}
    {(1+0.2*(1+\signx*\signy)*abs(abs(\x)-abs(\y)))}
  \draw
    ({0.3*abs(\x)}, 0.35+\signx*0.35)
    .. controls +(0, -\myFactor*\signx*0.2) and
                +(0, -\myFactor*\signy*0.2) ..
    ({0.3*abs(\y)}, 0.35+\signy*0.35);
}

I also rewrote your \PlanarDiagram macro as \NewPlanarDiagram using more expl3 stuff, but that wasn't necessary in the end (look at the history of the answer if you are interested). I commented out one #1, though, because you passed it both to the tikzpicture and to the pic. Also, you wrote 4/4 instead of -4/4 for the second proposed diagram. The \NewPlanarDiagram macro is called this way in my example:

\NewPlanarDiagram(4){{-1/-4,-2/-3,1/2,3/4}, {-1/3,-2/-3,1/2,-4/4}}

Full example:

\documentclass{article}
\usepackage{xparse}
\usepackage{tikz}

\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 {
        \pgfmathtruncatemacro{\signx}{\x < 0 ? -1 : 1}
        \pgfmathtruncatemacro{\signy}{\y < 0 ? -1 : 1}
        \pgfmathsetmacro{\myFactor}
          {(1+0.2*(1+\signx*\signy)*abs(abs(\x)-abs(\y)))}
        \draw
          ({0.3*abs(\x)}, 0.35+\signx*0.35)
          .. controls +(0, -\myFactor*\signx*0.2) and
                      +(0, -\myFactor*\signy*0.2) ..
          ({0.3*abs(\y)}, 0.35+\signy*0.35);
      }
    }
  }
}

\NewDocumentCommand \NewPlanarDiagram { O{} D(){3} m }
  {%
      \begin{tikzpicture}%[#1] commented out: already passed to the pic...
        \foreach \diag [count=\c] in {#3} {
          \draw(0, -\c*0.9) pic[#1] {planar={#2}{\diag}};
        }
     \end{tikzpicture}%
  }

\begin{document}

\NewPlanarDiagram(4){{-1/-4,-2/-3,1/2,3/4}, {-1/3,-2/-3,1/2,-4/4}}

\end{document}

enter image description here

| improve this answer | |
  • Thanks for your answer and it does provide a solution to my problems. I chose Schrodinger's Cat's answer because I think that's seemed to be a more orthodox way to do it IMO. – water liu Apr 3 at 11:55
  • I understand, no problem. – frougon Apr 3 at 12:16
  • I found that \begin{tikzpicture}%[#1] cannot be commented out. It will make the stacked diagram disconnect if a scale parameter is passed to it. – water liu Apr 3 at 18:47
  • Well, this is a matter of choice depending on how you intend to use the code. If it makes sense to pass the options you'll use in the two places we have in the \NewPlanarDiagram macro, feel free to do it. I can't predict myself which particular options you are going to pass... – frougon Apr 3 at 20:46
  • Clean solutions to this (IMHO) would be 1) either use two separate arguments for these options, or 2) (better because it scales well) define an argument with key=value syntax that accepts two keywords: pic opts={...} and tikzpicture opts={...}. This way, both sets of options would be independent. – frougon Apr 3 at 21:43
2

For the sake of continuity, here's an update on my answer to your earlier question that adds some if/then's to handle the new situation.

enter image description here

Compile with lualatex:

\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
                if (xpart p<0) and (ypart p>0): % between levels
                    drawarrow (abs(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);
                elseif (xpart p<0) and (ypart p<0): % bottom level
                    draw (abs(xpart p)*ux,n*uy){up}..{down}(abs(ypart p)*ux,n*uy) ;
                elseif (xpart p>0) and (ypart p>0): % top level
                    draw (abs(xpart p)*ux,(n+1)*uy){down}..{up}(abs(ypart p)*ux,(n+1)*uy);
                fi;
            endfor;
        k:=i+1; % pickup after separator
        n:=n+1; % increase level
        fi;
    endfor;
enddef;

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

I modified a little bit of Schrodinger's Cat's answer to allow the circle to look nicer and ended up using it. I'll post it here in case anyone stumbled upon it.

\usepackage{tikz}
\usetikzlibrary{braids,backgrounds,arrows.meta,fit}

\tikzset{pics/planar diagram/.style={code={
            \tikzset{planar diagram/.cd,#1}%
            \def\pv##1{\pgfkeysvalueof{/tikz/planar diagram/##1}}%
                \draw[/tikz/planar diagram/frame] ({-(\pv{n}+1)*\pv{x}/2},-\pv{y}/2) rectangle ({(\pv{n}+1))*\pv{x}/2},\pv{y}/2);
                \path foreach \XX in {1,...,\pv{n}}
                {({-(\pv{n}+1)*\pv{x}/2+\XX*\pv{x}},-\pv{y}/2)
                    node[circle,fill,inner sep=0.7pt] (-b-\XX){}
                    ({-(\pv{n}+1)*\pv{x}/2+\XX*\pv{x}},\pv{y}/2)
                    node[circle,fill,inner sep=0.7pt] (-t-\XX){}};
            \edef
            \localconnections{\pv{connections}}
            \foreach \XX/\YY in \localconnections{%
                \ifnum\XX=\YY
                \typeout{Loops are not implemented (yet).}
                \else 
                \pgfmathtruncatemacro{\itest}{(\XX<0)+2*(\YY<0)}
                \ifcase\itest % both >0
                \draw[planar diagram/arrow] (-t-\XX) .. controls +(0,-0.2*\pv{y}+0.025*\XX/\pv{x}-0.025*\YY/\pv{x}) and +(0,-0.2*\pv{y}+0.025*\XX/\pv{x}-0.025*\YY/\pv{x}) .. (-t-\YY);
                \or % \YY >0
                \draw[planar diagram/arrow] (-b\XX) .. controls +(0,0.2) and +(0,-0.2) .. (-t-\YY);
                \or % \XX >0
                \draw[planar diagram/arrow] (-t-\XX) .. controls +(0,-0.2) and +(0,0.2) .. (-b\YY);
                \or % both <0
                \draw[planar diagram/arrow] (-b\XX) .. controls +(0,0.2*\pv{y}+0.025*\XX/\pv{x}-0.025*\YY/\pv{x}) and +(0,0.2*\pv{y}+0.025*\XX/\pv{x}-0.025*\YY/\pv{x}) .. (-b\YY);
                \fi
                \fi
            }
    }},
    planar diagram/.cd,n/.initial=5,x/.initial=0.3,y/.initial=0.7,frame/.style={draw=red},connections/.initial={1/1},arrow/.style={-{stealth}}
}

\usepackage{xparse}
\NewDocumentCommand\NewPlanarDiagram{ O{} D(){3} m }{%
    \begin{tikzpicture}[#1]
    \foreach \diag [count=\c] in {#3} {
        %       \draw(0,\c*0.7) pic[#1]{planar={#2}{\diag}};
        \draw (0,\c*0.8) pic[#1]{planar diagram={n=#2,connections={\diag},arrow/.style={black},x=0.3,y=0.8}};
    }
    \end{tikzpicture}%
}

\NewDocumentCommand\planarDiagram{ O{} D(){3} m }{%
    \begin{scope}[#1]
        \foreach \diag [count=\c] in {#3} {
            \draw (0,\c*0.7) pic[#1]{planar diagram={n=#2,connections={\diag}}};
        }
    \end{scope}
}

\NewDocumentCommand\PlanarDiagram{ O{} D(){3} m }{%
    \begin{tikzpicture}[#1]
    \foreach \diag [count=\c] in {#3} {
        \draw (0,\c*0.7) pic[#1]{planar diagram={n=#2,connections={\diag}}};
    }
    \end{tikzpicture}%
}
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.