I created a TikZ coordinate system to solve a positioning problem I had. The code is presented below. However, the code doesn't work; if you run it, you will get the
Too many .'s
error message even though there are, in fact, the correct number of dots in each coordinate in the test tikzpicture. Careful investigation reveals that the macro \sidecs@numberofats
is not being populated with the correct value: it always ends up being zero.
Since I have used \makeatletter
before defining the coordinate system code, but "@" is an "other" character in the tikzpicture
where the coordinates appear, my assumption is that this is a catcode issue. This supposition is backed up by the fact that if you change @
to :
everywhere it appears (apart from in macro names, obviously), the document compiles successfully and has the expected appearance.
How can I get XString to count the @
characters? Most XString macros have a starred version that (as the manual explains) disregards catcodes, but for whatever reason, \StrCount
does not.
(I wanted to use the built-in PGF parsing library to do this rather than XString, but it isn't full-featured enough. Specifically, it appears that you can't provide a rule that works for "any character not amongst those separately specified", and since I want to accept node names - which might contain almost any character at all - this is no good.)
\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}
\expandafter\StrCut\expandafter\sidecs@expandedarg\expandafter{\string@}