2

I have defined a few commands that each output a bunch of TikZ nodes that, combined, conceptually represent a single node/object. In order for the generated nodes to be treated as a cohesive whole, I wrap them in a scope.

  • \instruction{identifier base}{text L}{text R}{box style}{length style}{scope style}
  • \twoPInstruction{identifier base}{text L}{textR}{box style}{len1}{len3}{scope style}

With these commands, I want to create chain diagrams where these colorful instruction blocks are put next to each other.
All I'm getting is this overlapping drawing:
enter image description here

But I want them aligned adjacently: (something like this)
enter image description here
(the gray outline, and black box surrounding my renderings is caused by texmaker's preview)

I have tried adjusting TikZ's node distance style, but whatever distance I chose, I cannot get the orange box off of the blue one.

Here's my (super long) MWE. Most of it's styling, though; The interesting stuff happens in \newcommands and at the end near \begin{tikzpicture}:

\documentclass{standalone}

\usepackage{tikz}

\usetikzlibrary{arrows,decorations.pathmorphing,positioning,calc,matrix,fit,shapes.miscs,scopes,chains}

\tikzstyle{base}=[
    font={\sffamily\bfseries},
]
\tikzstyle{box}=[
    base,
    rounded rectangle,
    draw=black,
    inner sep=0.5ex,
]
\tikzstyle{boxL}=[
    box,
    rounded rectangle right arc=none,
]
\tikzstyle{boxR}=[
    box,
    rounded rectangle left arc=none,
]
\tikzstyle{inner}=[
    base,
    inner sep=0,
    node distance=0,
    line width=0,
]
\tikzstyle{innerL}=[
    inner,
    font={\small\sffamily\bfseries\color{white}},
]
\tikzstyle{innerR}=[
    inner,
    font={\small\sffamily\bfseries\color{black}},
]
\tikzstyle{innerLine}=[
    ->,
    dashed,
    dash pattern=on 0.25ex off 0.125ex,
    decorate,
    decoration={
        snake,
        amplitude=0.18ex,
        segment length=1ex,
        post length=0.5ex
    },
]
\tikzstyle{hop}=[
    ->,
    out=45,
    in=135,
    dashed,
    dash pattern=on 0.25ex off 0.125ex,
]
\tikzstyle{sep}=[
    inner,
    minimum width=1ex,
]

\tikzstyle{blue}=[
    fill=cyan,
]
\tikzstyle{orange}=[
    fill=orange,
]
\tikzstyle{len1}=[
    inner,
    text width=6ex,
]
\tikzstyle{len2}=[
    inner,
    text width=13ex,
]
\tikzstyle{len3}=[
    inner,
    text width=19ex,
]
\tikzstyle{len4}=[
    inner,
    text width=25ex,
]

\pgfdeclarelayer{box}
\pgfdeclarelayer{text}
\pgfsetlayers{box,text,main}

\newcommand{\baseInstruction}[7]{% id-base, text L, text R, color style, box style, length style, scope style
{[#7]
    \begin{pgfonlayer}{text}%box contents
        \node[#6] (#1 Skel) {};
        \node[innerL, right=of #1 Skel.base west, anchor=base west] (#1 SkelL) {#2};
        \node[innerR, left=of #1 Skel.base east, anchor=base east] (#1 SkelR) {#3};
        \node[inner, fit=(#1 SkelL.north east)(#1 SkelR.south west)] (#1 Between1) {};
        \node[inner, fit=(#1 SkelL.south east)(#1 SkelR.north west)] (#1 Between2) {};
        \node[inner, fit=(#1 Between1)(#1 Between2)] (#1 Between) {};
        \node[inner, fit=(#1 SkelL)(#1 SkelR)] (#1 Contents) {}; %needed for attaching arrows to
        \draw[innerLine] ($(#1 Between.west) + (0.2ex,0)$) -- ($(#1 Between.east) - (0.2ex,0)$);
    \end{pgfonlayer}
    \begin{pgfonlayer}{box}%box outlines
        \node[#5, #4, fit=(#1 SkelL)(#1 SkelR)] (#1) {};
    \end{pgfonlayer}
}
}

\newcommand{\instruction}[6]{% id-base, text L, text R, color style, length style, scope style
    \baseInstruction{#1}{#2}{#3}{#4}{box}{#5}{#6}
}

\newcommand{\instructionL}[6]{% id-base, text L, text R, color style, length style, scope style
    \baseInstruction{#1L}{#2}{#3}{#4}{boxL}{#5}{#6}
}

\newcommand{\instructionR}[6]{% id-base, text L, text R, color style, length style, scope style
    \baseInstruction{#1R}{#2}{#3}{#4}{boxR}{#5}{#6}
}

\newcommand{\twoPInstruction}[7]{% id-base, text L, text R, color style, length style L, length style R, scope style
{[#7]
    \instructionL{#1}{#2}{\vphantom{#3}}{#4}{#5}{}
    \begin{pgfonlayer}{text}%box contents   
        \node[sep, right=of #1L Skel.base east] (#1 Sep) {};
    \end{pgfonlayer}    
    \instructionR{#1}{\vphantom{#2}}{#3}{#4}{#6, right=of #1 Sep}{}
    \begin{pgfonlayer}{text}
        \draw ($(#1L Between.east) + (0,0.3ex)$) edge[hop] ($(#1R Between.west) + (0,0.3ex)$);
    \end{pgfonlayer}
}
}


\begin{document}


\begin{tikzpicture}[
    start chain,
    node distance=10cm,
    every child node/.style={on chain,join},
    every join/.style={->}
]
\instruction{a}{a}{X}{blue}{len2}{}
\twoPInstruction{b}{b}{Y}{orange}{len1}{len3}{}
\end{tikzpicture}

\end{document}
  • node distance sets the default distance when you use the positioning keys for placing nodes using relative positions (such as left=of a), and not by using absolute placement as you do here – Bordaigorl Jan 6 '14 at 14:35
  • Also if you want to use the {[style]...} shorthand for scope you need to include the library scopes – Bordaigorl Jan 6 '14 at 14:36
  • I added the scopes library now. I've also updated my tikzpicture to use the chain library. The result is still the same though. – derabbink Jan 6 '14 at 14:49
  • If I am not wrong in \baseInstruction you position all the "sub"-nodes relative to the one created in \node[#6] (#1 Skel) {}; which, itself, is not positioned relatively to any other node (unless of course you specify otherwise in #6, which you don't). So, the two "main" nodes you create are not positioned one relatively to the other so the positioning/chain keys have no effect. Am I missing something? – Bordaigorl Jan 6 '14 at 14:53
  • where is the node-families located? – percusse Jan 6 '14 at 14:54
3

First you need to include the scopes library for the scope shorthands to work properly.

Second, node distance only works for nodes placed using the positioning keys, such as right=of a, which you do not use here to position the two main nodes relatively to each other.

Therefore the quickest fix to the code (after including scopes) is to manually shift the second node:

\begin{tikzpicture}
  \instruction{a}{a}{X}{blue}{len2}{}
  \twoPInstruction{b}{b}{Y}{orange}{len1}{len3}{xshift=2cm}
\end{tikzpicture}

Also, you may consider using multipart nodes instead of manually drawing the shapes.

Another option is using nested tikzpictures by having just two nodes \node (a) {\tikz{...}}; and \node[right=of a] (b) {\tikz{...}}; so that you can use the two "logical" nodes as "modules", which would make the use of fit unnecessary.

  • I have tried this, but I still get an error. I wrote up an answer that shows my modified code and corresponding errors – derabbink Jan 8 '14 at 8:20
1

As suggested by @Bordaigorl's answer and comments, I have wrapped the body of \instruction and \twoPInstruction in nodes and tikzpictures.

\newcommand{\baseInstruction}[7]{% id-base, text L, text R, color style, box style, length style, scope style
{
    \begin{pgfonlayer}{text}%box contents
        \node[#6] (#1 Skel) {};
        \node[innerL, right=of #1 Skel.base west, anchor=base west] (#1 SkelL) {#2};
        \node[innerR, left=of #1 Skel.base east, anchor=base east] (#1 SkelR) {#3};
        \node[inner, fit=(#1 SkelL.north east)(#1 SkelR.south west)] (#1 Between1) {};
        \node[inner, fit=(#1 SkelL.south east)(#1 SkelR.north west)] (#1 Between2) {};
        \node[inner, fit=(#1 Between1)(#1 Between2)] (#1 Between) {};
        \node[inner, fit=(#1 SkelL)(#1 SkelR)] (#1 Contents) {}; %needed for attaching arrows to
        \draw[innerLine] ($(#1 Between.west) + (0.2ex,0)$) -- ($(#1 Between.east) - (0.2ex,0)$);
    \end{pgfonlayer}
    \begin{pgfonlayer}{box}%box outlines
        \node[#5, #4, fit=(#1 SkelL)(#1 SkelR)] (#1 Box) {};
    \end{pgfonlayer}
}}

\newcommand{\instruction}[6]{% id-base, text L, text R, color style, length style, scope style
\node [#6] (#1) {\tikz{
    \baseInstruction{#1}{#2}{#3}{#4}{box}{#5}{}
}};
}

\newcommand{\instructionL}[6]{% id-base, text L, text R, color style, length style, scope style
    \baseInstruction{#1L}{#2}{#3}{#4}{boxL}{#5}{#6}
}

\newcommand{\instructionR}[6]{% id-base, text L, text R, color style, length style, scope style
    \baseInstruction{#1R}{#2}{#3}{#4}{boxR}{#5}{#6}
}

\newcommand{\twoPInstruction}[7]{% id-base, text L, text R, color style, length style L, length style R, scope style
\node [#7] (#1) {\tikz{
    \instructionL{#1}{#2}{\vphantom{#3}}{#4}{#5}{}
    \begin{pgfonlayer}{text}%box contents   
        \node[sep, right=of #1L Skel.base east] (#1 Sep) {};
    \end{pgfonlayer}    
    \instructionR{#1}{\vphantom{#2}}{#3}{#4}{#6, right=of #1 Sep}{}
    \begin{pgfonlayer}{text}
        \draw[hop] ($(#1L Between.east) + (0,0.3ex)$) to[hop] ($(#1R Between.west) + (0,0.3ex)$);
    \end{pgfonlayer}
}};
}

reverse order

\begin{tikzpicture}[start chain, node distance=5ex, every join/.style={->},]
\twoPInstruction{b}{b}{Y}{orange}{len1}{len3}{on chain, join}
\instruction{a}{a}{X}{blue}{len2}{on chain, join}
\end{tikzpicture}

But reversing a and b initially gave me an error on the line of the \twoPInstruction: "! Package pgf Error: No shape named chain-2 is known." ... "l.139 ...b}{Y}{orange}{len1}{len3}{on chain, join}"
To fix this, I had to get rid of the edge in twoPInstruction, as this interferes with the chain library. Instead, I now use to[hop]. N.B. that I'm using the hop style twice in one line (because I'm lazy); half of the style keys apply to the \draw part of the line, while the other half applies to the \to part.

normal order

\begin{tikzpicture}[start chain, node distance=5ex, every join/.style={->},]
\instruction{a}{a}{X}{blue}{len2}{on chain, join}
\twoPInstruction{b}{b}{Y}{orange}{len1}{len3}{on chain, join}
\end{tikzpicture}

PS: I also eradicated the group style ([#7]) from the \baseInstruction to avoid warnings in the log.

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.