3

I am trying to make a schematic, where I need to have a rectangle with the minimum width set to the distance between two other rectangle nodes. I tried to follow the solutions from Compute the x distance between two nodes post, but in that case I always end up with the following error

Illegal parameter number in definition of \iterate. \end{frame}

I guess I forgot something, but cannot figure out what.

Here is what I am trying to do:

Schematics

My problem is to get the green and red rectangles width. Here is what I have so far:

\documentclass[aspectratio=169, 14pt, notes]{beamer}

\usepackage{tikz} \usetikzlibrary{arrows.meta, calc, fit, graphs, math, positioning, shapes, shadows, tikzmark}

\begin{frame}[t] \frametitle{My frame} \framesubtitle{Example}

\newcommand\setxveclength[5]{% newmacro, node1, anchor1, node2, anchor2 \pgfpointdiff{\pgfpointanchor{#2}{#3}}{\pgfpointanchor{#4}{#5}} \edef#1{\the\pgf@x} }

\begin{tikzpicture}[remember picture, overlay] \node[inner sep=0pt, minimum width=0.30cm, minimum height=2.50cm, rounded corners=0.10cm, draw=gray4, fill=gray5, text=white, font=\tiny] (PartA) at ({$(current page.west)!0.25!(current page.east)$} |- {$(current page.north)!0.50!(current page.south)$}) {\rotatebox{270}{AAA}}; \node[inner sep=0pt, minimum width=0.30cm, minimum height=2.50cm, rounded corners=0.10cm, draw=gray4, fill=gray5, text=white, font=\tiny, anchor=west] (PartB) at ([xshift=0.50mm]PartA.east) {\rotatebox{270}{BBB}}; \node[inner sep=0pt, minimum width=0.30cm, minimum height=2.50cm, rounded corners=0.10cm, draw=gray4, fill=gray5, text=white, font=\tiny, anchor=west] (PartC) at ([xshift=0.50mm]PartB.east) {\rotatebox{270}{CCC}}; \node[inner sep=0pt, minimum width=0.30cm, minimum height=1.66cm, rounded corners=0.10cm, draw=gray4, fill=gray5, text=white, font=\tiny, anchor=south west] (PartD)at ([xshift=0.50mm]PartC.south east) {\rotatebox{270}{DDD}}; \node[inner sep=0pt, minimum width=0.30cm, minimum height=1.66cm, rounded corners=0.10cm, draw=gray4, fill=gray5, text=white, font=\tiny, anchor=west] (PartE) at ([xshift=0.50mm]PartD.east) {\rotatebox{270}{EEE}}; \node[inner sep=0pt, minimum width=0.30cm, minimum height=1.66cm, rounded corners=0.10cm, draw=gray4, fill=gray5, text=white, font=\tiny, anchor=west] (PartF) at ([xshift=0.50mm]PartE.east) {\rotatebox{270}{FFF}}; \node[inner sep=0pt, minimum width=0.30cm, minimum height=2.50cm, rounded corners=0.10cm, draw=gray4, fill=gray5, text=white, font=\tiny, anchor=south west] (PartG)at ([xshift=0.50mm]PartF.south east) {\rotatebox{270}{GGG}}; \node[inner sep=0pt, minimum width=0.30cm, minimum height=2.50cm, rounded corners=0.10cm, draw=gray4, fill=gray5, text=white, font=\tiny, anchor=west] (PartH) at ([xshift=0.50mm]PartG.east) {\rotatebox{270}{HHH}}; \node[inner sep=0pt, minimum width=0.30cm, minimum height=2.50cm, rounded corners=0.10cm, draw=gray4, fill=gray5, text=white, font=\tiny, anchor=west] (PartI) at ([xshift=0.50mm]PartH.east) {\rotatebox{270}{III}};

\setxveclength{\mydist}{LinuxComp_Tel}{west}{ActiaSoft_GNSS}{east}
\node[inner sep=0pt, minimum width=\pgfpointdiff{\pgfpointanchor{PartD}{west}}{\pgfpointanchor{PartF}{east}}, minimum height=0.30cm, rounded corners=0.10cm, draw=gray4, fill=GreenActia, text=white, font=\tiny, anchor=south west]  (PartJ)    at ([yshift=0.50mm]PartD.north west) {JJJ};

\end{tikzpicture} \end{frame}

I notice that the problem appears as soon as I declare the \newcommand. I do not need to invoke it

I found this solution creating the node the following way:

\path let \p1=($(PartD.west)-(PartF.east)$), \n1 = {veclen(\p1)}
  in node[inner sep=0pt, minimum width=\n1, minimum height=0.30cm, rounded corners=0.10cm, draw=gray4, fill=green4, text=white, font=\tiny, anchor=south west, name=PartJJJ]  at ([yshift=0.50mm]PartD.north west) {JJJ};

But in that case I cannot reuse the distance for a second node, that would mean that I have to recalculate the width each time, and make sure the node taken for the calculation are accurate for each rectangle with this particular width.

Thanks for the help,

Krzysztof
  • 121
  • Your MWE won't compile for me. Are you maybe missing some packages or definitions? – Jasper Habicht Mar 31 '23 at 15:58
  • That is the reason of my postm the fact that I have the compilation error. The only additional package that might be needed if ninecolors, but I check and did not load it excplicitelly. – Krzysztof Mar 31 '23 at 19:24
  • Thanks for pinting to the ninecolors package. I added this to my answer. Sorry, I first thought that your main question was about how to get this distances into the minimum width option, so I did not care about the beamer context – Jasper Habicht Mar 31 '23 at 20:48
  • 1
    Your command won't work because it uses a control sequence with an @ in its name without using \makeatletter. – Qrrbrbirlbel Mar 31 '23 at 23:51
  • 1
    Your error message comes from using #… inside a frame, it will need the option fragile to work (or you move the definition of \setxveclength in the preamble where it belongs). – Qrrbrbirlbel Apr 01 '23 at 00:45
  • Thanks @Qrrbrbirlbel, that was it. I put the newcommand in the preamble, and it started to work. As for the \makeatletter and \makeatother I tried it, but since it was not in the preamble I still got the error. Of course, in the preamble, I had to add it too for the whole thing to work. Thanks – Krzysztof Apr 01 '23 at 18:15

3 Answers3

3

This is one of the instances I've made the ext.positioning-plus library of my tikz-ext package for.

It combines the keys from positioning library with the syntax and measuring of the fit library while actually setting minimum width and not text width which is helpful if you actually want to typeset text inside that node without having to use a label. But this also means that the node will grow if you put more text in it than it will fit.

With it, you can say below = of -(A)(B) which will place a node below of the bounding box around A and B and sets the minimum width so that it will be as wide as A and B (i.e. said bounding box).

It will actually subtract twice the outer xsep of minimum width because that is included in the measurement of A and B, too. (This won't work so nicely with shapes like circle that ignore different values for outer xsep and outer ysep but setting different widths and heights for a circle won't work good to begin with.)


The ext.positioning-plus library also defines a positioning key south right, i.e.

\node[south right=of CCC] (DDD) {DDD};

which works like right = of CCC but uses the south east anchor on the reference of CCC and the anchor = south west on the DDD node – i.e. bottom-aligning nodes placed to the right – but since I'm opting to rotate the nodes here (instead of \rotatebox) I'm going to use the custom west right key instead.

Code

\documentclass[aspectratio=169, 14pt]{beamer}
\usepackage{tikz, ninecolors}
\usetikzlibrary{ext.positioning-plus, chains}
\tikzset{west right/.style={right=#1.south west, anchor=north west}}
\begin{document}
\begin{frame}[t]
\frametitle{My frame}
\framesubtitle{Example}
\centering % maybe
\begin{tikzpicture}[
  start chain=going west right,
  node distance=.5mm,
  my node/.style={
    inner sep=+0pt, minimum height=+.3cm, rounded corners=+.1cm,
    draw=gray4, fill=gray5, text=white, font=\tiny},
  every on chain/.append style={my node, rotate=90},
  greenish/.style={draw=green4, fill=green5},
  redish/.style={draw=red4, fill=red5}]
\foreach \WIDTH/\NODES in {2.5cm /{AAA, BBB, CCC},
                           1.66cm/{DDD, EEE, FFF},
                           2.5cm /{GGG, HHH, III}}
  \foreach \NODE in \NODES
    \node[on chain, minimum width=\WIDTH]{\NODE};
\node[below=of -(chain-begin)(chain-end), my node, greenish] {MMM};
\node[above=of -(chain-begin)(chain-end), my node, greenish] {LLL};

\node[above=of -(chain-4)(chain-6), my node, redish] (JJJ) {JJJ}; \node[above=of -(JJJ), ultra thick, my node, redish] {KKK}; \end{tikzpicture} \end{frame} \end{document}

Output

enter image description here

Qrrbrbirlbel
  • 119,821
2

you can use a fit node. The fit option just fixes node dimensions but you can place it wherever your need. It's not necessary to envolve the reference coordinates or nodes.

\documentclass[aspectratio=169, 14pt, notes]{beamer}

\usepackage{tikz} \usepackage{ninecolors} \usetikzlibrary{arrows.meta, calc, fit, graphs, math, positioning, shapes, shadows, tikzmark}

\begin{document} \begin{frame}[t] \frametitle{My frame} \framesubtitle{Example}

% \newcommand\setxveclength[5]{% newmacro, node1, anchor1, node2, anchor2 % \pgfpointdiff{\pgfpointanchor{#2}{#3}}{\pgfpointanchor{#4}{#5}} % \edef#1{\the\pgf@x} % }

\begin{tikzpicture}[remember picture, overlay] \node[inner sep=0pt, minimum width=0.30cm, minimum height=2.50cm, rounded corners=0.10cm, draw=gray4, fill=gray5, text=white, font=\tiny] (PartA) at ({$(current page.west)!0.25!(current page.east)$} |- {$(current page.north)!0.50!(current page.south)$}) {\rotatebox{270}{AAA}}; \node[inner sep=0pt, minimum width=0.30cm, minimum height=2.50cm, rounded corners=0.10cm, draw=gray4, fill=gray5, text=white, font=\tiny, anchor=west] (PartB) at ([xshift=0.50mm]PartA.east) {\rotatebox{270}{BBB}}; \node[inner sep=0pt, minimum width=0.30cm, minimum height=2.50cm, rounded corners=0.10cm, draw=gray4, fill=gray5, text=white, font=\tiny, anchor=west] (PartC) at ([xshift=0.50mm]PartB.east) {\rotatebox{270}{CCC}}; \node[inner sep=0pt, minimum width=0.30cm, minimum height=1.66cm, rounded corners=0.10cm, draw=gray4, fill=gray5, text=white, font=\tiny, anchor=south west] (PartD)at ([xshift=0.50mm]PartC.south east) {\rotatebox{270}{DDD}}; \node[inner sep=0pt, minimum width=0.30cm, minimum height=1.66cm, rounded corners=0.10cm, draw=gray4, fill=gray5, text=white, font=\tiny, anchor=west] (PartE) at ([xshift=0.50mm]PartD.east) {\rotatebox{270}{EEE}}; \node[inner sep=0pt, minimum width=0.30cm, minimum height=1.66cm, rounded corners=0.10cm, draw=gray4, fill=gray5, text=white, font=\tiny, anchor=west] (PartF) at ([xshift=0.50mm]PartE.east) {\rotatebox{270}{FFF}}; \node[inner sep=0pt, minimum width=0.30cm, minimum height=2.50cm, rounded corners=0.10cm, draw=gray4, fill=gray5, text=white, font=\tiny, anchor=south west] (PartG)at ([xshift=0.50mm]PartF.south east) {\rotatebox{270}{GGG}}; \node[inner sep=0pt, minimum width=0.30cm, minimum height=2.50cm, rounded corners=0.10cm, draw=gray4, fill=gray5, text=white, font=\tiny, anchor=west] (PartH) at ([xshift=0.50mm]PartG.east) {\rotatebox{270}{HHH}}; \node[inner sep=0pt, minimum width=0.30cm, minimum height=2.50cm, rounded corners=0.10cm, draw=gray4, fill=gray5, text=white, font=\tiny, anchor=west] (PartI) at ([xshift=0.50mm]PartH.east) {\rotatebox{270}{III}};

\node[fit={(PartD.west) (PartF.east)}, inner sep=0pt, minimum height=.3cm, rounded corners=.1cm, draw=gray4,fill=red!70!black, text=white, font=\tiny, anchor=south west, label={[font=\tiny,text=white]center:JJJ}] (PartJ) at ([yshift=0.5mm]PartD.north west){};

\node[fit={(PartD.west) (PartF.east)}, inner sep=0pt, minimum height=.3cm, rounded corners=.1cm, draw=gray4,fill=red!70!black, text=white, font=\tiny, anchor=south west, label={[font=\tiny,text=white]center:KKK}] (PartK) at ([yshift=0.5mm]PartJ.north west){};

\node[fit={(PartA.west) (PartI.east)}, inner sep=0pt, minimum height=.3cm, rounded corners=.1cm, draw=gray4,fill=green!70!black, anchor=south west, label={[font=\tiny,text=white]center:LLL}] (PartL) at ([yshift=0.5mm]PartA.north west){};

\node[fit={(PartA.west) (PartI.east)}, inner sep=0pt, minimum height=.3cm, rounded corners=.1cm, draw=gray4,fill=green!70!black, anchor=north west, label={[font=\tiny,text=white]center:MMM}] (PartM) at ([yshift=-0.5mm]PartA.south west){};

% \setxveclength{\mydist}{LinuxComp_Tel}{west}{ActiaSoft_GNSS}{east} % \node[inner sep=0pt, minimum width=\pgfpointdiff{\pgfpointanchor{PartD}{west}}{\pgfpointanchor{PartF}{east}}, minimum height=0.30cm, rounded corners=0.10cm, draw=gray4, fill=GreenActia, text=white, font=\tiny, anchor=south west] (PartJ) at ([yshift=0.50mm]PartD.north west) {JJJ}; \end{tikzpicture} \end{frame} \end{document}

enter image description here

Ignasi
  • 136,588
1

You can apply the solution provided by this nice answer. And you can use loops and styles to simplify your code:

\documentclass[border=10pt]{standalone}
\usepackage{ninecolors}
\usepackage{tikz}
\usetikzlibrary{calc}

\begin{document}

\makeatletter
\tikzset{
    span nodes horizontally/.style 2 args={
        insert path={
            \pgfextra{
                \pgfpointdiff{\pgfpointanchor{#1}{west}}{\pgfpointanchor{#2}{east}}
                \pgf@xa=\pgf@x
            }
        },
        minimum width={\pgf@xa-\pgfkeysvalueof{/pgf/outer xsep}}
    },
    span nodes vertically/.style 2 args={
        insert path={
            \pgfextra{
                \pgfpointdiff{\pgfpointanchor{#2}{south}}{\pgfpointanchor{#1}{north}}
                \pgf@ya=\pgf@y
            }
        },
        minimum height={\pgf@ya-\pgfkeysvalueof{/pgf/outer ysep}}
    },
}
\makeatother

\tikzset{
    rounded/.style={
        inner sep=0pt, 
        minimum width=0.30cm, 
        minimum height=0.30cm, 
        rounded corners=0.10cm, 
        draw=gray4,
        fill=gray5, 
        text=white, 
        font=\tiny, 
    }
}
\makeatother

\begin{tikzpicture}
    \coordinate (Init) at ([xshift=-0.50mm]{$(current page.west)!0.25!(current page.east)$} |- {$(current page.north)!0.50!(current page.south)$});
    \foreach \h/\n/\c [remember=\n as \lastn (initially Init)] in {
        2.5/PartA/AAA,
        2.5/PartB/BBB,
        2.5/PartC/CCC,
        1.66/PartD/DDD,
        1.66/PartE/EEE,
        1.66/PartF/FFF,
        2.5/PartG/GGG,
        2.5/PartH/HHH,
        2.5/PartI/III
    } {
        \node[rounded, minimum height={\h*1cm}, anchor=south west] (\n) 
            at ([xshift=0.50mm]\lastn.south east) {\rotatebox{270}{\c}};
    }

    \node[rounded, span nodes horizontally={PartD}{PartF}, fill=green4, anchor=south west] (PartJJJ) 
        at ([yshift=0.50mm]PartD.north west) {JJJ};

    % variation for vertical span

    \node[rounded, minimum width=2em, anchor=north west] (PartK) 
        at ([yshift=-0.50mm]PartA.south west) {KKK};

    \node[rounded, minimum width=2em, anchor=north west] (PartL) 
        at ([yshift=-0.50mm]PartK.south west) {LLL};

    \node[rounded, span nodes vertically={PartK}{PartL}, fill=green4, anchor=south west] (PartM) 
        at ([xshift=0.50mm]PartL.south east) {\rotatebox{270}{MM}};

\end{tikzpicture}

\end{document}

enter image description here


Probably a better and more robust solution would be (thaks again to Qrrbrbirlbel):

\documentclass[border=10pt]{standalone}
\usepackage{ninecolors}
\usepackage{tikz}
\usetikzlibrary{calc}

\begin{document}

\makeatletter
\tikzset{
    span nodes horizontally/.code 2 args={
        \pgf@process{\pgfpointdiff{\pgfpointanchor{#1}{west}}{\pgfpointanchor{#2}{east}}}
        \pgfkeyssetevalue{/pgf/minimum width}{\the\pgf@x-2*(\noexpand\pgfkeysvalueof{/pgf/outer xsep})}
    },
    span nodes vertically/.code 2 args={
        \pgf@process{\pgfpointdiff{\pgfpointanchor{#2}{south}}{\pgfpointanchor{#1}{north}}}
        \pgfkeyssetevalue{/pgf/minimum height}{\the\pgf@y-2*(\noexpand\pgfkeysvalueof{/pgf/outer ysep})}
    },
}
\makeatother

\tikzset{
    rounded/.style={
        inner sep=0pt, 
        minimum width=0.30cm, 
        minimum height=0.30cm, 
        rounded corners=0.10cm, 
        draw=gray4,
        fill=gray5, 
        text=white, 
        font=\tiny, 
    }
}
\makeatother

\begin{tikzpicture}
    \coordinate (Init) at ([xshift=-0.50mm]{$(current page.west)!0.25!(current page.east)$} |- {$(current page.north)!0.50!(current page.south)$});
    \foreach \h/\n/\c [remember=\n as \lastn (initially Init)] in {
        2.5/PartA/AAA,
        2.5/PartB/BBB,
        2.5/PartC/CCC,
        1.66/PartD/DDD,
        1.66/PartE/EEE,
        1.66/PartF/FFF,
        2.5/PartG/GGG,
        2.5/PartH/HHH,
        2.5/PartI/III
    } {
        \node[rounded, minimum height={\h*1cm}, anchor=south west] (\n) 
            at ([xshift=0.50mm]\lastn.south east) {\rotatebox{270}{\c}};
    }

    \node[rounded, span nodes horizontally={PartD}{PartF}, fill=green4, anchor=south west] (PartJJJ) 
        at ([yshift=0.50mm]PartD.north west) {JJJ};

    % variation for vertical span

    \node[rounded, minimum width=2em, anchor=north west] (PartK) 
        at ([yshift=-0.50mm]PartA.south west) {KKK};

    \node[rounded, minimum width=2em, anchor=north west] (PartL) 
        at ([yshift=-0.50mm]PartK.south west) {LLL};

    \node[rounded, span nodes vertically={PartK}{PartL}, fill=green4, anchor=south west] (PartM) 
        at ([xshift=0.50mm]PartL.south east) {\rotatebox{270}{MM}};

\end{tikzpicture}

\end{document}


Both solutions also work inside a beamer context.

  • The solution looks nice, but unfortunetelly, I still got the "Illegal parameter number in definition of \iterate. \end{frame}" error. I guess that this might be due to the fact that I am in a beamer environment – Krzysztof Mar 31 '23 at 19:21
  • @Krzysztof I cannot reproduce the problem. I added an MWE that uses the beamer document class and it compiles fine for me. Maybe try to compile from scratch. There might be some error stuck in one of the auxiliary files that were created during one of the last compilations. – Jasper Habicht Mar 31 '23 at 20:35
  • 1
    May I suggest span nodes horizontally/.code 2 args=\pgf@process{\pgfpointdiff{\pgfpointanchor{#1}{west}}{\pgfpointanchor{#2}{east}}}\pgfkeyssetevalue{/pgf/minimum width}{\the\pgf@x-2*(\noexpand\pgfkeysvalueof{/pgf/outer xsep})}}. insert path and \pgfextra is a detour to executing TeX code → .code handler (or /utils/exec= in a style). \pgf@process to be on the safe side in case \pgfpointdiff overwrites some values like \pgf@xa itself (unlikely but still). When minimum width will be evaluated \pgf@xa might already be another value → better expand it then and there. – Qrrbrbirlbel Apr 01 '23 at 00:30
  • Check your JJJ node, it is a tad wider than its reference because of their outer seps. – Qrrbrbirlbel Apr 01 '23 at 00:31
  • @Qrrbrbirlbel True! I need to wrap my head around this first ... Actually, I first thought to go with .code but I did not get how to correctly set a key value in this context. Thanks, now I know! – Jasper Habicht Apr 01 '23 at 08:07
  • 1
    You can always use \pgfkeysalso, \tikzset or any other \pgfkeys macro inside your code. (A .style is just a .code that puts its argument in a \pgfkeysalso.) – Qrrbrbirlbel Apr 01 '23 at 14:30