2

I have a graph with nodes, where the labels and their positions are controlled from outside. When mirroring the graph with some negative scale/xscale/yscale, the labels should change their positions, too. This seems to rule out several of the more powerful label positioning methods (the labels are not in the right place anymore after mirroring). I'm almost done, only one problem remains: The labels have their anchor in the center. What I need is a way to automatically set the anchor to the point nearest to the node.

\documentclass{standalone}
  \usepackage{tikz}
  \newcommand\mynode[2]%
    {\node[circle,draw,minimum width=1mm] (#2) at (#1) {};%
     \draw (#2) ++(\labelpos{#2}) node {\labelname{#2}};%
    }
  \newcommand\labelpos[1]{0.3,0} % dummy; really a lookup using #1 as key
  \newcommand\labelname[1]{xxx}  % dummy; really a lookup using #1 as key
\begin{document}
  \begin{tikzpicture}
  \mynode{0,0}{a}
  \mynode{1,1}{b}
  \end{tikzpicture}
\qquad
  \begin{tikzpicture}[scale=-1]
  \mynode{0,0}{a}
  \mynode{1,1}{b}
  \end{tikzpicture}
\end{document}

enter image description here What I would like to have: enter image description here Note that in general the labels may be anywhere around the node, not just left and right.

gernot
  • 49,614
  • By defaul, node places the center of the text at the given location. Use node[left] or node[right] to offset the text. – John Kormylo Aug 05 '16 at 14:19
  • Thanks for the hint, but this doesn't solve my problem: After mirroring the image, left still refers to left in the old frame, not in the mirrored one. – gernot Aug 05 '16 at 14:55
  • After having done many more experiments, it seems that there is no way to use anchors in a way such that they get affected by negative scaling (like left becoming right, above becoming below, 45 degrees becoming 225 degrees, etc). – gernot Aug 05 '16 at 14:58
  • I've found a partial answer here. There a new Anchor style is defined for anchors that rotate and scale. It works for labels with a round shape, but not very well for rectangular labels (which is the case for the labels longer than one or two characters). – gernot Aug 05 '16 at 15:34
  • If you have an angle, you could always add 180 to it? – cfr Aug 05 '16 at 16:25
  • @cfr wouldn't it be the complement to 180 for a symmetry? – Christoph Frings Aug 05 '16 at 16:37
  • @ChristophFrings Er ... possibly ;). – cfr Aug 05 '16 at 16:59
  • @cfr yeah maybe not, I was focusing on a horizontal symmetry only... – Christoph Frings Aug 05 '16 at 17:02
  • @gernot are you looking for a general solution (any possible values of xscale, yscale, any label placement [left, right, above, ...]) or something more specific? – Christoph Frings Aug 05 '16 at 17:04
  • @christoph Yes, I'm looking for a general solution. I can specify the position of the label beforehand, given one orientation of the graph, but it is up to the user to flip sides or top/bottom or both (or do some additional scaling). Given the arcs in the graph, I may have to place the label at some weird position, say at an angle of 53 degrees and a distance of 0.2 (or using some relative x/y coordinates). The difficulty is not to get the label position right after mirroring, but to let tikz determine the correct anchor, in particular after mirroring. – gernot Aug 05 '16 at 17:54

1 Answers1

3

Unfortunately, the design of node-anchor system makes it almost impossible. The spirit is that despite all transformations, the positioning of nodes is done with respect to reader's point of view.

For example, in node[anchor=north]{BAD} the anchor is located near the apex of the letter "A"; in node[above right]{LOF}, another way to say [anchor=south west], the anchor is located near the corner of the letter "L".

There is a brutal way to transform nodes as well. However this is not what you want.

\documentclass[border=9,tikz]{standalone}
\begin{document}
\tikz[transform shape,yscale=-2          ]\draw circle(.1)node[anchor=north]{BAD};
\tikz[transform shape,          rotate=60]\draw circle(.1)node[anchor=north]{BAD};
\tikz[transform shape,yscale=-2,rotate=60]\draw circle(.1)node[anchor=north]{BAD};
\end{document}

In practice, you can make your own positioning options as follows:

  • For simple purpose, we access the current transformation matrix and decide if we need to replace left and right by eachother.

\makeatletter
\tikzset{
    relative  left/.code={\ifdim\pgf@pt@aa pt>0pt\tikzset{ left=#1}\else\tikzset{right=#1}\fi},
    relative right/.code={\ifdim\pgf@pt@aa pt<0pt\tikzset{ left=#1}\else\tikzset{right=#1}\fi},
    relative above/.code={\ifdim\pgf@pt@bb pt>0pt\tikzset{above=#1}\else\tikzset{below=#1}\fi},
    relative below/.code={\ifdim\pgf@pt@bb pt<0pt\tikzset{above=#1}\else\tikzset{below=#1}\fi}
}
\tikz\draw foreach\i in{1,...,9}{[rotate=\i*40](2,0)circle(.1)node[relative  left]{rel- left}};
\tikz\draw foreach\i in{1,...,9}{[rotate=\i*40](2,0)circle(.1)node[relative right]{rel-right}};
\tikz\draw foreach\i in{1,...,9}{[rotate=\i*40](0,2)circle(.1)node[relative above]{rel-above}};
\tikz\draw foreach\i in{1,...,9}{[rotate=\i*40](0,2)circle(.1)node[relative below]{rel-below}};
  • If one want to input angles and get discrete anchors accordingly, we borrow some macros from [auto]

\tikzset{
    discrete anchor by angle/.code={
        \pgfpointtransformed{\pgfpointpolar{#1+90}{1pt}}
        \tikz@auto@anchor
    }
}
\tikz\draw foreach\i in{1,..., 8}{[rotate=\i*45](1,1)circle(.1)node[discrete anchor by angle=225]{discr-ang}};
\tikz\draw foreach\i in{1,...,90}{[rotate=\i* 4](1,1)circle(.1)node[discrete anchor by angle=225]{discr-ang}};
  • If one want to get continuous output, we follow @Qrrbrbirlbel's answer from the link in comment

\tikzset{
    continuous anchor by angle/.code={
        \pgfpointtransformed{\pgfpointpolar{#1}{1pt}}
        \pgfmathanglebetweenpoints{\pgfqpoint{-\pgf@x}{-\pgf@y}}{\pgfpointorigin}
        \def\tikz@anchor{\pgfmathresult}
    }
}
\tikz\draw foreach\i in{1,..., 8}{[rotate=\i*45](1,1)circle(.1)node[continuous anchor by angle=225]{conti-ang}};
\tikz\draw foreach\i in{1,...,90}{[rotate=\i* 4](1,1)circle(.1)node[continuous anchor by angle=225]{conti-ang}};
  • One might want to input discrete keys

\tikzset{
    discrete anchor by key/.code={
        \csname tikz@label@angle@is@#1\endcsname
        \tikzset{discrete anchor by angle=\tikz@label@angle+180}
    }
}
\tikz\draw foreach\i in{1,..., 8}{[rotate=\i*45](1,1)circle(.1)node[discrete anchor by key=above right]{discr-key}};
\tikz\draw foreach\i in{1,...,90}{[rotate=\i* 4](1,1)circle(.1)node[discrete anchor by key=above right]{discr-key}};
Symbol 1
  • 36,855
  • Thanks A LOT for this comprehensive research. I'll probably use the continuous approach since it is the most elegant one. I think the "discrete anchor by angle" approach has a bug; it seems to work with rotation, but does not with mirroring. To correct it, I had to distinguish the four cases according to whether \pgf@pt@aa and \pgf@pt@bb are negative or positive, and add different angles. – gernot Aug 09 '16 at 15:36