2

I've written a macro to do some drawing in tikz which I'd like to be able to invoke at arbitrary positions (which I think are called nodes?)

I've attempted to write the coordinates using relative positioning, but when I invoke it, my diagrams overlay each other.

I'm not sure if I'm misunderstanding the node syntax or relative coordinates.

\documentclass[a4paper]{article}
\usepackage{tikz} 
\usetikzlibrary{positioning}
    
\newcommand{\Register[1]}{
    \fill [gray] +(0,0) rectangle +(4,1);   
    \fill [pink] +(4,0) rectangle +(8,1);   
    
    \draw [thick]         
        +(0,0) -- +(8,0)  -- +(8,1)  -- +(0,1) -- cycle;
    \draw [thick]         
        +(0,0)  +(4,0) -- +(4,1);
    \draw [thin, dashed]  
        +(0,0)  +(2,0) -- +(2,1)  +(6,0) -- +(6,1); 
    \draw [very thin, dotted]  
        +(0,0)  +(1,0) -- +(1,1)  +(3,0) -- +(3,1)
                +(5,0) -- +(5,1)  +(7,0) -- +(7,1);
                
    \node [above right] at +(0,1) {#1};         
}
    

\begin{document}
\begin{tikzpicture} 
    \node (a) {A};
    \Register[R\textsubscript{1}];
    
    \node [below=of a] {B};
    \Register[R\textsubscript{2}]
\end{tikzpicture}
\end{document}

Current Output

7
  • Firstly, it's \newcommand{\Register}[1]. Note where the braces are. Nov 7, 2022 at 17:41
  • Your \Register will always be placed at the coordinates that are specified in the macro. You're going to need to use a transformation to have it move around. That said, I think you are looking for a pic which are almost like a macro but it can be placed like a node (where (0,0) of the pic will be placed at the specified coordinate: \pic at (<coord>) {pic name};) There are also ways to do that without a pic but it's the easiest way to do in TikZ. Nov 7, 2022 at 17:44
  • 1
    You could also use a rectangular node with a path picture or an append after command which has certain advantages and disadvantages. Can you explain how your final diagram should look like? Nov 7, 2022 at 17:47
  • Many thanks @Qrrbrbirlbel. The final diagram will be a number of these Registers each with a different combination of colored squares. For that reason I didn't go with a pic solution: is that in fact a possibility? Or must I pass parameters to indicate coordinates into the macro? (I'm a bit worried about running out of parameters: I believe I can only have 9?) The Registers will be connected together with arrows to indicate a processing flow. Some Registers will be grouped together, probably by a surrounding rectangle.
    – Ian
    Nov 7, 2022 at 19:01
  • Yes, TeX only allows nine # (there are ways around it but I don't think we need that here). It's better to use a different syntax or keys. Is it always dotted, dashed, dotted, solid, dotted, dashed, dotted, solid, …? Nov 7, 2022 at 19:12

2 Answers 2

2

A relative coordinate is relative to the previous position in the current path. At the beginning of the path, the previous position is always (0,0).

You must therefore provide a reference coordinate (a new argument to your command) and use it at the start of each path.

\documentclass[tikz]{standalone}
\usepackage{tikz} 
\usetikzlibrary{positioning}

\newcommand\Register[2]{
  \fill [gray] (#1) +(0,0) rectangle +(4,1);   
  \fill [pink] (#1) +(4,0) rectangle +(8,1);   
  \draw [thick]         
  (#1) +(0,0) -- +(8,0)  -- +(8,1)  -- +(0,1) -- cycle;
  \draw [thick]         
  (#1) +(0,0)  +(4,0) -- +(4,1);
  \draw [thin, dashed]  
  (#1) +(0,0)  +(2,0) -- +(2,1)  +(6,0) -- +(6,1); 
  \draw [very thin, dotted]  
  (#1) +(0,0)  +(1,0) -- +(1,1)  +(3,0) -- +(3,1)
  +(5,0) -- +(5,1)  +(7,0) -- +(7,1);
  \path (#1) +(0,1) node [above right] {#2};         
}

\begin{document}
\begin{tikzpicture} 
  \node (a) {A};
  \Register{a}{R\textsubscript{1}};
  
  \node [below=of a] (b) {B};
  \Register{b}{R\textsubscript{2}}
\end{tikzpicture}
\end{document}  

enter image description here

1
  • Oh, now I understand how OP was thinking! Nov 8, 2022 at 8:43
1

This looks scary but unless you have to rotate the nodes I'd go with this path picture approach:

  1. It allows a list of boxes, i.e.

    4/gray, 4/pink
    

    means four gray squares and four pink squares. (They are actually drawn as one rectangles to hide a few unlucky rendering artefacts.)

  2. The rectangle is as high as register/height and as wide as <number of boxes> * register/width.

    This allows you to change the size of the nodes without having to hard-coding them into the style.

  3. There are three loops necessary:

    1. The .list one from the dim list helper that evaluated how many boxes are needed and sets the minimum width of the node accordingly.
    2. The first \foreach that steps through the user-given list.
    3. The second \foreach that draws the vertical lines.
      (The \the\numexpr\registerNodeWidth-1\relax makes it so that the last one that would fall on the right border of the node is not drawn. It would be under the border of the node anyway.)
  4. The [style/.expand once=\Color] with the \fill command allows one to use arbitrary styles in the list, for example

    {
      1/red,
      1/{left color=red, right color=blue!50},
      1/{left color=blue!50, right color=green},
      1/green,
      1/{pattern=crosshatch, pattern color=gray}
    }
    

    and, of course, also your own.

  5. The label is not part of the register node and will not be considered when you place the node via below=of … or similars.

If you want the registers to be left-aligned, there are options for this, too, aside from just manually doing below=of ….south west, anchor=north west, for example a \matrix where all nodes are placed with anchor=west.

Code

\documentclass[tikz, border=5pt, convert]{standalone}
\usetikzlibrary{patterns, shadings, positioning, ext.misc}
\pgfkeys{
  /utils/dim list/.style={
    /utils/exec=\def\registerNodeWidth{0},
    /utils/@dim list/.list={#1}},
  /utils/@dim list/.code args={#1/#2}%
    {\edef\registerNodeWidth{\the\numexpr\registerNodeWidth+#1}}}
\tikzset{
  draw,
  register/width/.initial=1cm, register/height/.initial=1cm,
  register/dotted/.style={dotted}, register/dashed/.style={dashed},
  every register node/.style={draw, thick},
  register node/.value required,
  register node/.style 2 args={
    shape=rectangle, every register node, /utils/dim list={#2},
    minimum width=\registerNodeWidth*\pgfkeysvalueof{/tikz/register/width},
    minimum height=\pgfkeysvalueof{/tikz/register/height},
    path picture={
      \foreach[
        remember=\totalWidth as \lastTotalWidth (initially 0),
        evaluate={\totalWidth=\lastTotalWidth+\Width;}]
        \Width/\Color in {#2}
        \fill[style/.expand once=\Color]
          ([xshift=\lastTotalWidth*\pgfkeysvalueof{/tikz/register/width}]%
            path picture bounding box.south west)
          rectangle ++(\Width*\pgfkeysvalueof{/tikz/register/width},
                              \pgfkeysvalueof{/tikz/register/height});
      \foreach \cnt in {1,...,\the\numexpr\registerNodeWidth-1\relax}
        \draw[every register node,
          /utils/if={mod(\cnt,2)}{register/dotted},
          /utils/if={mod(\cnt,4)==2}{register/dashed}]
         ([xshift=\cnt*\pgfkeysvalueof{/tikz/register/width}]%
           path picture bounding box.south west)
           -- ++ (up:\pgfkeysvalueof{/tikz/register/height});
    },
    label={[anchor=south west]north west:$#1$}}}
\begin{document}
\begin{tikzpicture}
\node             [register node={R_1}{4/gray, 4/pink}]            (A) {};
\node[below=of A] [register node={R_2}{3/gray, 2/pink, 3/magenta}] (B) {};
\node[below=of B] [register node={R_3}{
  1/red,
  1/{left color=red, right color=blue!50},
  1/{left color=blue!50, right color=green},
  1/green,
  1/{pattern=crosshatch, pattern color=gray}
}] (C) {};
\end{tikzpicture}
\end{document}

Output

enter image description here

2
  • Wow. I'm not sure I really understand all that this is doing, but it looks truly amazing. Many, many thanks!
    – Ian
    Nov 7, 2022 at 20:54
  • @Ian You can comment out the path picture or part of it or the options that are used to see for yourself what is doing what. Or ask specifics. Nov 7, 2022 at 22:29

You must log in to answer this question.

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