5

I've defined a generic pressure vessel shape in Tikz with a varying number of connection ports. I want to define anchors for each connection port in a 'foreach' loop, but I can't get it to work properly. I've tried to follow the solution suggested in \pgfdeclareshape: how to define anchors in a loop? , but it doesn't work. Here's my code:

\documentclass{standalone}
\usepackage{tikz}
\usepackage{ifthen}

\usetikzlibrary{positioning}

\makeatletter

\pgfkeys{/tikz/.cd,% to set the path
    width/.initial=1,
    width/.store in = \vesselwidth,
    width/.get = \vesselwidth,
    height/.initial=2,
    height/.store in = \vesselheight,
    height/.get = \vesselheight,
    bottom connection/.initial=false,
    bottom connection/.get = \bconnection,
    bottom connection/.store in = \bconnection,
    top connection/.initial=false,
    top connection/.get = \tconnection,
    top connection/.store in = \tconnection,    
    connection size/.initial=0.15,
    connection size/.store in = \consize,
    connection size/.get = \consize,
    right connections/.initial={},
    right connections/.get = \rconnections,
    right connections/.store in = \rconnections,
    left connections/.initial={},
    left connections/.get = \lconnections,
    left connections/.store in = \lconnections,
}

\tikzset{minimum width= \vesselwidth cm}
\tikzset{minimum height= \vesselheight cm}

\pgfdeclareshape{column3}{

    \inheritsavedanchors[from=rectangle]
    \inheritanchorborder[from=rectangle]
    \inheritanchor[from=rectangle]{center}
    \inheritanchor[from=rectangle]{south}
    \inheritanchor[from=rectangle]{west}
    \inheritanchor[from=rectangle]{east}
    \inheritanchor[from=rectangle]{south east}
    \inheritanchor[from=rectangle]{north east}
    \inheritanchor[from=rectangle]{south west}
    \inheritanchor[from=rectangle]{north west}
    \anchor{north}{
        \pgf@process{\northeast}
        \pgf@xa=\pgf@x
        \pgf@ya=\pgf@y
        \advance\pgf@x by -\pgf@xa
        \advance\pgf@y by 0.667\pgf@xa
    }
    \anchor{south}{
        \pgf@process{\southwest}
        \pgf@xa=\pgf@x
        \pgf@ya=\pgf@y
        \advance\pgf@x by -\pgf@xa
        \advance\pgf@y by 0.667\pgf@xa
    }

    \backgroundpath{

        \southwest \pgf@xa=\pgf@x \pgf@ya=\pgf@y
        \northeast \pgf@xb=\pgf@x \pgf@yb=\pgf@y

        %  Construct main path
        \pgfpathmoveto{\pgfpoint{\pgf@xa}{\pgf@ya}}

        % Add connections on the left side
        \foreach \i [count=\ii] in \lconnections
        {
            \def\connectionpos{\pgfpartway{\i}{\pgfpoint{\pgf@xa}{\pgf@yb}}{\pgfpoint{\pgf@xa}{\pgf@ya}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{        0 cm}{-0.8*\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{-\consize cm}{-0.8*\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{-\consize cm}{-1.2*\consize cm}}}
            \pgfcoordinate{\tikz@fig@name-connection\ii w} {\pgfrelative{\connectionpos}{\pgfpoint{-\consize cm}{ 0 cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{-\consize cm}{ 1.2*\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{-\consize cm}{ 0.8*\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{        0 cm}{ 0.8*\consize cm}}}
        }
        \pgfpathlineto{\pgfpoint{\pgf@xa}{\pgf@yb}}

        % Add connection to top of vessel
        \pgfmathsetmacro{\angle}{acos(\consize*0.8/(0.5*\vesselwidth)}
        \pgfpatharc{0}{\angle}{\pgf@xa and -0.667\pgf@xa}

        \ifthenelse{\boolean{\tconnection}}
        {
            \def\connectionpos{\pgfpoint{0}{\pgf@yb+0.667*\pgf@xb}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{-0.8*\consize cm}{0}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{-0.8*\consize cm}{\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{-1.2*\consize cm}{\consize cm}}}
            \pgfcoordinate{\tikz@fig@name-connectiont} {\pgfrelative{\connectionpos}{\pgfpoint{0 cm}{ \consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{ 1.2*\consize cm}{\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{ 0.8*\consize cm}{\consize cm}}}  
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{ 0.8*\consize cm}{0}}}    
        }
        {\pgfpatharc{\angle}{180-\angle}{\pgf@xa and -0.667\pgf@xa}}
        \pgfpatharc{180-\angle}{180}{\pgf@xa and -0.667\pgf@xa}

        \pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@yb}}

        % Add connections on the right side
        \foreach \i [count=\ii] in \rconnections
        {
            \def\connectionpos{\pgfpartway{\i}{\pgfpoint{\pgf@xb}{\pgf@yb}}{\pgfpoint{\pgf@xb}{\pgf@ya}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{       0 cm}{ 0.8*\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{\consize cm}{ 0.8*\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{\consize cm}{ 1.2*\consize cm}}}
            \pgfcoordinate{\tikz@fig@name-connection\ii e} {\pgfrelative{\connectionpos}{\pgfpoint{\consize cm}{ 0 cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{\consize cm}{-1.2*\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{\consize cm}{-0.8*\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{       0 cm}{-0.8*\consize cm}}}
        }
        \pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@ya}}

        % Add connection to bottom of vessel
        \pgfmathsetmacro{\angle}{acos(\consize*0.8/(0.5*\vesselwidth)}
        \pgfpatharc{0}{\angle}{-\pgf@xa and 0.667\pgf@xa}

        \ifthenelse{\boolean{\bconnection}}
        {
            \def\connectionpos{\pgfpoint{0}{\pgf@ya+0.667*\pgf@xa}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{ 0.8*\consize cm}{0}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{ 0.8*\consize cm}{-\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{ 1.2*\consize cm}{-\consize cm}}}
            \pgfcoordinate{\tikz@fig@name-connectionb} {\pgfrelative{\connectionpos}{\pgfpoint{0 cm}{ -\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{-1.2*\consize cm}{-\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{-0.8*\consize cm}{-\consize cm}}} 
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{-0.8*\consize cm}{0}}}    
        }
        {\pgfpatharc{\angle}{180-\angle}{-\pgf@xa and 0.667\pgf@xa}}
        \pgfpatharc{180-\angle}{180}{-\pgf@xa and 0.667\pgf@xa}
        \pgfpathclose
    }
}

\makeatother

\begin{document}

    \begin{tikzpicture}[auto, node distance=1.5cm]

    % Draw the first tank 
    \node[column3,draw,thick,
    scale=1, width=1, height=2.6, 
    bottom connection=false, 
    top connection=false,
    right connections={0.10,0.90},
    left connections={0.5}] (col1) {};

    % Draw the second tank  
    \node[column3,draw,right=of col1.connection2e, anchor=connection1w, thick,
    scale=1, width=1, height=2.6, 
    bottom connection=false, 
    top connection=false,
    right connections={0.25,0.90},
    left connections={0.25}] (col2) {};

    \draw[->](col1-connection2e) -- (col2-connection1w);

    \end{tikzpicture}

\end{document}

Resulting in: Desired result

I want to avoid the usage of xshift/yshift by defining an anchor at each connection, which would allow me to write:

\begin{document}

    \begin{tikzpicture}[auto, node distance=1.5cm]

    % Draw the first tank 
    \node[column3,draw,thick,
    scale=1, width=1, height=2.6, 
    bottom connection=false, 
    top connection=false,
    right connections={0.10,0.90},
    left connections={0.5}] (col1) {};

    % Draw the second tank  
    \node[column3,draw,right=of col1.connection2e, anchor=connection1w, thick,
    scale=1, width=1, height=2.6, 
    bottom connection=false, 
    top connection=false,
    right connections={0.25,0.90},
    left connections={0.25}] (col2) {};

    \draw[->](col1.connection2e) -- (col2.connection1w);

    \end{tikzpicture}

\end{document}

How do I do this?

  • The package tqft does something similar to yours. I checked the code. It simply creates a new node and names it like main.anchor name. So there is nothing to do with \anchor. – Symbol 1 Nov 05 '15 at 07:10

1 Answers1

2

In your code

\foreach \i [count=\ii] in \lconnections

\lconnections is not a list. It is not a list even if you write

\foreach \i [count=\ii] in {\lconnections}

To make \foreach recognize this list, you have to expand \lconnections before the expansion of \foreach.

The canonical trick is the following

    \edef\pgf@marshal{\noexpand\foreach \noexpand\i [count=\noexpand\ii] in {\lconnections}}
    \pgf@marshal

Here, \noexpand protects \foreach/\i/\ii and \edef will expand \lconnections.

Here is the functioning code

\documentclass{standalone}
\usepackage{tikz}
\usepackage{ifthen}

\usetikzlibrary{positioning}

\makeatletter

\pgfkeys{/tikz/.cd,% to set the path
    width/.initial=1,
    width/.store in = \vesselwidth,
    width/.get = \vesselwidth,
    height/.initial=2,
    height/.store in = \vesselheight,
    height/.get = \vesselheight,
    bottom connection/.initial=false,
    bottom connection/.get = \bconnection,
    bottom connection/.store in = \bconnection,
    top connection/.initial=false,
    top connection/.get = \tconnection,
    top connection/.store in = \tconnection,    
    connection size/.initial=0.15,
    connection size/.store in = \consize,
    connection size/.get = \consize,
    right connections/.initial={},
    right connections/.get = \rconnections,
    right connections/.store in = \rconnections,
    left connections/.initial={},
    left connections/.get = \lconnections,
    left connections/.store in = \lconnections,
}

\tikzset{minimum width= \vesselwidth cm}
\tikzset{minimum height= \vesselheight cm}

\pgfdeclareshape{column3}{

    \inheritsavedanchors[from=rectangle]
    \inheritanchorborder[from=rectangle]
    \inheritanchor[from=rectangle]{center}
    \inheritanchor[from=rectangle]{south}
    \inheritanchor[from=rectangle]{west}
    \inheritanchor[from=rectangle]{east}
    \inheritanchor[from=rectangle]{south east}
    \inheritanchor[from=rectangle]{north east}
    \inheritanchor[from=rectangle]{south west}
    \inheritanchor[from=rectangle]{north west}
    \anchor{north}{
        \pgf@process{\northeast}
        \pgf@xa=\pgf@x
        \pgf@ya=\pgf@y
        \advance\pgf@x by -\pgf@xa
        \advance\pgf@y by 0.667\pgf@xa
    }
    \anchor{south}{
        \pgf@process{\southwest}
        \pgf@xa=\pgf@x
        \pgf@ya=\pgf@y
        \advance\pgf@x by -\pgf@xa
        \advance\pgf@y by 0.667\pgf@xa
    }

    \backgroundpath{

        \southwest \pgf@xa=\pgf@x \pgf@ya=\pgf@y
        \northeast \pgf@xb=\pgf@x \pgf@yb=\pgf@y

        %  Construct main path
        \pgfpathmoveto{\pgfpoint{\pgf@xa}{\pgf@ya}}

        % Add connections on the left side
        \edef\pgf@marshal{\noexpand\foreach \noexpand\i [count=\noexpand\ii] in {\lconnections}}
        \pgf@marshal
        {
            \def\connectionpos{\pgfpartway{\i}{\pgfpoint{\pgf@xa}{\pgf@yb}}{\pgfpoint{\pgf@xa}{\pgf@ya}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{        0 cm}{-0.8*\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{-\consize cm}{-0.8*\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{-\consize cm}{-1.2*\consize cm}}}
            \pgfcoordinate{\tikz@fig@name-connection\ii w} {\pgfrelative{\connectionpos}{\pgfpoint{-\consize cm}{ 0 cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{-\consize cm}{ 1.2*\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{-\consize cm}{ 0.8*\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{        0 cm}{ 0.8*\consize cm}}}
        }
        \pgfpathlineto{\pgfpoint{\pgf@xa}{\pgf@yb}}

        % Add connection to top of vessel
        \pgfmathsetmacro{\angle}{acos(\consize*0.8/(0.5*\vesselwidth)}
        \pgfpatharc{0}{\angle}{\pgf@xa and -0.667\pgf@xa}

        \ifthenelse{\boolean{\tconnection}}
        {
            \def\connectionpos{\pgfpoint{0}{\pgf@yb+0.667*\pgf@xb}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{-0.8*\consize cm}{0}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{-0.8*\consize cm}{\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{-1.2*\consize cm}{\consize cm}}}
            \pgfcoordinate{\tikz@fig@name-connectiont} {\pgfrelative{\connectionpos}{\pgfpoint{0 cm}{ \consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{ 1.2*\consize cm}{\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{ 0.8*\consize cm}{\consize cm}}}  
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{ 0.8*\consize cm}{0}}}    
        }
        {\pgfpatharc{\angle}{180-\angle}{\pgf@xa and -0.667\pgf@xa}}
        \pgfpatharc{180-\angle}{180}{\pgf@xa and -0.667\pgf@xa}

        \pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@yb}}

        % Add connections on the right side
        \edef\pgf@marshal{\noexpand\foreach \noexpand\i [count=\noexpand\ii] in {\rconnections}}
        \pgf@marshal
        {
            \def\connectionpos{\pgfpartway{\i}{\pgfpoint{\pgf@xb}{\pgf@yb}}{\pgfpoint{\pgf@xb}{\pgf@ya}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{       0 cm}{ 0.8*\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{\consize cm}{ 0.8*\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{\consize cm}{ 1.2*\consize cm}}}
            \pgfcoordinate{\tikz@fig@name-connection\ii e} {\pgfrelative{\connectionpos}{\pgfpoint{\consize cm}{ 0 cm}}}
            \message{^^J^^J\tikz@fig@name-connection\ii e^^J^^J}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{\consize cm}{-1.2*\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{\consize cm}{-0.8*\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{       0 cm}{-0.8*\consize cm}}}
        }
        \pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@ya}}

        % Add connection to bottom of vessel
        \pgfmathsetmacro{\angle}{acos(\consize*0.8/(0.5*\vesselwidth)}
        \pgfpatharc{0}{\angle}{-\pgf@xa and 0.667\pgf@xa}

        \ifthenelse{\boolean{\bconnection}}
        {
            \def\connectionpos{\pgfpoint{0}{\pgf@ya+0.667*\pgf@xa}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{ 0.8*\consize cm}{0}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{ 0.8*\consize cm}{-\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{ 1.2*\consize cm}{-\consize cm}}}
            \pgfcoordinate{\tikz@fig@name-connectionb} {\pgfrelative{\connectionpos}{\pgfpoint{0 cm}{ -\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{-1.2*\consize cm}{-\consize cm}}}
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{-0.8*\consize cm}{-\consize cm}}} 
            \pgfpathlineto{\pgfrelative{\connectionpos}{\pgfpoint{-0.8*\consize cm}{0}}}    
        }
        {\pgfpatharc{\angle}{180-\angle}{-\pgf@xa and 0.667\pgf@xa}}
        \pgfpatharc{180-\angle}{180}{-\pgf@xa and 0.667\pgf@xa}
        \pgfpathclose
    }
}

\makeatother

\begin{document}

    \begin{tikzpicture}[auto, node distance=1.5cm]

    % Draw the first tank 
    \node[column3,draw,thick,
    scale=1, width=1, height=2.6, 
    bottom connection=false, 
    top connection=false,
    right connections={0.10,0.90},
    left connections={0.5}] (col1) {};

    % Draw the second tank  
    \path(2,-1.5)
    node[column3,draw, thick,
    scale=1, width=1, height=2.6, 
    bottom connection=false, 
    top connection=false,
    right connections={0.25,0.90},
    left connections={0.25}] (col2) {};

    \draw[->](col1-connection2e) -- (col2-connection1w);
%
    \end{tikzpicture}

\end{document}

By the way, since you did not define the anchor properly, you cannot write right=of col1.connection2e nor anchor=connection1w. An anchor should be defined using one of

  • \savedanchor
  • \anchor
  • \deferredanchor
  • \anchorborder
Symbol 1
  • 36,855