Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I know thanks to @TorbjørnT. that messing with nodes and matrix of nodes is not recommended as you would no more be able to use (m-1-1) for a coordinate of a cell of a matrix m. (cf. Compilation problem with tikz diagram)

Now my question is: I define a node shape like that

\newcommand{\bSum}[4]{ 
\node [draw=\linecolor, circle,minimum size=1.5em, \linetype, fill=\backgroundcolor, drop shadow={opacity=\dark}] (sum) at (0,0) {}; 
\node [draw, cross out, minimum size=1.0em, thin, draw=gray!50] {}; 
\node [above of= sum, node distance = 0.45em] () { \color{\colortext}\tiny{$#1$}}; 
\node [left of= sum, node distance = 0.45em] () { \color{\colortext}\tiny{$#2$}}; 
\node [below of= sum, node distance = 0.45em] () { \color{\colortext}\tiny{$#3$}}; 
\node [right of= sum, node distance = 0.45em] () { \color{\colortext}\tiny{$#4$}}; 
}

There is no way for me to use only \draw command to design such a shape because of its arguments.

If I try to use this (now compilable) code, if I uncomment the \draw ... it won't work:

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{matrix}
\usepgflibrary{shapes.misc}
\usetikzlibrary{arrows,shadows}

\newcommand{\bSum}[4]{ 
\node [draw, circle,minimum size=1.5em, fill=white, thick, drop shadow={opacity=1}] (sum) at (0,0) {}; 
\node [draw, cross out, minimum size=1.0em, thin, draw=gray!50] {}; 
\node [above of= sum, node distance = 0.45em] () {\tiny{$#1$}}; 
\node [left of= sum, node distance = 0.45em] () {\tiny{$#2$}}; 
\node [below of= sum, node distance = 0.45em] () {\tiny{$#3$}}; 
\node [right of= sum, node distance = 0.45em] () {\tiny{$#4$}}; 
}

\begin{document}
\begin{tikzpicture}
    \matrix (m) [matrix of nodes, ampersand replacement=\&, column sep = 1.5cm, row sep = 1.2cm]{
        \bSum{+}{+}{-}{}\\
    };
    %\draw (m-1-1) -- (0,0); % <- where it fails
\end{tikzpicture}

\end{document}

Does a trick exist to allow me to get the same result WITH the possibility to access to (m-1-1) ?

share|improve this question

3 Answers 3

up vote 7 down vote accepted

I preferred writing another answer instead of editing my previous one, because this one addresses some new issues and provides a different solution.

Note: This is a very long answer which contains gory details about inner workings of matrix of nodes, pic, etc.. If you are only interested in the final solution, jump to section "Final solution".

The ideal (but almost impossible) way:

I like the pic approach. However, as stated in my other answer, once you put a \draw command in a cell of a matrix of nodes, you break the automatic naming system.

To understand why (and a possible solution) we have to understand first what tikz does in a matrix of nodes. Basically, if you put inside a cell the text:

\matrix (m) [matrix of nodes, ... other options... ] {
   |whatever| foo \& ... \\
   ...
};

Tikz generates for that cell the command:

    \node (m-1-1) whatever {foo}; 

I.e. it automatically puts a \node commando, assigns an automatic name to the node, pass whatever options you wrote inside the vertical bars, and finally the contents of the cell are passed to the argument (content) of the node.

However, if the content of the cell is \path, \draw, \fill, or \node, none of the above is done, but instead the content of that cell is whatever drawing command you specified. So, if you have:

\matrix (m) [matrix of nodes, ... other options... ] {
   \draw pic {bSum}; \& ... \\
   ...
 };

Then that cell contains only \draw pic {bSum}; and thus no automatic node name is assigned.

This explains why it does not work. But it also cast some light about how to make it work.

pic elements can be part of a \draw or \path command, as we know, but I discovered that they can be also part of a \node command if they are used after the node content. I.e: the following does not work (in general, for any pic shape):

\node pic {bSum} {node contents};

But the following does work:

\node {node contents} pic {bSum};

and places at the same coordinates the node contents and the pic.

So this should work, even if it is a horrible hack:

\matrix (m) [matrix of nodes, ... other options... ] {
   |{} pic{bSum};|  IGNORED TEXT \& ... \\
   ...
};

Because that cell will be expanded as:

\node {} pic{bSum}; {IGNORED TEXT};

Note that the IGNORED TEXT is not part of any tikz command, so it will be ignored by tikz (in fact, it will be typeset with the null font).

The following code is a proof of concept (note that I've simplified the pic code compared with my other answer, but this is unrelated to the characteristic that we are studying here):

\documentclass{article}
\usepackage{tikz}
\begin{document}
\usetikzlibrary{calc,shadows,matrix}
\usepgflibrary{shapes.misc}

\tikzset{
    myShape/.style = {
        circle, minimum size=1.5em, fill, drop shadow={opacity=1},
        draw, thick
    },
    myCross/.style = {
        draw, cross out, minimum size=1.0em, thin, draw=gray!50,
    },
    bSum/.pic = {
        \node [myShape] at (0,0) {};
        \node [myCross] at (0,0) {};
        \foreach \t [count=\i] in {#1}{
            \pgfmathsetmacro{\angle}{\i*90}
            \node[anchor=center] at (\angle:0.45em) {\tiny$\t$};
         }
    },
}

\begin{tikzpicture}
 \matrix (m) [matrix of nodes, ampersand replacement=\&,
             column sep = 1.5cm, row sep = 1.2cm]{
    |{} pic[fill=yellow!20] {bSum={+,+, ,-}};| IGNORED TEXT \\
    A \\
 };
\draw (m-1-1) -- (m-2-1);  % <-- IT WORKS!! :D
\end{tikzpicture}
\end{document}

So the syntax works, but it is a horrible hack, and besides, the result is:

Result

You can see that the line does not end at the edge of the circle, nor at its center, but a bit "inside". This is because (m-1-1) is the name of the empty node what we put in that position of the matrix, which has a default minimum size.

A possible solution is to specify additional options for this empty node, so that it has circular shape of the same radius than the pic:

\begin{tikzpicture}
 \matrix (m) [matrix of nodes, ampersand replacement=\&,
             column sep = 1.5cm, row sep = 1.2cm]{
    |[circle, minimum size=1.5em] {} pic[fill=yellow!20] {bSum={+,+, ,-}};| IGNORED TEXT \\
    A \\
 };
 \draw (m-1-1) -- (m-2-1);
\end{tikzpicture}

And it would work, but the syntax is uglier and uglier...

A different approach

A different approach is to use a name for the pic, and then use that name to refer to that node, instead of the standard (m-1-1). This is the idea:

\matrix (m) [matrix of nodes, ... other options... ] {
   \draw pic[options] (myName) {bSum} \& ... \\
   ...
};

If you give a name to a pic in this way, this name does not refer to any particular node, but it is instead a name prefix for any node name defined inside the pic. So we can give a name to the circular node which makes the shape, for example -edge, and thus, we can refer to (myName-edge) from any other part of this tikzpicture.

This looks like a good idea. However, in this particular case more problems appear due to the use of drop shadow. Apparently, this option uses the node named (current bounding box) for its computations. But since it appears inside a pic whose namespace is myName, tikz gets confused and tries to use instead the node named (myNamecurrent bounding box) which does not exist.

To avoid this problem we have to "reset" the namespace when drawing the shadow, via name prefix .. option. So we have to draw the shape of the bSum in two phases: one without namespace prefixes for drawing the shadow, other with namespace prefixes for drawing the circular contour and give it a name available from outside. So...

The final solution

\documentclass{article}
\usepackage{tikz}
\begin{document}
\usetikzlibrary{calc,shadows,matrix}
\usepgflibrary{shapes.misc}

\tikzset{
    myShape background/.style = {  % For the first phase
        circle, minimum size=1.5em, fill, drop shadow={opacity=1},
        name prefix ..
    },
    myShape edge/.style = { % For the second phase
        draw, circle, minimum size=1.5em, thick, 
    },
    myCross/.style = {
        draw, cross out, minimum size=1.0em, thin, draw=gray!50,
    },
    bSum/.pic = {
        \node [myShape background] at (0,0) {};  % First phase
        \node [myCross] at (0,0) {};
        \foreach \t [count=\i] in {#1}{
            \pgfmathsetmacro{\angle}{\i*90}
            \node[anchor=center] at (\angle:0.45em) {\tiny$\t$};
         }
        \node [myShape edge] (-edge) at (0,0) {}; % Second phase, give it a name
    },
}

\begin{tikzpicture}
 \matrix (m) [matrix of nodes, ampersand replacement=\&,
             column sep = 1.5cm, row sep = 1.2cm]{
    \draw pic[fill=white] (S) {bSum={+,+, ,-}}; \\
    A \\
 };
 \draw[latex-] (S-edge) -- (m-2-1);
\end{tikzpicture}
\end{document}

Note the (S) option we passed to pic. This causes S to be the namespace prefix, so the (-edge) named node inside the pic is available from outside under the name (S-edge). This code produces the desired result:

Final result

Of course, nothing stops you to give (m-1-1) as namespace prefix instead of (S). However, remember that this does not define a node named (m-1-1), but a namespace prefix. You still have to use (m-1-1-edge) to refer to the actual pic shape.

Update: the final hack

I realized that if you give the namespace (m) to the pic element, and inside it you give the name (-1-1) to the last circular node, then you can access to (m-1-1) from anywhere in your tikzpicture to refer to the circular node in that cell. Indeed, you can use the counters \pgfmatrixcurrentrow and \pgfmatrixcurrentcolumn as part of the node name in the pic, and thus finally achieve the desired goal.

The following code shows a complete example:

\documentclass{article}
\usepackage{tikz}
\begin{document}
\thispagestyle{empty}
\usetikzlibrary{calc,shadows,matrix}
\usepgflibrary{shapes.misc}

\tikzset{
    myShape background/.style = {  % For the first phase
        circle, minimum size=1.5em, fill, drop shadow={opacity=1},
        name prefix ..
    },
    myShape edge/.style = { % For the second phase
        draw, circle, minimum size=1.5em, thick,
    },
    myCross/.style = {
        draw, cross out, minimum size=1.0em, thin, draw=gray!50,
    },
    bSum/.pic = {
        \node [myShape background] at (0,0) {};  % First phase
        \node [myCross] at (0,0) {};
        \foreach \t [count=\i] in {#1}{
            \pgfmathsetmacro{\angle}{\i*90}
            \node[anchor=center] at (\angle:0.45em) {\tiny$\t$};
         }
        \node [myShape edge]      % Second phase, give it a name
            (-\the\pgfmatrixcurrentrow-\the\pgfmatrixcurrentcolumn)
            at (0,0) {}; 
    },
}

\begin{tikzpicture}
 \matrix (m) [matrix of nodes, ampersand replacement=\&,
            column sep = 1.5cm, row sep = 1.2cm, nodes={anchor=center}]{
    \draw pic[fill=white] (m) {bSum={+,+,-,}};     \&                  B \\
     A  \& \draw pic[fill=green!10] (m) {bSum={,\odot,\smile,\prec}}; \\
 };
 \draw[-latex] (m-2-1) -- (m-2-2);  % <-- It works!
 \draw[-latex] (m-1-1) -- (m-2-2);  % <-- It works!
\end{tikzpicture}
\end{document}

Result:

Final hack result

share|improve this answer
    
Otra respuesta para que quitarse el sombrero! y van ... (I'm sorry but my english is not enough good to translate it) –  Ignasi Jun 10 at 19:05
    
@JLDiaz to be even more generic, you should instead of name prefix .. (which seems to be usable only one), use \tikzmatrixname so that you could define more shapes... –  s__C Jun 10 at 19:54
    
@Ignasi Gracias! :-) –  JLDiaz Jun 11 at 7:10
    
@s__C I don't understand what do you mean with "usable only one" and "define more shapes". Do you refer to give more names to nodes inside the pic, or to define other pics to be used in other places of the matrix? Could be more specific about the problems when using "more shapes"? Perhaps it may worth posting another question? –  JLDiaz Jun 11 at 7:13
    
@JLDiaz nah but I have found the trick to define other pics to be used in the matrix while keeping (m-x-y) usable. You need (\tikzmatrixname-\the\pgfmatrixcurrentrow-\the\pgfmatrixcurrentcolumn) instead of just the suffix and by deleting name prefix .. in the style –  s__C Jun 11 at 8:05

See update for a solution using pic

Original answer

Apparently the code behaves differently depending on the PGF version in use.

With PGF 2.10, the following code (which is basically OP's code with a second row in the matrix to make more visible the later drawn line), compiles without problems:

\documentclass{article}
\usepackage{tikz}
\begin{document}
\thispagestyle{empty}
\usetikzlibrary{matrix}
\usepgflibrary{shapes.misc}
\usetikzlibrary{arrows,shadows}

\newcommand{\bSum}[4]{ 
\node [draw, circle,minimum size=1.5em, thick, fill = white, drop shadow={opacity=1}] (sum) at (0,0) {}; 
\node [draw, cross out, minimum size=1.0em, thin, draw=gray!50] (sum) {}; 
\node [above of= sum, node distance = 0.45em] () {\tiny{$#1$}}; 
\node [left of= sum, node distance = 0.45em] () {\tiny{$#2$}}; 
\node [below of= sum, node distance = 0.45em] () {\tiny{$#3$}}; 
\node [right of= sum, node distance = 0.45em] () {\tiny{$#4$}}; 
\node [draw, circle,minimum size=1.5em, thick] at (0,0) {}; 
}

\begin{tikzpicture}
    \matrix (m) [matrix of nodes, ampersand replacement=\&, column sep = 1.5cm, row sep = 1.2cm]{
        \bSum{+}{+}{-}{}\\
        A \\
    };
    \draw (m-1-1) -- (m-2-1); % <- where it fails
\end{tikzpicture}
\end{document}

And produces:

Result

However, the same code with PGF 3.0.0 produces the error:

! Package pgf Error: No shape named m-1-1 is known.

This can be fixed by making the \bSum macro to create a new tikz picture, i.e:

\newcommand{\bSum}[4]{ 
\tikz{
  \node [draw, circle,minimum size=1.5em, thick, fill = white, drop shadow={opacity=1}] (sum) at (0,0) {}; 
  \node [draw, cross out, minimum size=1.0em, thin, draw=gray!50] (sum) {}; 
  \node [above of= sum, node distance = 0.45em] () {\tiny{$#1$}}; 
  \node [left of= sum, node distance = 0.45em] () {\tiny{$#2$}}; 
  \node [below of= sum, node distance = 0.45em] () {\tiny{$#3$}}; 
  \node [right of= sum, node distance = 0.45em] () {\tiny{$#4$}}; 
  \node [draw, circle,minimum size=1.5em, thick] at (0,0) {}; 
  }
}

The result, however is sligthly different:

Result2

Oddly enough, this "fix" doesn't work with PGF 2.10.

Update: using PGF 3.0.0 pics

PGF 3.0.0 introduces a new feature called pic which allows you to define arbitrary shapes which can be placed anywhere in a path.

1. Defining the pic

Using this feature (and defining some styles to simplify the code) you can have:

\tikzset{
    myShape/.style = {
        draw, circle, minimum size=1.5em, thick, 
        fill, drop shadow={opacity=1}
    },
    myCross/.style = {
        draw, cross out, minimum size=1.0em, thin, draw=gray!50,
    },
    myLabel1/.style = {
        above of=sum, node distance=0.45em,
    },
    myLabel2/.style = {
       left of=sum, node distance=0.45em,
    },
    myLabel3/.style = {
        below of=sum, node distance=0.45em,
    },
    myLabel4/.style = {
        right of=sum, node distance=0.45em,
    },
    pics/bSum/.style = {
        code = {
        \node [myShape] (sum) at (0,0) {};
        \node [myCross] at (sum) {};
        \foreach \t [count=\i] in {#1}
            \node[myLabel\i] {\tiny$\t$};
        }
    },
}

The interesting part is the definition of pics/bSum/.style which has a key named code which draws your shape. Note that #1 is used inside the code, expecting to have a comma-separated list of labels for each quadrant. You can use it as part of any \draw, like this:

\begin{tikzpicture}
  \draw[fill=white] (0,0) pic {bSum={+,+,-, }};
\end{tikzpicture}

to get:

pic

Note that you have to specify the fill color in the \draw, because I did not specify it in the style myShape. I preferred doing it this way, so you have flexibility of drawing the same pic with different background colors if desired.

2. Using it in a matrix of nodes.

Unfortunately, if you try to use it directly in the place of a node inside a matrix of nodes, like this:

\matrix (m) [matrix of nodes, ampersand replacement=\&, 
            column sep = 1.5cm, row sep = 1.2cm]{
   \draw[fill=white] pic {bSum={+,+,-, };
};

you'll run in the same problems you got with your first approach. Using drawing commands in the place of a node in a matrix of nodes, interferes with node auto-naming mechanism, and then you won't have access to (m-1-1) node name.

Hackish workaround

The only solution I was able to imagine is not to draw those shapes in place of nodes. Instead, leave those nodes with some "dummy" placeholder. Then, after the matrix is completed, you can go to (m-1-1) and draw there the desired shape.

The following code shows this approach. In this code I used some numbers as placeholders, because the pic has an opaque background that will hide them. You can use instead whitespace (\). I also show how the same pic can appear with different background colors and different symbols inside (which are pased as parameters).

\begin{tikzpicture}
    \matrix (m) [matrix of nodes, ampersand replacement=\&, 
                 column sep = 1.5cm, row sep = 1.2cm]{
       1 \& 2 \& 3 \\
       4 \& 5 \& 6 \\ 
    };
    \draw (m-1-1) -- (m-2-1); 
    \draw (m-2-2) -- (m-1-3); 
    \draw[fill=yellow] (m-1-1) pic{bSum={+,+,-, }};
    \draw[fill=white]  (m-2-2) pic{bSum={+, ,-,+}};
\end{tikzpicture}

This produces:

pic in matrix

share|improve this answer
    
Well this doesn't solve my problem, as the last part with matrix implies to do twice the same job... –  s__C Jun 9 at 14:23

Here is another suggestion based on the pic code from @JLDiaz.

First of all note that the node contents is set in a rectangular box even it is a circle itself. Therefore I use the overlay option to hide the dimensions of the picin a matrix node.

\documentclass[tikz,margin=5mm,convert=false]{standalone}
\usepackage{tikz}
\begin{document}
\usetikzlibrary{matrix}
\usepgflibrary{shapes.misc}
\usetikzlibrary{arrows,shadows}

\tikzset{
   myShape/.style = {
    circle, minimum size=1.5em, thick, 
  },
   mybSum/.style = {
    myShape, 
    draw,fill, drop shadow={opacity=1},
  },
  myCross/.style = {
    draw, cross out, minimum size=1.0em, thin, draw=gray!50,
  },
  myLabel1/.style = {above of=#1}, myLabel2/.style = {left of=#1},
  myLabel3/.style = {below of=#1}, myLabel4/.style = {right of=#1}, 
  myLabelDist/.style = {node distance=0.45em},
  pics/bSum/.style = {
    code = {
      \node [mybSum](sum){};
      \node [myCross]{};
      \foreach \t [count=\i] in {#1}
        \node[myLabelDist, myLabel\i=sum] {\tiny$\t$};
      \node [myShape,draw]{};
    }
  },
}

\newcommand\bSum[2]{|[myShape]|\tikz\pic[overlay,fill=white,#1]{bSum={#2}};}

\begin{tikzpicture}
  \matrix (m) [matrix of nodes, ampersand replacement=\&, column sep = 1.5cm, row sep = 1.2cm]{
    \bSum{}{+,+,-,}\&\bSum{fill=yellow,draw=red,text=green}{-,,,+}\\
    A \& B\\
  };
  \path[<->] (m-2-1) edge (m-1-1)
                     edge (m-1-2);
\end{tikzpicture}
\end{document}

The command \bSum has two mandantory arguments: #1 influences the layout of the pic and can be empty, #2 contains the comma-separated list of labels for each quadrant.

enter image description here

share|improve this answer
    
Interesting, but due to the overlay option, this won't work with an arrow (->) between nodes... –  s__C Jun 10 at 5:05
    
@s__C I have added some arrows between the matrix nodes in the example. –  esdd Jun 10 at 7:24
    
OK that's fine. But, there is a but, if I had defined a circle with text inside, ie not a fixed size, this wouldn't work. –  s__C Jun 10 at 17:32

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.