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 02 '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 02 '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 02 '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 02 '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 02 '16 at 22:50
  • I meant more that various kinds of complex expressions can be part of the (<angle>:<dimension>) specification for a polar coordinate, including macros and pgfmath expressions. And the code needs to do or say something sensible when misused, of course e.g. if the name does not refer to a node or the syntax is wrong or whatever. Maybe the default error handlers cope with this already - I haven't tested it, but you should if you are thinking of publishing it in any form. – cfr Aug 02 '16 at 23:16
  • Try, for example, \draw (0:{mod(50,6)}) -- ++(1,1);. – cfr Aug 02 '16 at 23:24
  • I AM SORRY..... Should we (re)start from\tikz@calc@anchor or \pgfpointanchor? Because that is where TikZ or PGF realizes that (node.anchor) is a pair of node and anchor. I expect something like (node.30,20). – Symbol 1 Aug 03 '16 at 09:17
  • That would make sense, but it would require to integrate the parsing into the \pgfpointanchor subsystem. I will have a look at it – stettberger Aug 03 '16 at 10:48
  • @Symbol1 There's no need to shout! – cfr Aug 03 '16 at 21:11
  • @cfr Oops... I thought it means that i am really sorry... not sarcasm. – Symbol 1 Aug 04 '16 at 06:33
  • @Symbol1 Why would you be sorry? – cfr Aug 10 '16 at 01:25

1 Answers1

5

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}

Symbol 1
  • 36,855
  • 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 04 '16 at 16:27