5

With the MWE below, I get the following figure:

enter image description here

Could someone explain me why in the right figure, the red circle is not "inside" the square? I guess it has to do with the square being a node, and not the circle, but I do not understand how to properly use these anchors when making more complex figures.

Ideally, I would need to use many times some "subfigures" that I would like to code as TikZ pics. Then I need to create multiple such pictures, and position them relatively to each other (given that it is not so easy to determine their size, absolute positioning would not look good). Any general rules on how to avoid positioning problems in such situations?

\documentclass[a4paper]{article}
\usepackage{tikz,xcolor}
\usetikzlibrary{positioning}
\begin{document}
    \begin{tikzpicture}[
        pics/test/.style n args={0}{
            code={
                \node[minimum width=2cm, minimum height=2cm,draw]  at (0,0) {};
                \draw[] (0,0) circle (1cm); 
                \coordinate (-north) at (0,1);
                \coordinate (-south) at (0,-1);
             }       
        }]
        \pic[blue] (A) at (0, 0) {test};
        \pic[red,below=1cm of A-south,anchor=center] (B) {test};

        \pic[blue] (C) at (3, 0) {test};
        \pic[red,below=1cm of C-south,anchor=north] (D) {test};
    \end{tikzpicture}
\end{document}

[EDIT] Loop Space's solution is convenient, but I have a problem when trying to position more than 2 pics relatively to each other, so I am wondering if I really understood everything. From what I understand, the definition of (-north) and (-south) allows to draw a 2nd pic at the desired position with respect to a first one, but the anchors on this 2nd pic are not positioned at the expected position. Below is a MWE with a proposition of correction (test2, last column in the image), but the code does not look nice. Here is the output, at least to show what I am trying to achieve. I think it is the behavior that one might expect with the notion of anchors, but I might be wrong. What do you think, and any idea how to make this code nicer?

enter image description here

\documentclass[a4paper]{article}
%\url{https://tex.stackexchange.com/q/401980/86}
\usepackage{tikz,xcolor}
\usetikzlibrary{positioning}
\makeatletter
\tikzset{
  shift to anchor/.code={
    \tikz@scan@one@point\pgfutil@firstofone(-\tikz@anchor)\relax
    \pgfkeysalso{shift={(-\pgf@x,-\pgf@y)}}
  }
}
\makeatother

\begin{document}
    \begin{tikzpicture}[
        pics/test/.style={ code={
            \coordinate (-north) at (0,1);
            \coordinate (-south) at (0,-1);
            \coordinate (-center) at (0,0);
            \begin{scope}[shift to anchor]
                \node[minimum width=2cm, minimum height=2cm,draw,anchor=center]  at (0,0) {};
                \draw[] (0,0) circle (1cm);
                \fill (0,0) circle[radius=2pt];
            \end{scope}
        }},
        pics/test2/.style={ code={
            \newcommand\redefineanchors{
                \coordinate (-north) at (0,1);
                \coordinate (-south) at (0,-1);
                \coordinate (-center) at (0,0);
            }
            \redefineanchors{}
            \begin{scope}[shift to anchor]
                \redefineanchors{}
                \node[minimum width=2cm, minimum height=2cm,draw,anchor=center]  at (0,0) {};
                \draw[] (0,0) circle (1cm);
                \fill (0,0) circle[radius=2pt];
            \end{scope}
        }}
        ]
        \pic[blue] (A) at (0, 0) {test};
        \pic[red,below=1cm of A-south,anchor=center] (B) {test};
        \pic[green,below=1cm of B-south,anchor=center] (C) {test};

        \pic[blue] (D) at (3, 0) {test};
        \pic[red,below=1cm of D-south,anchor=north] (E) {test};
        \pic[green,below=1cm of E-south,anchor=north] (F) {test};

        \pic[blue] (G) at (6, 0) {test2};
        \pic[red,below=1cm of G-south,anchor=north] (H) {test2};
        \pic[green,below=1cm of H-south,anchor=north] (I) {test2};
    \end{tikzpicture}
\end{document}
4

There's a few things going on here.

  1. pics don't have anchors. They are positioned so that their internal coordinate system has origin at the stated point, but that's as far as they go. So the anchor=... keys have no effect on the positioning of their respective pics. This is why both circles in the lower pics are placed at the same height.

  2. nodes do have anchors. This might not be the expected behaviour, but it does make a sort of logic, but since the anchor=north option doesn't make sense for the pic, it's interpreted as a pass-through option in that it is inherited by the nodes inside the pic. In particular, it means that the square node is now anchored at its north anchor rather than its centre. This is why the second square is placed lower down.

To fix the second issue, either don't specify the anchor=... option on the pic or explicitly set the anchor option on the node.

To fix the first, we need to introduce a shift inside the pic which simulates the node positioning. The code below is probably not the most elegant, and would need extending to allow for more anchors, but works in this case. If you need more fancy positioning (for example, if the pic were not of a fixed size), then you might want to consider a custom node instead of a pic.

\documentclass[a4paper]{article}
%\url{https://tex.stackexchange.com/q/401980/86}
\usepackage{tikz,xcolor}
\usetikzlibrary{positioning}
\makeatletter
\tikzset{
  shift to anchor/.code={
    \tikz@scan@one@point\pgfutil@firstofone(-\tikz@anchor)\relax
    \pgfkeysalso{shift={(-\pgf@x,-\pgf@y)}}
  }
}
\makeatother

\begin{document}
    \begin{tikzpicture}[
        pics/test/.style n args={0}{
            code={
                \coordinate (-north) at (0,1);
                \coordinate (-south) at (0,-1);
              \coordinate (-center) at (0,0);
              \begin{scope}[shift to anchor]
                \node[minimum width=2cm, minimum height=2cm,draw,anchor=center]  at (0,0) {};
              \draw[] (0,0) circle (1cm);
              \fill (0,0) circle[radius=2pt];
              \end{scope}
            }       
        }]
        \pic[blue] (A) at (0, 0) {test};
        \pic[red,below=1cm of A-south,anchor=center] (B) {test};

        \pic[blue] (C) at (3, 0) {test};
\pic[red,below=1cm of C-south,anchor=north] (D) {test};

    \end{tikzpicture}
\end{document}

Shifted pic

  • Thank you, this makes sense. If I calculate the positions of the anchors in the pics' code, then with your trick it seems that it is a convenient way to achieve what I need. What about creating custom nodes, what would be the benefit, and is it possible to define new nodes using tikz code similar to this one, or should we in this case rely on pgf, which is maybe less readable? The doc says that nodes are "simple shapes with some text on it", so should I really use them for more complex structures? – Zooky Nov 19 '17 at 10:05
  • @Zooky Nodes are much more intricate, and ought to be designed using pgf syntax rather than tikz. I've defined nodes in the past (the original version of the tqft package used them) and they were definitely a step level more complicated than pics (the new version of tqft uses pics and it was with a sigh of relief that I switched). So stick with pics while you can, even if the positioning is a little more complicated than for nodes, and only switch to nodes when you have a month or two to spare to figure out the code! – Loop Space Nov 19 '17 at 10:52
  • OK I answered a bit too fast, there is something tricky: computing the position of the anchors relatively to the pic's (0,0) position allow us to define a scope to draw at the desired place. But if you want the anchors to be at the right position, then you need to overwrite the anchors inside the scope, no ? (Otherwise I get trouble e.g. if I create a pic, a 2nd one in the south with "north anchor", and a 3rd one in the south of the 2nd one, and "north anchor" as well.) This does not look really clean to me, is there an easy workaround? Maybe I misunderstood how your "shift to anchor" works. – Zooky Nov 19 '17 at 22:06
  • @Zooky I think I'd need to see some code to understand what you're trying to do. You could edit it in to your question as providing more detail. – Loop Space Nov 20 '17 at 22:29
  • Ok I actually hesitated to edit the post, looks like I should have. I just did it and added an example showing what I am trying to do. – Zooky Nov 21 '17 at 8:01

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.