pgf-tikz / pgf

A Portable Graphic Format for TeX
https://pgf-tikz.github.io/
1.08k stars 104 forks source link

`bend left` has strange label placements with a jump with large nodes on both ends #1272

Open juliangilbey opened 10 months ago

juliangilbey commented 10 months ago

Brief outline of the bug

Using pgf version 3.1.10 with an up-to-date MacTeX distribution, an arrow with bend left places the label by default somewhat to the right of centre when the left node is large; the bend right arrow does this when in addition the right node is small. With more investigation, I discovered that there is a discontinuity in label placement between pos=0.48 and pos=0.50, though the exact place varies depending on context. The label placement is also strange when the left hand node is large and the right node is small. I first observed this with tikz-cd, but then tried repeating this with plain TikZ and observed the same behaviour.

Minimal working example (MWE)

Here are a sequence of test diagrams illustrating the weird behaviour.

\documentclass[12pt]{article}
\usepackage{tikz}
\usetikzlibrary{cd}
\pagestyle{empty}

\begin{document}
\begin{tikzcd}
C\times D
\ar[r, bend left, "F"]
\ar[r, bend right, "G"']
& \mathsf{Set}
\end{tikzcd}
tikz-cd, default positioning

\bigskip

\begin{tikzcd}
C\times D
\ar[r, bend left, pos=0.48, "F"]
\ar[r, bend right, pos=0.5, "G"']
& \mathsf{Set}
\end{tikzcd}
tikz-cd, F at 0.48, G at 0.5

\bigskip

\begin{tikzcd}
C\times D
\ar[r, bend left, pos=0.49, "F"]
\ar[r, bend right, pos=0.5, "G"']
& \mathsf{Set}
\end{tikzcd}
tikz-cd, F at 0.49, G at 0.5

\bigskip

\begin{tikzcd}
C\times D
\ar[r, bend left, "F"]
\ar[r, bend right, "G"']
& S
\end{tikzcd}
tikz-cd, default positioning

\bigskip

\begin{tikzcd}
C\times D
\ar[r, bend left, pos=0.48, "F"]
\ar[r, bend right, pos=0.5, "G"']
& S
\end{tikzcd}
tikz-cd, F at 0.48, G at 0.5

\bigskip

\begin{tikzcd}
C\times D
\ar[r, bend left, pos=0.49, "F"]
\ar[r, bend right, pos=0.5, "G"']
& S
\end{tikzcd}
tikz-cd, F at 0.49, G at 0.5

\bigskip

\begin{tikzpicture}
  \node (A) at (0,0) {$C\times D$};
  \node (B) at (2,0) {$\mathsf{Set}$};
  \draw [->] (A) to [bend left, "F"] (B);
  \draw [->] (A) to [bend right, "G"'] (B);
\end{tikzpicture}
plain Ti\emph{k}Z, default positioning

\bigskip

\begin{tikzpicture}
  \node (A) at (0,0) {$C\times D$};
  \node (B) at (2,0) {$\mathsf{Set}$};
  \draw [->] (A) to [bend left, "F", pos=0.49] (B);
  \draw [->] (A) to [bend right, "G"'] (B);
\end{tikzpicture}
plain Ti\emph{k}Z, F at 0.49, G at default positioning

\bigskip

\begin{tikzpicture}
  \node (A) at (0,0) {$C\times D$};
  \node (B) at (2,0) {$\mathsf{Set}$};
  \draw [->] (A) to [bend left, "F", pos=0.5] (B);
  \draw [->] (A) to [bend right, "G"'] (B);
\end{tikzpicture}
plain Ti\emph{k}Z, F at 0.5, G at default positioning

\end{document}

Removing the 12pt class option fixes some of the diagrams, but not all; presumably tweaking them will resurface this bug.

Here are the PDF output and the TeX log file: tikztest.pdf tikztest.log.txt

hmenke commented 10 months ago

The reason is that quotes imply auto anchor, i.e. these are equivalent:

\draw [->] (A) to [bend left, "F"] (B);
\draw [->] (A) to [bend left] node[auto] {F} (B);

By means of auto the anchor is selected by the following heuristic:

https://github.com/pgf-tikz/pgf/blob/42bd29bdd61beae8d9b46f8f83505d15e301ae02/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex#L4484-L4508

If I fumble some \showthe in there, I can see

> -0.05804pt.
\tikz@auto@anchor ...owthe \pgf@x \showthe \pgf@y 
                                                  \ifdim \pgf@x >0.05pt\ifdi...
l.42 ...[->] (A) to [bend left] node[auto] {F} (B)
                                                  ;
?

So for some reason \pgf@y=-0.05804pt and therefore anchor=south west instead of anchor=south is selected. My guess is rounding errors.

juliangilbey commented 10 months ago

Oh wow, thanks so much @hmenke! I don't think I'd have found that in a long long time :) I don't understand what \pgf@x and \pgf@y are in this context (and tracing back through the code still leaves me confused), but what is clear is that this code is designed with discontinuities (and it probably can't do any better than that with the current system design), and I just happen to have stumbled upon it. So it's not really a bug, but more a "feature".

And for anyone else reading this, the placement can be adjusted manually by writing something like:

\begin{tikzcd}
C\times D
\ar[r, bend left, "F" above]
\ar[r, bend right, "G"']
& \mathsf{Set}
\end{tikzcd}
tikz-cd, default positioning

which sets the anchor for the F label to be south rather than the computed south west.