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 was playing around with this question, and in particular tried to modify and expand the linked example. It works fine, but there is an error when I try to use a \foreach loop to define the different groups of anchors. If I use the loop variable directly, I get an "! Undefined control sequence." error. If I use the value of the counter, only the last value the loop takes on is used. However, as one can see all three anchors are defined. So my questions are:

  • Why does that happen?
  • How to work around that?

Code

\documentclass[tikz, border=2mm]{standalone}


\tikzset{%
    multipoles/.is family,
    multipoles,
    pin spacing/.initial=5mm,
    top left pins/.initial=3,
    bottom left pins/.initial=3,
    top right pins/.initial=8,
    bottom right pins/.initial=0,
    top pins/.initial=0,
    bottom pins/.initial=0, 
}

\newcommand{\mpp}[1]%
{   \pgfkeysvalueof{/tikz/multipoles/#1}
}

\newcounter{mypincounter}

\pgfdeclareshape{ic8pin}{
\anchor{center}{\pgfpointorigin} % within the node, (0,0) is the center
\anchor{text} % this is used to center the text in the node
{\pgfpoint{-.5\wd\pgfnodeparttextbox}{-.5\ht\pgfnodeparttextbox}}

\pgfmathtruncatemacro{\lrd}{max(\mpp{top left pins} +\mpp{bottom left pins} +or(\mpp{top left pins},\mpp{bottom left pins}), \mpp{top right pins} +\mpp{bottom right pins} +or(\mpp{top right pins},\mpp{bottom right pins}) == 0 ? 2 : max(\mpp{top left pins} +\mpp{bottom left pins} +or(\mpp{top left pins},\mpp{bottom left pins}), \mpp{top right pins} +\mpp{bottom right pins} +or(\mpp{top right pins},\mpp{bottom right pins})}
\xdef\LRDivisions{\lrd}

\pgfmathtruncatemacro{\tbd}{max(\mpp{top pins} +and(1,\mpp{top pins}), \mpp{bottom pins} +and(1,\mpp{bottom pins}) == 0 ? 2 : max(\mpp{top pins} +and(1,\mpp{top pins}), \mpp{bottom pins} +and(1,\mpp{bottom pins})}
\xdef\TBDivisions{\tbd}

\foreach \x in {1,...,\mpp{top left pins}}
{   \setcounter{mypincounter}{\x}
    \expandafter\savedanchor\csname tlpin\Roman{mypincounter} \endcsname{\pgfpoint{-\TBDivisions*\mpp{pin spacing}/2}{\LRDivisions*\mpp{pin spacing}/2-\mpp{pin spacing}}}
    \anchor{pin\x}{\csname tlpin\Roman{mypincounter} \endcsname}
}

\foregroundpath{ % border and pin numbers are drawn here
\pgfsetlinewidth{0.4pt}

\pgfpathrectanglecorners{\pgfpoint{\TBDivisions*\mpp{pin spacing}/2}{\LRDivisions*\mpp{pin spacing}/2}}{\pgfpoint{-\TBDivisions*\mpp{pin spacing}/2}{-\LRDivisions*\mpp{pin spacing}/2}}
\pgfusepath{draw} %draw rectangle

\pgftext[left,at={\pgfpoint{-.5cm}{-.55cm}}]{\scriptsize LR : \textcolor{red}{\LRDivisions}}
\pgftext[left,at={\pgfpoint{-.5cm}{.55cm}}]{\scriptsize TB : \textcolor{red}{\TBDivisions}}
}}

\begin{document}

\begin{tikzpicture}
\draw (0,0) node[ic8pin] (IC1) {IC 1};
\draw (-5,5) node[ic8pin,rotate=90] (IC2) {IC 2};
\draw[red] (IC1.pin1) -- ++ (-1,0.5) -- (IC2.pin1);
\draw[blue] (IC1.pin2) -- ++ (-1,0) -- (IC2.pin2);
\draw[green] (IC1.pin3) -- ++ (-1,-0.5) -- (IC2.pin3);
\end{tikzpicture}

\end{document}

This produces the error (the problem is the *\x):

\foreach \x in {1,...,\mpp{top left pins}}
{   \setcounter{mypincounter}{\x}
    \expandafter\savedanchor\csname tlpin\Roman{mypincounter} \endcsname{\pgfpoint{-\TBDivisions*\mpp{pin spacing}/2}{\LRDivisions*\mpp{pin spacing}/2-\value{mypincounter}*\mpp{pin spacing}*\x}}
    \anchor{pin\x}{\csname tlpin\Roman{mypincounter} \endcsname}
}

And this does not work properly (\value{mypincounter} is always equal 3):

\foreach \x in {1,...,\mpp{top left pins}}
{   \setcounter{mypincounter}{\x}
    \xdef\mydummyx{\x}
    \expandafter\savedanchor\csname tlpin\Roman{mypincounter} \endcsname{\pgfpoint{-\TBDivisions*\mpp{pin spacing}/2}{\LRDivisions*\mpp{pin spacing}/2-\value{mypincounter}*\mpp{pin spacing}}}
    \anchor{pin\x}{\csname tlpin\Roman{mypincounter} \endcsname}
}

Output

enter image description here

Expected Output

enter image description here

share|improve this question
    
are the red, blue and green lines supposed to go from the bottom right corner of the horizontal rectangle to the bottom left corner of the vertical rectangle? (with some varying intermediate point with an angle) this is what I get from copying your code with the *\x, and using an \xintFor* loop rather than a \foreach loop. –  jfbu Nov 24 '13 at 17:53
    
@jfbu: the lines are supposed to be starting and ending equally spaced. So the red one starts one unit from the top left, the blue one two units and the green one three units. –  Tom Bombadil Nov 24 '13 at 17:56
    
I don't see any info in the log. Is \value{mypincounter}*\mpp{pin spacing}*\x intended? doesn't it mean that the value of \x is used twice once via \value{mypincounter} then again via \x? –  jfbu Nov 24 '13 at 18:04
    
The problem seems to be that \anchor does not expand immediately its arguments, so the last set value is used. If I use (in my \xintFor* loop) something like \anchor{pin#1}{\csname tlpin#1\endcsname} (with the corresponding \savedanchor) I do obtain the intended output –  jfbu Nov 24 '13 at 18:28
    
maybe related? tex.stackexchange.com/questions/65192/… –  percusse Nov 24 '13 at 22:53
add comment

2 Answers

Both savedanchor and anchor are aliases for internal macros. \anchor globally defines an anchor when the shape is declared (so expansion occurs in the anchor name), but only expands the code for the anchor when the anchor is called in a coordinate, for example, (A.pin 1). \savedanchor doesn't expand either of its arguments when the shape is declared but does so when the shape is drawn, for example with \node.

For this kind of thing the \pgfmathloop command can be used. It is very lightweight as it is simply a reimplementation of the \loop-\repeat construct but holds the number of the current iteration in the macro \pgfmathcounter. Counting starts at 1.

The star, regular polygon and cloud shapes all show how this can be used for variable numbers of anchors, although they use internal macros to create the anchors and do some extra magic (see below).

A more straightforward and much simpler example is this:

\documentclass[tikz, border=2mm]{standalone}

\makeatletter

\pgfkeys{%
  /pgf/chip pins/.initial=4,
  /pgf/chip pin spacing/.initial=0.25cm
}
\def\maxpins{10}

\pgfdeclareshape{chip}{
  \savedanchor\centerpoint{%
    \pgf@x=.5\wd\pgfnodeparttextbox%
    \pgf@y=.5\ht\pgfnodeparttextbox%
    \advance\pgf@y by-.5\dp\pgfnodeparttextbox%
  }
  \anchor{center}{\centerpoint}
  \saveddimen\height{%
    \pgfmathsetlength\pgf@x{(\pgfkeysvalueof{/pgf/chip pins}+1)*\pgfkeysvalueof{/pgf/chip pin spacing}}%
  }
  \saveddimen{\chipspacing}{\pgfmathsetlength\pgf@x{\pgfkeysvalueof{/pgf/chip pin spacing}}}
  \backgroundpath{%
    \pgfpathrectanglecorners{\pgfpointadd{\centerpoint}{\pgfpoint{-.5cm}{-\height/2}}}%
    {\pgfpointadd{\centerpoint}{\pgfpoint{.5cm}{\height/2}}}%
  }
  %
  \pgfmathloop%
  \ifnum\pgfmathcounter>\maxpins\relax% 
  \else%
    % Need to expand \pgfmathcounter
    \edef\marshal{\noexpand\anchor{pin \pgfmathcounter}}%
    \expandafter\marshal\expandafter{\expandafter\chippinanchor\expandafter{\pgfmathcounter}}%
  \repeatpgfmathloop%
}

\def\chippinanchor#1{%
  % When this macro is called,
  % \centerpoint, \height and \chipspacing will be defined.
  \pgfpointadd{\centerpoint}{\pgfpoint{.5cm}{\height/2-#1*\chipspacing}}%
}

\begin{document}

\begin{tikzpicture}
\node [chip, draw] (A) {A};
\node [chip, draw, rotate=-90, chip pins=5, chip pin spacing=.5cm] (B) at (2,2){B};

\foreach \p in {1,...,4}
 \draw (A.pin \p) node [left, font=\tiny] {\p} -| (B.pin \p) node [above, font=\tiny] {\p};

\end{tikzpicture}

\end{document}

enter image description here

Note, that in the above example the maximum number of pins is fixed at the point the shape is declared (based on \maxpins). To allow arbitrary numbers of pins which are determined when the shape is drawn (e.g., using \node) some extra work must be done. This the same approach used by the star, regular polygon and cloud shapes.

\documentclass[tikz, border=2mm]{standalone}

\makeatletter

\pgfkeys{%
  /pgf/chip pins/.initial=4,
  /pgf/chip pin spacing/.initial=0.25cm
}

\pgfdeclareshape{chip}{
  \savedanchor\centerpoint{%
    \pgf@x=.5\wd\pgfnodeparttextbox%
    \pgf@y=.5\ht\pgfnodeparttextbox%
    \advance\pgf@y by-.5\dp\pgfnodeparttextbox%
  }%
  \anchor{center}{\centerpoint}
  \saveddimen\height{%
    \pgfmathsetlength\pgf@x{(\pgfkeysvalueof{/pgf/chip pins}+1)*\pgfkeysvalueof{/pgf/chip pin spacing}}%
  }%
  \saveddimen{\chipspacing}{\pgfmathsetlength\pgf@x{\pgfkeysvalueof{/pgf/chip pin spacing}}}
  \backgroundpath{%
    \pgfpathrectanglecorners{\pgfpointadd{\centerpoint}{\pgfpoint{-.5cm}{-\height/2}}}%
    {\pgfpointadd{\centerpoint}{\pgfpoint{.5cm}{\height/2}}}%
  }%
  % \pgf@sh@s@chip contains all the code for the chip shape
  % and is executed just before a chip node is drawn.
  \pgfutil@g@addto@macro\pgf@sh@s@chip{%
      % Start with the maximum pin number and go backwards.
      % If the anchor is undefined, create it. Otherwise stop.
      \c@pgf@counta=\pgfkeysvalueof{/pgf/chip pins}\relax%
      \pgfmathloop%
      \ifnum\c@pgf@counta>0\relax%
        \pgfutil@ifundefined{pgf@anchor@chip@pin\space\the\c@pgf@counta}{%
          \expandafter\xdef\csname pgf@anchor@chip@pin\space\the\c@pgf@counta\endcsname{%
            \noexpand\chippinanchor{\the\c@pgf@counta}%
          }%
        }{\c@pgf@counta=0\relax}%
        \advance\c@pgf@counta-1\relax%
      \repeatpgfmathloop%  
    }%
}

\def\chippinanchor#1{%
  % When this macro is called,
  % \centerpoint, \height and \chipspacing will be defined.
  \pgfpointadd{\centerpoint}{\pgfpoint{.5cm}{\height/2-#1*\chipspacing}}%
}

\begin{document}

\begin{tikzpicture}
\node [chip, draw] (A) {A};
\node [chip, draw, rotate=-90, chip pins=5, chip pin spacing=.5cm] (B) at (2,2){B};

\foreach \p in {1,...,4}
 \draw (A.pin \p) node [left, font=\tiny] {\p} -| (B.pin \p) node [above, font=\tiny] {\p};

\end{tikzpicture}

\end{document}
share|improve this answer
add comment

It seems that the \anchor does not expand immediately its second argument.

The following code appears to produce the intended output:

\documentclass[tikz, border=2mm]{standalone}

\usepackage{xint}

\tikzset{%
    multipoles/.is family,
    multipoles,
    pin spacing/.initial=5mm,
    top left pins/.initial=3,
    bottom left pins/.initial=3,
    top right pins/.initial=8,
    bottom right pins/.initial=0,
    top pins/.initial=0,
    bottom pins/.initial=0, 
}

\newcommand{\mpp}[1]%
{   \pgfkeysvalueof{/tikz/multipoles/#1}
}

\newcounter{mypincounter}

\pgfdeclareshape{ic8pin}{
\anchor{center}{\pgfpointorigin} % within the node, (0,0) is the center
\anchor{text} % this is used to center the text in the node
{\pgfpoint{-.5\wd\pgfnodeparttextbox}{-.5\ht\pgfnodeparttextbox}}

\pgfmathtruncatemacro{\lrd}{max(\mpp{top left pins} +\mpp{bottom left pins} +or(\mpp{top left pins},\mpp{bottom left pins}), \mpp{top right pins} +\mpp{bottom right pins} +or(\mpp{top right pins},\mpp{bottom right pins}) == 0 ? 2 : max(\mpp{top left pins} +\mpp{bottom left pins} +or(\mpp{top left pins},\mpp{bottom left pins}), \mpp{top right pins} +\mpp{bottom right pins} +or(\mpp{top right pins},\mpp{bottom right pins})}
\xdef\LRDivisions{\lrd}

\pgfmathtruncatemacro{\tbd}{max(\mpp{top pins} +and(1,\mpp{top pins}), \mpp{bottom pins} +and(1,\mpp{bottom pins}) == 0 ? 2 : max(\mpp{top pins} +and(1,\mpp{top pins}), \mpp{bottom pins} +and(1,\mpp{bottom pins})}
\xdef\TBDivisions{\tbd}

\xintFor* #1 in {\xintSeq {1}{\mpp{top left pins}}}
\do
{%\setcounter{mypincounter}{#1}% not needed
    \expandafter\savedanchor\csname tlpin#1\endcsname
{\pgfpoint{-\TBDivisions*\mpp{pin spacing}/2}%
          {(\LRDivisions*\mpp{pin spacing}/2)-(\mpp{pin spacing}*#1)}}
    \anchor{pin#1}{\csname tlpin#1\endcsname}
}

\foregroundpath{ % border and pin numbers are drawn here
\pgfsetlinewidth{0.4pt}

\pgfpathrectanglecorners{\pgfpoint{\TBDivisions*\mpp{pin spacing}/2}{\LRDivisions*\mpp{pin spacing}/2}}{\pgfpoint{-\TBDivisions*\mpp{pin spacing}/2}{-\LRDivisions*\mpp{pin spacing}/2}}
\pgfusepath{draw} %draw rectangle

\pgftext[left,at={\pgfpoint{-.5cm}{-.55cm}}]{\scriptsize LR : \textcolor{red}{\LRDivisions}}
\pgftext[left,at={\pgfpoint{-.5cm}{.55cm}}]{\scriptsize TB : \textcolor{red}{\TBDivisions}}
}}

\begin{document}

\begin{tikzpicture}
\draw (0,0) node[ic8pin] (IC1) {IC 1};
\draw (-5,5) node[ic8pin,rotate=90] (IC2) {IC 2};
\draw[red] (IC1.pin1) -- ++ (-1,0.5) -- (IC2.pin1);
\draw[blue] (IC1.pin2) -- ++ (-1,0) -- (IC2.pin2);
\draw[green] (IC1.pin3) -- ++ (-1,-0.5) -- (IC2.pin3);
\end{tikzpicture}

\end{document}

anchors

share|improve this answer
    
\xintFor has now been moved from xint to the newly released xinttools package (release 1.09g) –  jfbu Nov 24 '13 at 20:36
add comment

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.