我创建了一个 Ti钾Z 坐标系来解决我遇到的定位问题。代码如下所示。但是,代码不起作用;如果你运行它,你将得到
太多 .
尽管测试 tikzpicture 中每个坐标上的点数都是正确的,但仍然会显示错误消息。仔细调查后发现,宏中\sidecs@numberofats
没有填充正确的值:它最终总是为零。
由于我之前使用过\makeatletter
定义坐标系代码,但“@”在tikzpicture
坐标出现的位置是“其他”字符,因此我假设这是一个 catcode 问题。这一假设得到了以下事实的支持:如果您将其出现的所有位置(显然宏名称除外)更改@
为,文档将成功编译并具有预期的外观。:
如何让 XString 计算@
字符数?大多数 XString 宏都有一个带星号的版本(如手册所述)忽略 catcode,但无论出于何种原因,\StrCount
它都没有。
(我想使用内置的 PGF 解析库而不是 XString 来执行此操作,但它的功能不够齐全。具体来说,似乎您无法提供适用于“任何不在单独指定的字符中的字符”的规则,并且因为我想接受节点名称 - 可能包含几乎任何字符 - 这不好。)
\documentclass{standalone}
\usepackage{tikz, xstring}
\makeatletter
\def\sidecs@expandtotop{top}
\def\sidecs@expandtobottom{bottom}
\def\sidecs@expandtoleft{left}
\def\sidecs@expandtoright{right}
\def\sidecs@trimspacesleft#1{%
\IfBeginWith{#1}%
{ }%
{\StrGobbleLeft{#1}{1}[#1]\sidecs@trimspacesleft{#1}}%
{}%
}
\def\sidecs@trimspacesright#1{%
\IfEndWith{#1}%
{ }%
{\StrGobbleRight{#1}{1}[#1]\sidecs@trimspacesright{#1}}%
{}%
}
\tikzdeclarecoordinatesystem{side}{
\begingroup
\fullexpandarg
\edef\sidecs@expandedarg{#1}
\StrCount{\sidecs@expandedarg}{@}[\sidecs@numberofats]
\if\sidecs@numberofats0
\let\sidecs@partbeforeat\sidecs@expandedarg
\def\sidecs@pos{0.5}
\def\sidecs@posreversed{0.5}
\else\if\sidecs@numberofats1
\StrCut{\sidecs@expandedarg}{@}{\sidecs@partbeforeat}{\sidecs@pos}
\pgfmathsubtract{1}{\sidecs@pos}
\let\sidecs@posreversed\pgfmathresult
\else
\errmessage{Too many @'s in side cs coordinate}
\fi\fi
\StrCount{\sidecs@partbeforeat}{.}[\sidecs@numberofdots]
\if\sidecs@numberofdots0
\errmessage{Not enough .'s in side cs coordinate}
\else\if\sidecs@numberofdots1
\else
\errmessage{Too many .'s in side cs coordinate}
\fi\fi
\StrCut{\sidecs@partbeforeat}{.}{\sidecs@node}{\sidecs@side}
\sidecs@trimspacesleft{\sidecs@node}
\sidecs@trimspacesright{\sidecs@node}
\sidecs@trimspacesleft{\sidecs@side}
\sidecs@trimspacesright{\sidecs@side}
\ifx\sidecs@side\sidecs@expandtotop
\def\sidecs@anchorone{north west}
\def\sidecs@anchortwo{north east}
\else\ifx\sidecs@side\sidecs@expandtobottom
\def\sidecs@anchorone{south west}
\def\sidecs@anchortwo{south east}
\else\ifx\sidecs@side\sidecs@expandtoleft
\def\sidecs@anchorone{north west}
\def\sidecs@anchortwo{south west}
\else\ifx\sidecs@side\sidecs@expandtoright
\def\sidecs@anchorone{north east}
\def\sidecs@anchortwo{south east}
\else
\errmessage{Unknown side}
\fi\fi\fi\fi
\pgfpointadd%
{\pgfpointscale{\sidecs@posreversed}%
{\pgfpointanchor{\sidecs@node}{\sidecs@anchorone}}}%
{\pgfpointscale{\sidecs@pos}%
{\pgfpointanchor{\sidecs@node}{\sidecs@anchortwo}}}
\global\pgf@x=\pgf@x
\global\pgf@y=\pgf@y
\endgroup
}
\makeatother
\begin{document}
\begin{tikzpicture}
\begin{scope}[x = 35mm, y = 3mm, every node/.style = draw]
\node (x) at (0, 0) {XXXXXXXXXX};
\node (a) at (1, 7) {AAAAAAAAAA};
\node (b) at (1, 5) {BBBBBBBBBB};
\node (c) at (1, 3) {CCCCCCCCCC};
\node (d) at (1, 1) {DDDDDDDDDD};
\node (e) at (1, -1) {EEEEEEEEEE};
\node (f) at (1, -3) {FFFFFFFFFF};
\node (g) at (1, -5) {GGGGGGGGGG};
\node (h) at (1, -7) {HHHHHHHHHH};
\end{scope}
\begin{scope}[->,
tl/.style = {out = 90, in = 180},
bl/.style = {out = 270, in = 180}]
\draw [tl] (side cs: x.top @ 0.5) to (side cs: a.left @ 0.5);
\draw [tl] (side cs: x.top @ 0.6) to (side cs: b.left @ 0.5);
\draw [tl] (side cs: x.top @ 0.7) to (side cs: c.left @ 0.5);
\draw [tl] (side cs: x.top @ 0.8) to (side cs: d.left @ 0.5);
\draw [bl] (side cs: x.bottom @ 0.8) to (side cs: e.left @ 0.5);
\draw [bl] (side cs: x.bottom @ 0.7) to (side cs: f.left @ 0.5);
\draw [bl] (side cs: x.bottom @ 0.6) to (side cs: g.left @ 0.5);
\draw [bl] (side cs: x.bottom @ 0.5) to (side cs: h.left @ 0.5);
\end{scope}
\end{tikzpicture}
\end{document}
答案1
有几种方法可以解决这个问题。
因为你在下面,
\fullexpandarg
你可以简单地做\StrCut{\sidecs@expandedarg}{\string @}{\sidecs@partbeforeat}{\sidecs@pos}
如果不在
\fullexpandarg
,您可以使用字符串\expandafter
\@xp\StrCut\@xp{\@xp\sidecs@expandedarg\@xp}\@xp{\string @}{\sidecs@partbeforeat}{\sidecs@pos}
(我使用
\@xp
,\expandafter
如果你加载 则可用amsmath
)或者,
\lowercase
诀窍是:\begingroup\lccode`?=`@ \lowercase{\endgroup \StrCut{\sidecs@expandedarg}{?}{\sidecs@partbeforeat}{\sidecs@pos}% }
\lowercase
它利用了不改变类别代码的事实。
对于以下行也类似\StrCount
:
\StrCount{\sidecs@expandedarg}{\string @}[\sidecs@numberofats]
或者
\@xp\StrCount\@xp{\@xp\sidecs@expandedarg\@xp}\@xp{\string @}[\sidecs@numberofats]
或者
\begingroup\lccode`?=`@ \lowercase{\endgroup
\StrCount{\sidecs@expandedarg}{?}[\sidecs@numberofats]%
}
答案2
根据用户 egreg 的评论,一种可能的方法是使用\string
@ 来表示“side cs”。这会在执行时将其转换为带有 catcode“other”的“@”,而这正是 XString 在本例中要查找的内容。
在代码开头或附近添加以下行:
\let\ea\expandafter
将第一\StrCount
行替换为以下内容:
\ea\StrCount\ea{\ea\sidecs@expandedarg\ea}\ea{\string @}[\sidecs@numberofats]
\StrCut
将条件语句中的第一行替换为以下内容:
\ea\StrCut\ea{\ea\sidecs@expandedarg\ea}\ea{\string @}{\sidecs@partbeforeat}{\sidecs@pos}