使用 \foreach 循环在形状中创建锚点

使用 \foreach 循环在形状中创建锚点

我在玩这个问题,特别是试图修改和扩展链接的例子\foreach。它工作正常,但当我尝试使用循环来定义不同的锚点组时,会出现错误。如果我直接使用循环变量,我会得到一个“! 未定义的控制序列。“错误。如果我使用计数器的值,则仅使用循环所采用的最后一个值。但是,正如我们所见,所有三个锚点都已定义。所以我的问题是:

  • 为什么会发生这种情况?
  • 如何解决这个问题?

代码

\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}

这会产生错误(问题出在*\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}
}

但这不能正常工作(\value{mypincounter}始终等于 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}
}

输出

在此处输入图片描述

预期输出

在此处输入图片描述

答案1

savedanchor都是anchor内部宏的别名。\anchor当形状为已声明(因此扩展发生在锚点名称中),但只有当锚点是称为(A.pin 1)例如,在坐标中,\savedanchor在声明形状时,不会扩展其任何参数,但在形状为,例如使用\node

对于这种情况,\pgfmathloop可以使用命令。它是非常轻量级,因为它只是\loop-\repeat构造的重新实现,但在宏中保存了当前迭代的次数\pgfmathcounter。计数从 1 开始。

star和形状regular polygoncloud展示了如何将其用于可变数量的锚点,尽管它们使用内部宏来创建锚点并做一些额外的魔法(见下文)。

一个更直接、更简单的例子是:

\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}

在此处输入图片描述

请注意,在上面的例子中,形状的最大引脚数是固定的已声明(基于\maxpins)。允许任意数量的引脚,这些引脚的数量由形状决定(例如,使用)必须做一些额外的工作。这与、和形状\node使用的方法相同。starregular polygoncloud

\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}

答案2

(编辑)2017:因为xint 1.1 (2014/10/28)这里需要\usepackage{xinttools}。代码已更新以替换\usepackage{xint}初始答案。


看起来并\anchor没有立即展开它的第二个参数。

以下代码似乎产生了预期的输出:

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

\usepackage{xinttools}

\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}

锚点

相关内容