2

I looked at Check-the-value-of-a-pgfkey but I don't feel like using etoolbox.

I found a method but I am not sure it is the right one and I would like to find more concise. My problem is to test all cases when using a key.

It is called from and is used with the macro test. I have considered the following cases:

  1. \test[](a,b) or \test(a,b)
  2. \test[from](a,b)
  3. \test[from=](a,b)
  4. \test[from=c](a,b)

I have discarded for the moment the case from=d (d undefined)

\documentclass[border=1cm]{standalone} 
\usepackage{tikz}
\def\empty{}
\makeatletter
\def\test{\pgfutil@ifnextchar[{\@test}{\@test[]}}
\pgfkeys{/test/.cd,
  from/.store in           = \from,
  from                     = {}}
  
\def\@test[#1](#2,#3){%
\begingroup
\pgfqkeys{/test}{#1}
\ifx\from\empty%
  \draw[red] (#2) -- (#3);
\else 
\expandafter\ifx\expandafter\pgfkeysnovalue\from\relax
  \draw[blue] (#2) -- (#3);
  \else
 \draw[green] (\from) -- (#3);
 \fi
 \fi
 \endgroup
}
\makeatother
\begin{document} 
\begin{tikzpicture}
\coordinate (a) at (0,0);
\coordinate (b) at (3,0);
\coordinate (c) at (1,2);
\test(a,b)
\end{tikzpicture}
\begin{tikzpicture}
\coordinate (a) at (0,0);
\coordinate (b) at (3,0);
\coordinate (c) at (1,2);
\test[from](a,b)
\end{tikzpicture}
\begin{tikzpicture}
\coordinate (a) at (0,0);
\coordinate (b) at (3,0);
\coordinate (c) at (1,2);
\test[from=](a,b)
\end{tikzpicture}
\begin{tikzpicture}
\coordinate (a) at (0,0);
\coordinate (b) at (3,0);
\coordinate (c) at (1,2);
\test[from=c](a,b)
\end{tikzpicture}
\end{document}

Is my code correct? What other possibilities are there? Is it possible to avoid having both tests?

enter image description here

8
  • 1
    I don't see a way to avoid both tests in pgfkeys except for setting a default to be empty, hence \test[from]{foo,bar} being the same as \test[from={}]{foo,bar} and \test{foo,bar}. With expkv (unnecessarily adding another key=value interface) you'd have a perfect separation of keys without any value and keys with an empty value.
    – Skillmon
    Commented Feb 12, 2022 at 23:57
  • And to really avoid both tests you could do \def\myflag{\myflag}\pgfqkeys{/test}{from/.store in=\from,from/.default={\myflag},from} and upon usage use \ifx\from\myflag instead of \ifx\from\empty -> 1 test. Though you can make the macro name only arbitrarily unlikely, not impossible to get (but who would insert a macro name such as \csname this is{very}~un@likELy\endcsname?).
    – Skillmon
    Commented Feb 13, 2022 at 0:00
  • 1
    Your question seems to be an XY problem... Commented Feb 13, 2022 at 0:16
  • @PaulGaborit I did not know the XY problem but you are probably right Commented Feb 13, 2022 at 5:33
  • @Skillmon Yes my problem was to have a separation between two cases only. `pgfkeysnovalue' is confusing it is empty without being really. Commented Feb 13, 2022 at 5:41

2 Answers 2

1

Here is a solution with two variants: simple pgf key (key1) and key stored its own macro (key2).

\documentclass[border=1cm]{standalone} 
\usepackage{tikz}
\makeatletter
\pgfkeys{/test/.is family}
\def\test@set#1{\pgfkeys{test,#1}}
\test@set{
  key1/.initial={initial 1},
  key1/.default={default 1},
  %
  key2/.store in=\test@keytwo,
  key2={initial 2},
  key2/.default={default 2},
}

\def\test{\pgfutil@ifnextchar[{\@test}{\@test[]}}
\def\@test[#1](#2,#3){%
  \begingroup
  \test@set{#1}
  %
  \pgfkeysgetvalue{/test/key1}\test@keyone
  key1=\pgfkeysvalueof{/test/key1}
  (\ifx\test@keyone\empty empty\else not empty\fi),
  %
  key2=\test@keytwo{}
  (\ifx\test@keytwo\empty empty\else not empty\fi),
  %
  2=#2,
  %
  3=#3
  \par
  \endgroup
}
\makeatother
\begin{document}
\begin{minipage}{16cm}
  \test(a,b)
  \test[key1,key2](a,b)
  \test[key1=test 1,key2=test 2](a,b)
  \test[key1=,key2=](a,b)
\end{minipage}
\end{document}

enter image description here

Note: I use the \empty macro as defined by LaTeX.

1

I think you can crank out the following cases:

  • from-key not being provided at all.
  • from-key being provided without value.
  • from-key being provided with empty value. (I don't know if cranking out this case is really needed - afaik you can define the "nameless" coordinate/node.)
  • from-key being provided with a value that could be the name of a coordinate/node but does not denote a coordinate/node that is defined.
  • from-key being provided with a value that denotes the name of a coordinate/node that is defined.

The following example (where it is relied on \@undefined being undefined whenever carrying out \@test and \choosecolordraw) shows how I might do that.

However, the following example does not crank out the case

  • from-key being provided with a value that cannot be the name of a coordinate/node.

I think cranking out that case is not easy:
If I got tikz-documentation/implementation right, control sequence tokens whose names are of pattern pgf@sh@ns@⟨name of coordinate/node⟩ are formed from names of nodes/coordinates via \csname..\endcsname. Thus checking if a value can/cannot be the name of a coordinate/node implies checking if the tokens pgf@sh@ns@⟨name of coordinate/node⟩ can safely be used inside \csname..\endcsname. A 100% reliable check for testing if an arbitrary sequence of tokens can be used within \csname..\endcsname is not known to me.

\documentclass[border=1cm]{standalone} 
\usepackage{tikz}
\makeatletter
\pgfkeys{/test/.cd, from/.store in = \from,}%
\newcommand\from{}%
\newcommand*\test{\pgfutil@ifnextchar[{\@test}{\@test[]}}%
\@ifdefinable\@test{%
  \def\@test[#1](#2,#3){%
    \begingroup
    \let\from\@undefined
    \pgfqkeys{/test}{#1}%
    \choosecolordraw{from}{#2}{teal}{blue}{red}{yellow}{green} -- (#3);
    \endgroup
  }%
}%
\newcommand\choosecolordraw[7]{%
  % #1 - Name of macro that should expand to name of from-node/from-coordinate.
  %      That macro is undefined when the from-key is not provided at all.
  % #2 - Default-node if macro whose name is provided is undefined or
  %      expands to no-value-marker or expands to emptiness or does not
  %      denote a defined node/coordinate.
  % #3 - Color if macro whose name is provided is undefined.
  % #4 - Color if macro whose name is provided expands to pgfkey's no-value-marker.
  %      This is the case when the from-key is provided without value.
  % #5 - Color if macro whose name is provided expands to emptiness.
  %      This is the case when the from-key is provided with empty value.
  %      I don't know if cranking out this case  is really needed - afaik you can define the "nameless" \coordinate.
  % #6 - Color if macro whose name is provided expands to a value that doesn't denote a defined coordinate/node.
  %      This is the case when the from-key is provided with a value that could be the name of a coordinate/node but 
  %      does not denote a coordinate/node that is defined.
  % #7 - Color if macro whose name is provided expands to a value that does denote a defined coordinate/node.
  %      This is the case when the from-key is provided with a value that denotes the name of a coordinate/node that is defined.
  \@ifundefined{#1}%
               {\draw[{#3}] (#2) }%
               {%
                 \expandafter\expandafter\expandafter\fromfork
                 \expandafter\expandafter\expandafter{\csname#1\endcsname}{#2}{#4}{#5}{#6}{#7}%
               }%
}%
\newcommand\fromfork[6]{%
  % #1 - From-node.
  % #2 - Default-node.
  % #3 - Color if macro whose name is provided expands to pgfkey's no-value-marker.
  % #4 - Color if macro whose name is provided expands to emptiness.
  % #5 - Color if macro whose name is provided expands to a value that doesn't denote a defined coordinate/node.
  % #6 - Color if macro whose name is provided expands to a value that does denote a defined coordinate/node.
  \ifcat$\detokenize\expandafter{\gobbletoexclam#1!}$%
  \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
  {%
    \forkfrom
    !#1!\pgfkeysnovalue!{\draw[{#4}] (#2) }% #1 is empty
    !!#1!{\draw[{#3}] (#2)}% #1 is \pgfkeysnovalue
    !!\pgfkeysnovalue!{% #1 is s.th else where testing is needed whether it is a defined node.
      \@ifundefined{pgf@sh@ns@#1}{\draw[{#5}] (#2) }{\draw[{#6}] (#1) }%
    }%
    !!!!%
  }{%
    % The argument holding name of node/coordinate contains ! and therefore
    % using !-delimited macro is unsafe. But the presence of ! implies exclusion of the cases
    % - from-key not being provided at all
    % - from-key being provided with no value
    % - from-key being provided with empty value
    \@ifundefined{pgf@sh@ns@#1}{\draw[{#5}] (#2) }{\draw[{#6}] (#1) }%
  }%
}%
\@ifdefinable\gobbletoexclam{\long\def\gobbletoexclam#1!{}}%
\@ifdefinable\forkfrom{\long\def\forkfrom#1!!\pgfkeysnovalue!#2#3!!!!{#2}}%
\makeatother
\begin{document}
\vbox{\hbox{%
\begin{tikzpicture}
\coordinate (a) at (0,0);
\coordinate (b) at (3,0);
\coordinate (c) at (1,2);
\test(a,b)
\end{tikzpicture}
}\hbox{\tiny from-key not provided}}%
\kern1em\vrule\kern1em
\vbox{\hbox{%
\begin{tikzpicture}
\coordinate (a) at (0,0);
\coordinate (b) at (3,0);
\coordinate (c) at (1,2);
\test[from](a,b)
\end{tikzpicture}
}\hbox{\tiny from-key provided without value}}%
\kern1em\vrule\kern1em
\vbox{\hbox{%
\begin{tikzpicture}
\coordinate (a) at (0,0);
\coordinate (b) at (3,0);
\coordinate (c) at (1,2);
\test[from=](a,b)
\end{tikzpicture}
}\hbox{\tiny from-key provided with empty value}}%
\kern1em\vrule\kern1em
\vbox{\hbox{%
\begin{tikzpicture}
\coordinate (a) at (0,0);
\coordinate (b) at (3,0);
\coordinate (c) at (1,2);
\test[from=d](a,b)
\end{tikzpicture}
}\hbox{\tiny from-key provided with undefined coordinate}}%
\kern1em\vrule\kern1em
\vbox{\hbox{%
\begin{tikzpicture}
\coordinate (a) at (0,0);
\coordinate (b) at (3,0);
\coordinate (c) at (1,2);
\test[from=c](a,b)
\end{tikzpicture}
}\hbox{\tiny from-key provided with defined coordinate}}%
\end{document}

enter image description here

3
  • The csnames pgf@sh@ns@ are not part of the public user interface and therefore not stable (which is why they are not mentioned in the manual). Please do not use them. Commented Feb 13, 2022 at 15:58
  • 1
    @HenriMenke Seems pgfmanual does not mention whatsoever possibility of checking if a node/coordinate is defined/already exists. As long as no stable interfaces are provided the only option is resorting to "internals". I think adapting the code when things change will not be that hard. Commented Feb 13, 2022 at 16:44
  • 1
    Okay, I'll log that as a feature request, but note that you cannot check if a node is defined using \@ifundefined. PGF internally uses \csname quite a lot, so the name might well be defined as \relax. But you also cannot simply check \expandafter\ifx...\relax because there exists yet another special internal value pgf@sh@ns@not yet positionedPGFINTERNAL which is used by decorations. Commented Feb 13, 2022 at 19:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .