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{% \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); \testfrom \end{tikzpicture} \begin{tikzpicture} \coordinate (a) at (0,0); \coordinate (b) at (3,0); \coordinate (c) at (1,2); \testfrom= \end{tikzpicture} \begin{tikzpicture} \coordinate (a) at (0,0); \coordinate (b) at (3,0); \coordinate (c) at (1,2); \testfrom=c \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

cgnieder
  • 66,645
Alain Matthes
  • 95,075
  • 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 Feb 12 '22 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 Feb 13 '22 at 00:00
  • 1
    Your question seems to be an XY problem... – Paul Gaborit Feb 13 '22 at 00:16
  • @PaulGaborit I did not know the XY problem but you are probably right – Alain Matthes Feb 13 '22 at 05:33
  • @Skillmon Yes my problem was to have a separation between two cases only. `pgfkeysnovalue' is confusing it is empty without being really. – Alain Matthes Feb 13 '22 at 05:41
  • The three usual cases are: the absence of the key (the initial value is used), the key without value (the default value is used), the key with a value (which is used). – Paul Gaborit Feb 13 '22 at 07:24
  • @PaulGaborit So my code was inaccurate if I add from/.default = {}, then I only need one more test. ( empty or not empty) – Alain Matthes Feb 13 '22 at 07:42
  • In my opinion, the nature of the value (empty, authorized, etc.) must be tested in a second step. – Paul Gaborit Feb 13 '22 at 07:52

2 Answers2

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{% \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) \testkey1,key2 \testkey1=test 1,key2=test 2 \testkey1=,key2= \end{minipage} \end{document}

enter image description here

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

Paul Gaborit
  • 70,770
  • 10
  • 176
  • 283
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

Ulrich Diez
  • 28,770
  • 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. – Henri Menke Feb 13 '22 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. – Ulrich Diez Feb 13 '22 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. – Henri Menke Feb 13 '22 at 19:30