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}
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}
. Withexpkv
(unnecessarily adding another key=value interface) you'd have a perfect separation of keys without any value and keys with an empty value.\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
?).