pgf-tikz / pgf

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

Pic angle in a pic doesn't respect pic name/name prefix. #1363

Open Qrrbrbirlbel opened 1 month ago

Qrrbrbirlbel commented 1 month ago

Brief outline of the bug

When the pic angle (or right angle) is used inside another pic, coordinates named inside the pic with non-empty name prefix or name suffix can't be used for coordinates of the angle pic.

This is, because \tikz@lib@angle@parse uses its argument directly with \pgfpointanchor and doesn't check whether a node named \tikz@pp@name{#?} exists: https://github.com/pgf-tikz/pgf/blob/ab7c1475b4acbaafd0f3ba8d166489d32fb0fe0e/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryangles.code.tex#L73-L84

My suggestion is to do just that:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{angles}
\newcommand*\tikzpicname{\pgfkeysvalueof{/tikz/name prefix}}

\makeatletter
\def\tikz@lib@angle@parse#1--#2--#3\pgf@stop{%
  % Compute radius:
  \pgfmathsetmacro\tikz@lib@angle@rad{\pgfkeysvalueof{/tikz/angle radius}}
  \ifdim\tikz@lib@angle@rad pt>0pt\else\def\tikz@lib@angle@rad{12}\fi%
  % Compute first coordinate:
  \pgf@process{\pgfpointanchor{\pgfutil@IfUndefined{pgf@sh@ns@\tikz@pp@name{#2}}{#2}{\tikz@pp@name{#2}}}{center}}%
  \pgf@xa=\pgf@x%
  \pgf@ya=\pgf@y%
  \pgf@process{\pgfpointanchor{\pgfutil@IfUndefined{pgf@sh@ns@\tikz@pp@name{#1}}{#1}{\tikz@pp@name{#1}}}{center}}%
  \pgf@xb=\pgf@x%
  \pgf@yb=\pgf@y%
  \pgf@process{\pgfpointanchor{\pgfutil@IfUndefined{pgf@sh@ns@\tikz@pp@name{#3}}{#3}{\tikz@pp@name{#3}}}{center}}%
  \pgf@xc=\pgf@x%
  \pgf@yc=\pgf@y%
  \advance\pgf@xb by-\pgf@xa%
  \advance\pgf@yb by-\pgf@ya%
  \advance\pgf@xc by-\pgf@xa%
  \advance\pgf@yc by-\pgf@ya%
  \pgfmathsetmacro{\tikz@start@angle@temp}{atan2(\the\pgf@yb,\the\pgf@xb)}
  \pgfmathsetmacro{\tikz@end@angle@temp}{atan2(\the\pgf@yc,\the\pgf@xc)}
  \ifdim\tikz@end@angle@temp pt<\tikz@start@angle@temp pt%
    \pgfmathsetmacro{\tikz@start@angle@temp}{\tikz@start@angle@temp-360}%
  \fi%
}%
\makeatother
\begin{document}
\tikz\pic (name-) {
  code={
    \coordinate (x) at (right:1) % actually named name-x
     coordinate (y) at (up:1)    % actually named name-y
     coordinate (O) at (0,0);    % actually named name-O
    \pic[draw, ultra thick]  {angle=x--O--y}; % works
    \pic[draw=yellow, thick] {angle=name-x--name-O--name-y}; % works as well
    \pic[draw=red]           {angle=\tikzpicname x--\tikzpicname O--\tikzpicname y}; % workaround still works
  }
};
\end{document}

Of course, just using \tikz@scan@one@point would work, too, while allowing arbitrary coordinates as well, but apparently that wasn't wanted for the angle pic since the manual points this out explicitly: https://github.com/pgf-tikz/pgf/blob/ab7c1475b4acbaafd0f3ba8d166489d32fb0fe0e/doc/generic/pgf/pgfmanual-en-library-angles.tex#L36-L38

Minimal working example (MWE)

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{angles}
\newcommand*\tikzpicname{\pgfkeysvalueof{/tikz/name prefix}}
\begin{document}
\tikz\pic (name-) {
  code={
    \coordinate (x) at (right:1) % actually named name-x
     coordinate (y) at (up:1)    % actually named name-y
     coordinate (O) at (0,0);    % actually named name-O
    \pic[draw, ultra thick]  {angle=x--O--y};                                        % fails
    \pic[draw=yellow, thick] {angle=name-x--name-O--name-y};                         % works
    \pic[draw=red]           {angle=\tikzpicname x--\tikzpicname O--\tikzpicname y}; % workaround
  }
};
\end{document}
hmenke commented 1 month ago

Even if it wasn't designed that way, I like the idea of using \tikz@scan@one@point. However, if it's a node, then we need to use the center anchor. This would probably require quite a lot of code. Maybe it's worth it.

Qrrbrbirlbel commented 1 month ago

Not really, since that returns the center anchor as a first suggestion and flags \iftikz@shapeborder for later: https://github.com/pgf-tikz/pgf/blob/ab7c1475b4acbaafd0f3ba8d166489d32fb0fe0e/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex#L5386

However, the other macros use #2.center so these also need to be changed. I don't see a reason why that is needed though.

Here's just the rewrite for the calculation. I've chosen to use a few quicker PGFMath macros. One could also think about using \pgfmathanglebetweenpoints but that does basically the same.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{angles}
\newcommand*\tikzpicname{\pgfkeysvalueof{/tikz/name prefix}}

\makeatletter
\def\tikz@lib@angle@parse#1--#2--#3\pgf@stop{%
  % Compute radius:
  \pgfmathsetmacro\tikz@lib@angle@rad{\pgfkeysvalueof{/tikz/angle radius}}%
  \ifdim\tikz@lib@angle@rad pt>0pt\else\def\tikz@lib@angle@rad{12}\fi
  % Compute first coordinate:
  \tikz@scan@@absolute\pgf@process(#2)%
  \pgf@xa=\pgf@x
  \pgf@ya=\pgf@y
  \tikz@scan@@absolute\pgf@process(#1)%
  \pgf@xb=\pgf@x
  \pgf@yb=\pgf@y
  \tikz@scan@@absolute\pgf@process(#3)%
  \pgf@xc=\pgf@x
  \pgf@yc=\pgf@y
  \advance\pgf@xb by-\pgf@xa
  \advance\pgf@yb by-\pgf@ya
  \advance\pgf@xc by-\pgf@xa
  \advance\pgf@yc by-\pgf@ya
  \pgfmathatantwo@{\pgf@sys@tonumber\pgf@yb}{\pgf@sys@tonumber\pgf@xb}%
  \let\tikz@start@angle@temp\pgfmathresult
  \pgfmathatantwo@{\pgf@sys@tonumber\pgf@yc}{\pgf@sys@tonumber\pgf@xc}%
  \let\tikz@end@angle@temp\pgfmathresult
  \ifdim\tikz@end@angle@temp pt<\tikz@start@angle@temp pt
    \pgfmathsubtract@{\tikz@start@angle@temp}{360}%
    \let\tikz@start@angle@temp\pgfmathresult
  \fi
}
\makeatother
\begin{document}
\tikz\pic (name-) {
  code={
    \coordinate (x) at (right:1) % actually named name-x
     coordinate (y) at (up:1)    % actually named name-y
     coordinate (O) at (0,0);    % actually named name-O
    \pic[draw, ultra thick]  {angle=x--O--y};                                        % works
    \pic[draw=yellow, thick] {angle=name-x--name-O--name-y};                         % works as well
    \pic[draw=red]           {angle=\tikzpicname x--\tikzpicname O--\tikzpicname y}; % workaround still works
  }
};
\end{document}
muzimuzhi commented 1 month ago

I didn't follow previous discussions, but the \pic[draw=yellow, thick] {angle=name-x--{0,0}--name-y}; used in the last example (https://github.com/pgf-tikz/pgf/issues/1363#issuecomment-2396317309) raises four identical errors

! Package PGF Math Error: Unknown operator `c' or `ce' (in '0.center').

It seems to support uses of non-node coordinates in angle specification, uses of #<n>.center in \tikz@lib@(right)angle@(back|fore)ground need to be generalized.

Qrrbrbirlbel commented 1 month ago

It seems to support uses of non-node coordinates in angle specification, uses of #<n>.center in \tikz@lib@(right)angle@(back|fore)ground need to be generalized.

Yes, I mentioned that in my post but didn't take out the offending code from the example.

However, the other macros use #2.center so these also need to be changed. I don't see a reason why that is needed though.

I've replaced it now so that the MWE works … and would also work for arbitrary coordinates but the #2 coordinate still needs to be a node name or the other macros need changing as well.