[Tex/LaTex] Understanding TikZ anchors using pics

tikz-pgftikz-pic

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}

Best Answer

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