4

I'd like to use pics to easily and quickly create new node shapes. Sadly, I can't find how to use internal nodes to define anchors. For instance, I'd like the B to point to the small x in the ground symbol:

enter image description here

MWE:

\documentclass{article}
\usepackage{amsmath}
\usepackage{tikz-cd}

\begin{document}

\tikzset{
  groundpic/.pic={
    % Code for the ground
    \draw[line width=.4pt]
    (0,0) -- (1mm,0)
    -- +(0,1mm) -- +(0,-1mm)
    ++(.4mm,0)
    -- +(0,.7mm) -- +(0,-.7mm) node[anchor=center,scale=.5,name=myanchor] {x}
    ++(.4mm,0)
    -- +(0,.35mm) -- +(0,-.35mm)
    ;
  },
  ground/.style={
    shape=coordinate, % Otherwise the arrow will not point to the center
    append after command={
      pic[scale=2] {groundpic}
    },
  },
}

Goal: add an arrow to x, but end anchor=myanchor fails:
\noindent\begin{tikzcd}
  A \rar & |[ground]| & \\
  B \ar[
    ru,
    % end anchor=myanchor
  ]
\end{tikzcd}


\end{document}
3
  • 1
    This seems related. Though maybe it would be better to name the pic based on the cell, i.e. matrixname-row-column and name the node -myanchor which would be actually named matrixname-row-column-myanchor and use that as the to from the \arrow. Aren't these shapes already defined by circuit libraries/packages? Feb 21, 2023 at 11:39
  • Please clarify, where should be label ground symbol? (for example, above it?). If you like to draw electrical schemes, tikz-cd is not right tool, See circuitikz package,
    – Zarko
    Feb 21, 2023 at 11:40
  • @Zarko thanks, @Qrrbrbirlbel answered nicely my question. Regarding circuitikz, I'm actually building my own library (zx-calculus on CTAN) and circuitikz was not really meant for this kind of diagrams as I want a matrix layout.
    – tobiasBora
    Feb 22, 2023 at 0:39

3 Answers 3

5
+200

A node can't be a real anchor. An anchor is always one single point. It can't be another node.

Two things can be done, though:

  • We can add an anchor to an existing node (or coordinate which is just a special node).

    For this, I adjusted my answer to “path picture add custom anchor” which was problematic but didn't show in the linked answer. This should be more robust now.

  • We can name a node in a pic which is actually named <pic name><node name> on the global scale. (TikZ sets up name prefix = <pic name> which means every named coordinate or node uses this prefix unless name prefix .. is given.)

    If we also name the node the same as a matrix of (math) nodes names its cells' nodes we can incorporate this easily into TikZ-CD (see the adjustments to the ground style).


Now, we could actually just use

\ar[ru, name suffix=<node name>]

but this would apply for both start and target and for every coordinate/node we define on the way.

And for this small example it only applies for the start not the target?

\begin{tikzcd}[arrows=help lines]
  |[ground]| \ar[r, bend right=90, name suffix=-east]
& |[ground]| \ar[l, bend right=90, name suffix=-east]
\end{tikzcd}

enter image description here

Weird. (When TikZ encounters a node coordinate specification, i.e. (<node name>) it first tries to find a node with both name prefix and suffix and if it doesn't find that it checks for a node without these added.)

This is why I propose the keys from pic node and to pic node (which also adds the - for you):

\begin{tikzcd}[arrows=help lines]
  |[ground]| \ar[r, bend right=90, from pic node=east, to pic node=east]
& |[ground]| \ar[l, bend right=90, from pic node=east, to pic node=east]
\end{tikzcd}

enter image description here

These keys should be use after the target is specified, i.e. after ru or the usage of to/from otherwise the setting is lost again.

Code

\documentclass[tikz]{standalone}
%\documentclass{article}
\usepackage{amsmath}
\usepackage{tikz-cd}
\makeatletter
\tikzset{
  add anchor to node/.code n args={3}{%
    \edef\tikz@temp##1{% \tikz@pp@name/\tikzlastnode needs to be expanded
      \noexpand\pgfutil@g@addto@macro\expandafter\noexpand\csname
        pgf@sh@ma@\tikz@pp@name{#1}\endcsname{%
        \def\expandafter\noexpand\csname pgf@anchor@\csname
          pgf@sh@ns@\tikz@pp@name{#1}\endcsname @#2\endcsname{##1}%
      }%
    }%
    \tikz@temp{#3}%
  },
  add anchor to pic'/.style={/tikz/add anchor to pic={#1}{-#1}},
  add anchor to pic/.code 2 args={%
    % If base coordinate isn't created yet, do so at (0,0)
    % in the current coordinate system!
    \pgfutil@ifundefined{pgf@sh@ns@\tikz@pp@name{}}{%
      \pgfcoordinate{\tikz@pp@name{}}{\pgfpointorigin}%
    }{}%
    \begingroup
      % What is the distance between coordinate and base?
      % We have to do this in the coordinate system of the base coordinate.
      \pgfsettransform{\csname pgf@sh@nt@\tikz@pp@name{}\endcsname}%
      \pgf@process{\pgfpointanchor{\tikz@pp@name{#2}}{center}}%
      % This distance is the new anchors coordinate
      % in the base's coordinate system.
      % Adding an anchor to a node must be global
      % which is why we can do this inside the group.
      \pgfkeysalso{
        /tikz/add anchor to node/.expanded=%
          {}{#1}{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}
      }%
    \endgroup
  }%
}
\tikzcdset{
  from pic node/.code=\edef\tikzcd@ar@start{\tikzcd@ar@start-#1},
  to pic node/.code=\edef\tikzcd@ar@target{\tikzcd@ar@target-#1}}
\makeatother
\tikzset{
  groundpic/.pic={
    % Code for the ground
    \draw[line width=.4pt] (0,0) -- (1mm,0)
         +(0, 1mm) coordinate(-t1) -- +(0,-1mm) coordinate(-b1)
        ++(.4mm,0) +(0,.7mm) coordinate(-t2) -- +(0,-.7mm) coordinate(-b2)
        ++(.4mm,0) +(0,.35mm) coordinate(-t3)--  +(0,-.35mm)coordinate(-b3)
        node[scale=.4, below, rotate=90, midway, inner sep=+3pt, rectangle]
          (-east) {Ground};
%    \node[draw, help lines](){4444};
%    \coordinate(-b4) at (.west);
    \tikzset{add anchor to pic'/.list={t1, b1, t2, b2, t3, b3}}
  },
  ground/.style={
    shape=coordinate, yshift=axis_height,
    append after command={
      pic[scale=2,at=(\tikzlastnode),name=\tikzlastnode]{groundpic}
    }
  }
}
\begin{document}
\begin{tikzcd}
  A \rar & |[ground]| \\
  B
  \foreach \num in {1,2,3}{
    \foreach\tORb/\leftORright in {t/left,b/right}{
      \expanded{\noexpand\ar[
        bend \leftORright, -, help lines, ru , end anchor=\tORb\num]}
    }
  }
  \ar[ru, out=0, in=-30, "Here's the ground"' math mode=false, name suffix=-east]
\end{tikzcd}
\begin{tikzcd}[arrows=help lines]
  |[ground]| \ar[r, bend right=90, name suffix=-east]
& |[ground]| \ar[l, bend right=90, name suffix=-east]
\end{tikzcd}
\begin{tikzcd}[arrows=help lines]
  |[ground]| \ar[r, bend right=90, from pic node=east, to pic node=east]
& |[ground]| \ar[l, bend right=90, from pic node=east, to pic node=east]
\end{tikzcd}
\end{document}

Output

enter image description here

9
  • Thank you sooo much, this last robust definition of add anchor to pic together with name=\tikzlastnode (actually, can't this cause some conflicts as now two nodes have the same name?) is exactly what I needed!
    – tobiasBora
    Feb 22, 2023 at 0:31
  • 1
    @tobiasBora No, only one node is named \tikzmatrixname-<row>-<col>: the coordinate you declare in the ground style. While the pic is syntactally named the pic isn't actually an object that has (and keeps) a name. All a named pic does is setup the the appropriate name prefix. The coordinate and nodes inside the pic all use that prefix and then what. If you would name a coordinate () it would actually named like the coordinate from ground but that one is just lost then (and so would any anchors we added to it). Feb 22, 2023 at 14:30
  • I see, thanks a lot for the clarification!
    – tobiasBora
    Feb 22, 2023 at 20:18
  • Actually, it's even better than I would imagine: if I define a node with an empty name, it replaces the node of the matrix, therefore stuff like \rar will not go through the node, and will stop on its boundary :-D
    – tobiasBora
    Feb 23, 2023 at 0:25
  • 1
    @tobiasBora I've edited the answer. Take a look at the \pgf@process line. Since I've set up the node's coordinate system I don't actually need to calculate a difference between points. Since a coordinate has all anchors and points at its local origin (i.e. the coordinate itself) it made no difference what anchor has been used. A real node has its origin not at the center but at the text anchor. Anyway, we don't need to know where the origin is since that's why we set up the node's coordinate system. Mar 15, 2023 at 0:22
3

enter image description here

I think your problem comes from the fact that end anchor takes as argument only a coordinate defined by the corresponding element in the diagram, i.e. the element the arrow points to.

In the figure, the black arrow starting at B uses end anchor=240, i.e. it ends to the point on the border of the top right element defined by the angle 240 with respect to the Ox axis. The red arrow is a normal one.

Remark I slightly redefined your pic object; I think, in case you want to use it in a diagram, it is better to define it using relative length units.

The code

\documentclass[11pt, margin=5mm]{standalone}
\usepackage{amsmath}
\usepackage{tikz-cd}

\begin{document}

\tikzset{
  pics/ground pic/.style={%
    code={%
      \draw
      (0, 0) -- +(0, 1.5ex) -- +(0,-1.5ex)
      ++(.5ex, 0) -- +(0, 1ex) -- +(0, -1ex)
      node[anchor=center, scale=.5, outer sep=2ex] (-g) {x}
      ++(.5ex, 0) -- +(0, .65ex) -- +(0, -.65ex);
      \draw (0, 0) -- (-1ex, 0);
    }
  },
  ground/.style={%
    outer xsep=.3ex, outer ysep=1ex, 
    insert path={%
      pic[yshift=.5ex] {ground pic}
    }
  }  
}

\begin{tikzcd}
  A \arrow[r] & |[ground]|\phantom{A} \\
  B \arrow[ru, end anchor=240]
  \arrow[ru, red]
\end{tikzcd}

\end{document}
2
  • Thanks a lot for your answer… sadly I need more precision than a manually tunes anchor, first because I might want to go inside the node, and also because I'm designing a library to automatically do this nasty computations… and most of the (dirty) code I have so far really relies on custom anchors.
    – tobiasBora
    Feb 21, 2023 at 15:42
  • Well, I guess that in that case tikzcd is not the right choice; you would need more liberty in using nodes embedded into pic elements. To achieve this you might need tikz instead.
    – Daniel N
    Feb 21, 2023 at 19:47
3

I am unsure whether tikz-cd is the right thing to do this, but maybe you have something special in mind.

As was already proposed in the comments, you can name coordinates and nodes inside a pic and then later refer to them by appending these names to the name of the pic. So, if you, for example, name the node with the small x -myanchor and the pic mypic, then you can refer to the node with the small x inside this pic using mypic-myanchor. Since it is a node, you can also refer to its anchors, for example with mypic-myanchor.south.

Now, the next problem is how to tell TikZ to draw an arrow to a certain coordinate that is not the one it would typically point to when using the \arrow (or \ar) macro inside a tikzcd environment. This can be done by styling this arrow in a way so that the to-path is replaced by another path that has the relevant coordinate (or anchor) as target.

A possible solution would be as follows:

\documentclass{standalone}
\usepackage{tikz-cd}

\tikzset{
  groundpic/.pic={
    % Code for the ground
    \draw[line width=.4pt]
    (0,0) -- (1mm,0)
    -- +(0,1mm) -- +(0,-1mm)
    ++(.4mm,0)
    -- +(0,.7mm) -- +(0,-.7mm) node[anchor=center,scale=.5] (-myanchor) {x}
    ++(.4mm,0)
    -- +(0,.35mm) -- +(0,-.35mm)
    ;
  },
  ground/.style={
    shape=coordinate, % Otherwise the arrow will not point to the center
    append after command={
      pic[scale=2] (#1) {groundpic}
    },
  },
  custom target/.style={
    to path={
      -- (#1)
    }
  }
}

\begin{document}
\begin{tikzcd}
  A \rar & |[ground=mypic]| & \\
  B \ar[
    ru,
    custom target=mypic-myanchor.south
  ]
\end{tikzcd}
\end{document}

enter image description here


Thanks to Qrrbrbirlbel, I learned that tikz-cd already provides an option to that does exactly what I tried using my own custom target option. So, you can also just do the following:

\documentclass{standalone}
\usepackage{tikz-cd}

\tikzset{
  groundpic/.pic={
    % Code for the ground
    \draw[line width=.4pt]
    (0,0) -- (1mm,0)
    -- +(0,1mm) -- +(0,-1mm)
    ++(.4mm,0)
    -- +(0,.7mm) -- +(0,-.7mm) node[anchor=center, scale=.5] (-myanchor) {x}
    ++(.4mm,0)
    -- +(0,.35mm) -- +(0,-.35mm)
    ;
  },
  ground/.style={
    shape=coordinate, % Otherwise the arrow will not point to the center
    append after command={
      pic[scale=2] (#1) {groundpic}
    },
  }
}

\begin{document}
\begin{tikzcd}
  A \rar & |[ground=mypic]| & \\
  B \ar[
    ru,
    to=mypic-myanchor.south
  ]
\end{tikzcd}
\end{document}
5
  • TikZ-CD has to = mypic-myanchor.south without having to adjust the to path. So, \ar[ru, bend right, to=mypic-myanchor] is still possible. Feb 21, 2023 at 14:49
  • @Qrrbrbirlbel Oh, I did not know this! Then, it is even easier to solve this. Thanks! Feb 21, 2023 at 15:36
  • Did you get the name= for pic references to work? I ask because it was broken. I'd be interested if you've got a workaround or fixed version - or maybe this is tikz-cd specific?
    – cfr
    Oct 4, 2023 at 16:43
  • @cfr Not sure what you mean, but if I replace pic[scale=2] (#1) {groundpic} by pic[scale=2, name=#1] {groundpic} in both examples, it seems to work fine with a current TeX distribution. Oct 4, 2023 at 17:18
  • 1
    The issue is (or was?) if you tried to use something like <name>-<something> after defining the coordinate -<something> in the pic. This worked in the pic with just -<something> but later references to <name>-<something> failed. In particular, the example of this from the manual failed. The prefix- system was (is?) broken. But I should just try it out since you don't know what I'm talking about ;).
    – cfr
    Oct 4, 2023 at 17:27

You must log in to answer this question.

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