6

I was dissatisfied with the implicit degree anchors TikZ provides for nodes (i.e., (node.30)). I often want to start an path at 30 percent from the left at the lower border of a node. This is of course possible with calc, but this feels a little clumsy.

Therefore, I wrote a snippet that defines a node relative coordinate system and hooked it into the parser to provide a convenient syntax. After some usage, I found it so useful that I would like to show it and request some feedback, before sending it to the PGF/TikZ maintainers.

The resulting syntax for 20 percent at the bottom is (NODE:20,0). What do you think?

Example usage of node relative coordinate system

\documentclass[crop,tikz]{standalone}

\makeatletter

% Declare a relative coordinate system, that is relative to the edges
% of a given node.
\tikzdeclarecoordinatesystem{rel}{%
    \tikzset{cs/.cd,x=0pt,y=0pt,#1}%
    \pgfpointlineattime{(\tikz@cs@x / 100)}%
       {\pgfpointanchor{\tikz@pp@name{\tikz@cs@node}}{south west}}%
       {\pgfpointanchor{\tikz@pp@name{\tikz@cs@node}}{south east}}%
    \edef\tikz@cs@x{\the\pgf@x}%
    \pgfpointlineattime{(\tikz@cs@y / 100)}%
       {\pgfpointanchor{\tikz@pp@name{\tikz@cs@node}}{south west}}%
       {\pgfpointanchor{\tikz@pp@name{\tikz@cs@node}}{north west}}%
    \pgfpoint{\tikz@cs@x}{\pgf@y}%
  }

% Hook into Tikz coordinate parse to provide (<name>:<rel x>x<rel y>)
\def\tikz@parse@relcs#1(#2:#3,#4){%
\tikz@parse@coordinatesystem#1(rel cs:name={#2},x={#3},y={#4})%
}
\let\tikz@parse@relcs@polar@saved=\tikz@parse@polar
\def\tikz@parse@polar#1(#2:#3){%
  \pgfutil@in@{,}{#3}%
  \ifpgfutil@in@%
    \let\@next\tikz@parse@relcs%
  \else%
    \let\@next\tikz@parse@relcs@polar@saved%
  \fi%
  \@next#1(#2:#3)%
}

\makeatother


\begin{document}
\begin{tikzpicture}
  % First we define a border node, that we use as coordinate system
  \node[draw, text width=100mm, align=center, text height=40mm, text depth=40mm](border){};
  % Just some coordinate system
  \foreach \x in {10,20,30,40,50,60,70,80,90} {
    \draw[black!20] (border:\x,100) -- (border:\x,0) node[black,anchor=north] {\x};
    \draw[black!20] (border:0,\x) -- (border:100,\x) node[black,anchor=west] {\x};
  }


  % Use the explicit coordinate syntax
  \draw (rel cs:x=50,y=0,name=border) -- (rel cs:x=75,y=100,name=border);

  % Use the shorthand version that is hooked into the TiKZ coordinate
  % parser.
  \node at (border:20,30) (A) {A};
  \node at (border:50,75) (B) {B};

  % Some more nice demonstrations
  \node[draw] at (border:25,75) (text) {Long-Text};
  \draw (text:20,0) edge[->,bend right] (A);
  \draw (text:60,0) edge[->,bend right] (B.south);
  \foreach \x in {10,20,30,40,50,60,70,80,90} {
    \draw (text:\x,100) -- ++(up:5pt);
  }

  % Polar Coordinates still work
  \draw (0,0) -- (30:2cm);

 \end{tikzpicture}
\end{document}
  • 1
    Welcome! I'm not sure how well this question fits the official desiderata, but it is certainly interesting ;). This is intended for use with rectangular nodes, is it? I have no idea what RFC means in this context. To me, it means Rugby Football Club, but I'm struggling to make sense of that in this context. – cfr Aug 2 '16 at 21:17
  • Currently the implementation relies on the anchors {south,north} {east,west}. So any node that provides these anchors is supported. But yes, I had only rectangular nodes in mind. And RFC means "request for comments" – stettberger Aug 2 '16 at 21:19
  • 1
    This will break polar coordinates if the unit of measurement contains an x, as is very common. Try changing 2cm to 2ex, for example. – cfr Aug 2 '16 at 21:27
  • And note that I can use a macro for the length and/or angle when specifying a polar coordinate e.g. (30:\myxyz). This works OK in simple cases, but I've no idea if it is generally safe with your code. – cfr Aug 2 '16 at 21:32
  • I replaced 'x' with ',' as a separator. And as far as I see it, does (node:\myxyz) work also with node relative coordinates. – stettberger Aug 2 '16 at 22:50
4

This is a long comment.

Recall that TikZ parses (something) as follows:

  • Check if it involves calculation ($(U)+(V)$)
  • Check if it is of specific coordinate system (foo cs: bar)
  • Check if it is an intersection (intersection-1) & (A|-B)
  • Check if it polar or cartesian coordinate (1:2) & (3,4)
  • Check if it is a pre-defined node (node)
  • Check if it is a node with a predefined anchor (node.anchor)
  • Check if it is a node with an angle (node.360)

So there are two possibilities:

  • If a node is very important, (current page) for instance, then one can make it a coordinate system: (current page cs: bar bar bar). (see this)
  • If all nodes are equal, then it should be part of the node-anchor system: The expected syntax are (node.360) and (node.{12,34}).

Recall that although a node in TikZ (a shape in PGF) can have many many anchors, it is not always satisfying. Hence PGF introduces border anchor so that one can create anchors on the fly. Similarly one could create an interior anchor system parallel to border anchors.

By the way, PGF introduces generic anchor that creates anchors for all nodes. For example if you want a point "at 30 percent from the left at the lower border of a node", you can write

\documentclass[border=9,tikz]{standalone}
\begin{document}
\makeatletter
\pgfdeclaregenericanchor{30 from left on bottom}{
    \pgf@process{\northeast}
    \pgf@xa=.3\pgf@x
    \pgf@process{\southwest}
    \pgf@x=.7\pgf@x
    \advance\pgf@x by\pgf@xa
}
\makeatother
\tikz{
    \path[nodes=draw]node(A){Apple}(5,0)node(B){Banana};
    \draw[bend right](A.30 from left on bottom)to(B.30 from left on bottom);
}
\end{document}

  • That is really helpful. You understand this stuff really well, I think. I just mess around on the surface, but you dive right down. :-) – cfr Aug 4 '16 at 16:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.