2

I've created a template using pic that has 3 cells. Then used fit to created a box I named -perimeter around these 3 cells. Now I want to place and connect multiple pics of this type using a line (that will have a label in the future). I'm trying to do the placement and connections using pic_name-perimeter since from what I understand we can't simply place and connect the pics directly using their name.

The issue is using below=of or right=of does not align pics as expected.

I've tried using anchors (now removed) like done in this example to now avail (though I could be doing it wrong).

Does someone have an idea on how I can get this working?

  1. I'd like to get pics to vertically or horizontally align, and
  2. Have the same distance between each other independently of how much text there is in the cells.

Essentially get something similar to what is shown with the nodes on the top-left corner.

MWE and respective image below.


\documentclass{standalone}
\usepackage{tikz}

\usetikzlibrary{arrows}
\usetikzlibrary{calc}
\usetikzlibrary{fit}
\usetikzlibrary{positioning}

\tikzset{
    pics/state/.style n args={4}{
        code = {
            \begin{scope}[
                node distance=0cm, outer sep=0cm,
                cell/.style={draw, minimum height=0.6cm},
                %every node/.append style={anchor=center}
            ]
                \path node[cell] (a) [minimum width=1cm, align=center] {#1} node[cell] (b) [minimum width=3cm, align=center, right=of a] {#2} let \p1=($(b.south east)-(a.south west)$), \n1={veclen(\p1)} in node[cell] (c) [below=of a.south west, anchor=north west, minimum width=\n1, text width=(\pgfkeysvalueof{/pgf/minimum width}-2*\pgfkeysvalueof{/pgf/inner xsep}), align=left] {#3} node (-perimeter) [draw, red, fit=(a)(b)(c), inner sep=0pt] {};
            \end{scope}
        }
    }
}

\begin{document}
\begin{tikzpicture}[some box/.style={rectangle, draw}, node distance=2cm]
    \node[some box] (A1) {1};
    \node[some box] (A2) [below=of A1] {2};
    \node[some box] (A3) [below=of A2, text width=0.5cm] {3 3 3 3 3 3 3 3};
    \node[some box] (A4) [below=of A3] {4};
    \node[some box] (A9) [right=of A1] {9};
    \draw (A9) -- (A1) -- (A2) -- (A3) -- (A4);

    \pic (p1) [right=of A2]           {state={1}{idle}{This text is somewhat long but that's okay because it will wrap.}{}};
    \pic (p2) [below=of p1-perimeter] {state={2}{start}{This text is not long}{b3}};
    \pic (p3) [below=of p2-perimeter] {state={3}{this is a long state oh no and it keeps getting longer and longer}{}{}};
    \pic (p4) [below=of p3-perimeter] {state={4}{}{This is a multi-line description.\\And another line\\One last line.}{}};

    \pic (p9) [right=of p1-perimeter] {state={9}{}{}{}};

    \draw (p9-perimeter) -- (p1-perimeter) -- (p2-perimeter) -- (p3-perimeter) -- (p4-perimeter);
\end{tikzpicture}
\end{document}

enter image description here

2 Answers 2

5

Positioning of pic is tricky. I guess that you after something like this:

enter image description here

IN MWE below i change your code for pic and way of positioning. where is determined position of top left node in \pic which is starting point for positioning of other nodes in pic:

\documentclass[border=3.141592]{standalone}
\usepackage{tikz}

\usetikzlibrary{arrows, 
                calc, 
                fit, 
                positioning}

\tikzset{
    pics/state/.style n args={3}{code = {
        \begin{scope}[node distance=0pt,
                      nodes={draw, minimum height=0.6cm,
                             inner sep=1mm, outer sep=0pt,
                             align=center}
                     ]
        \node (-a) [text width=1cm, draw=none]      {#1};
        \node (-b) [text width=3cm,
                    below right=of -a.north east]          {#2};
        \node (-c) [text width=4.2 cm, align=left,
                    below left=of -b.south east] {#3};  
        \node (-f) [red, inner sep=0pt, fit=(-a)(-b)(-c)] {};
            \end{scope}
        }
    }
}

\begin{document}
    \begin{tikzpicture}[
node distance = 1cm and 2cm,
   box/.style = {draw, align=center}
                        ]
\node[box]              (A1)    {1};
\node[box, below=of A1] (A2)    {2};
\node[box, below=of A2] (A3)    {3 3\\ 3 3\\ 3 3\\ 3 3};
\node[box, below=of A3] (A4)    {4};
\node[box, right=of A1] (A9)    {9};
\draw (A9) -- (A1) -- (A2) -- (A3) -- (A4);
%
\pic (p1) [right=of A2] {state={1}{idle}{This text is somewhat long but that's okay because it will wrap.}};
\pic (p2) [below=of p1-a |- p1-f.south] 
                        {state={2}{start}{This text is not long}{b3}};
\pic (p3) [below=of p2-a |- p2-f.south] 
                        {state={3}{this is a long state oh no and it keeps getting longer and longer}{}};
\pic (p4) [below=of p3-a |- p3-f.south] 
                        {state={4}{}{This is a multi-line description.\\
                        And another line.\\
                        One last line.}{}};
\pic (p9) [right=of p1-c.north east] {state={9}{}{}{}};
%
\draw (p9-f.west) -- (p9-f.west -| p1-f.east) 
      (p1-f) -- (p2-f) -- (p3-f) -- (p4-f);
    \end{tikzpicture}
\end{document}
3

Explanation

  1. The position of each pic (relative to the whole tikzpicture env) is determined by local coordinate (0, 0) inside that pic. In your example, that is the anchor of node a.
  2. The anchor of each node a is influenced by the positioning options like below=of ....

Hence in \pic (p2) [below=of p1-perimeter], it is the two coordinates (p1-perimeter.south) and (p2a.north) that are vertically aligned. See the demonstration below.

\documentclass[margin=5pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{arrows, calc, fit, positioning}

\tikzset{
    pics/state/.style n args={4}{%
        code = {
            \begin{scope}[
                node distance=0cm, outer sep=0cm,
                cell/.style={draw, minimum height=0.6cm}
            ]

                \path 
                  node[cell] (a) [minimum width=1cm, align=center] {#1}
                  node[cell] (b) [minimum width=3cm, align=center, right=of a] {#2}
                  let
                    \p1=($(b.south east)-(a.south west)$), 
                    \n1={veclen(\p1)}
                  in
                    node[cell] (c) [below=of a.south west, anchor=north west, minimum width=\n1, text width=(\pgfkeysvalueof{/pgf/minimum width}-2*\pgfkeysvalueof{/pgf/inner xsep}), align=left] {#3} 
                    node (-perimeter) [draw, red, fit=(a)(b)(c), inner sep=0pt] {};
               \fill[blue] circle (2pt);
            \end{scope}%
        }
    }
}

\begin{document}
\begin{tikzpicture}[some box/.style={rectangle, draw}, node distance=2cm]
    \node[some box] (A1) {1};
    \node[some box] (A2) [below=of A1] {2};
    \node[some box] (A3) [below=of A2, text width=0.5cm] {3 3 3 3 3 3 3 3};
    \node[some box] (A4) [below=of A3] {4};
    \node[some box] (A9) [right=of A1] {9};
    \draw (A9) -- (A1) -- (A2) -- (A3) -- (A4);

    \pic (p1) [right=of A2]           
      {state={1}{idle}{This text is somewhat long but that's okay because it will wrap.}{}};
    \pic (p2) [below=of p1-perimeter] 
      {state={2}{start}{This text is not long}{b3}};
    \pic (p3) [below=of p2-perimeter] 
      {state={3}{this is a long state oh no and it keeps getting longer and longer}{}{}};
    \pic (p4) [below=of p3-perimeter] 
      {state={4}{}{This is a multi-line description.\\And another line\\One last line.}{}};

    \pic (p9) [right=of p1-perimeter] {state={9}{}{}{}};

%    \draw (p9-perimeter) -- (p1-perimeter) -- (p2-perimeter) -- (p3-perimeter) -- (p4-perimeter);
    
    \draw[blue]
        (A2) -- (p1a)
        (p1-perimeter) -- (p9a)
        (p1-perimeter) -- (p2a)
        (p2-perimeter) -- (p3a)
        (p3-perimeter) -- (p4a);
\end{tikzpicture}
\end{document}

enter image description here

Workaround

Since the desired alignment is with nodes -perimeter of each pic, I made the widest node c drawn first, with some trick calculating its width.

This fixes the vertical alignment, but not the horizontal one.

\documentclass[margin=5pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{arrows, calc, fit, positioning}

\tikzset{
    pics/state/.style n args={4}{%
        code = {
            \begin{scope}[
                node distance=0cm, outer sep=0cm,
                cell/.style={draw, minimum height=0.6cm},
            ]
                \path
                  node[cell] (c) [text width={
                    max(1cm, width("\unexpanded{#1}")+2*\pgfkeysvalueof{/pgf/inner xsep})
                   +max(3cm, width("\unexpanded{#2}")+2*\pgfkeysvalueof{/pgf/inner xsep})
                   -2*\pgfkeysvalueof{/pgf/inner xsep}
                  }, align=left] {#3}
                  node[cell] (a) [minimum width=1cm, align=center, anchor=south west] at (c.north west) {#1}
                  node[cell] (b) [minimum width=3cm, align=center, right=of a] {#2}
                  node (-perimeter) [draw, red, fit=(a)(b)(c), inner sep=0pt] {};
            \end{scope}%
        }
    }
}

\begin{document}
\begin{tikzpicture}[some box/.style={rectangle, draw}, node distance=2cm]
    \node[some box] (A1) {1};
    \node[some box] (A2) [below=of A1] {2};
    \node[some box] (A3) [below=of A2, text width=0.5cm] {3 3 3 3 3 3 3 3};
    \node[some box] (A4) [below=of A3] {4};
    \node[some box] (A9) [right=of A1] {9};
    \draw (A9) -- (A1) -- (A2) -- (A3) -- (A4);

    \pic (p1) [right=of A2]           
      {state={1}{idle}{This text is somewhat long but that's okay because it will wrap.}{}};
    \pic (p2) [below=of p1-perimeter] 
      {state={2}{start}{This text is not long}{b3}};
    \pic (p3) [below=of p2-perimeter] 
      {state={3}{this is a long state oh no and it keeps getting longer and longer}{}{}};
    \pic (p4) [below=of p3-perimeter] 
      {state={4}{}{This is a multi-line description.\\And another line\\One last line.}{}};

    \pic (p9) [right=of p1-perimeter] {state={9}{}{}{}};

    \draw (p9-perimeter) -- (p1-perimeter) -- (p2-perimeter) -- (p3-perimeter) -- (p4-perimeter);
\end{tikzpicture}
\end{document}

enter image description here

2
  • Ah, I don't regard my answer as useful as an accepted answer. Maybe OP can wait for better answers. Commented Jun 14, 2021 at 20:21
  • I found both answers useful and I'm not sure which one to select; I'll leave it open for now. At the moment I'm using @Zarko's solution with below=of p3-a |- p3-f.south and just kept all boxes with the same width. But your solution would also work; and in particular I liked the explanation (ty!), it was clear and helped me understand the problem.
    – Daniel C
    Commented Jun 15, 2021 at 12:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .