pgf icon indicating copy to clipboard operation
pgf copied to clipboard

Adding the opposite of `open` to arrow tips definitions

Open muzimuzhi opened this issue 1 year ago • 21 comments

Discussed in https://github.com/pgf-tikz/pgf/discussions/1347

Originally posted by Rmano July 9, 2024 Good morning!

If I define a new arrow tip that by default is open, like this (which is still somehow buggy, I can't see exactly why)

%% Jack Tap, see
%% https://github.com/circuitikz/circuitikz/issues/806
\pgfdeclarearrow{name=Jack Tap,
    parameters = {%
    \the\pgfarrowlength,%
    \the\pgfarrowwidth,%
    \ifpgfarrowswap s\fi%
    \ifpgfarrowopen o\fi%
    },
    setup code = {
        \pgfarrowssettipend{.5\pgfarrowlength}
        \pgfarrowssetlineend{-.6\pgfarrowlength}
        \pgfarrowssetvisualbackend{-.6\pgfarrowlength}
        \pgfarrowssetbackend{-.6\pgfarrowlength}
        % hull
        \pgfarrowshullpoint{.5\pgfarrowlength}{0pt}
        \pgfarrowshullpoint{0pt}{\pgfarrowwidth}
        \pgfarrowshullpoint{-.6\pgfarrowlength}{0pt}
        % Saves: Only the length:
        \pgfarrowssavethe\pgfarrowlength
        \pgfarrowssavethe\pgfarrowwidth
    },
    drawing code = {
        \pgfpathmoveto{\pgfqpoint{.5\pgfarrowlength}{0pt}}
        \pgfpathlineto{\pgfqpoint{0pt}{\pgfarrowwidth}}
        \pgfpathlineto{\pgfqpoint{-.5\pgfarrowlength}{0pt}}
        \pgfpathlineto{\pgfqpoint{-.6\pgfarrowlength}{0pt}}
        \ifpgfarrowopen
            \pgfusepathqstroke
        \else
            \pgfpathclose\pgfusepathqfillstroke
        \fi
    },
    defaults = {length = 0.3cm, width=0.15cm,open},
    % cache=false, % breaks everything
}
\tikzset{v/.tip={Jack Tap[swap]}, ^/.tip={Jack Tap}}

then I can't switch to the not-open (closed, filled?) version without knowing the color, i.e., using Jack Barb[fill=black]. Wouldn't it be useful to add a "complementary" switch for open, like, for example:

\pgfkeys{/pgf/arrow keys/filled/.code=\pgfarrowsaddtooptions{\pgfarrowopenfalse}}

Or maybe closed, to convey the not-open thing?

I tried to search for a complementary flag without success, but if it's already there, please tell me ;-)

muzimuzhi avatar Jul 09 '24 23:07 muzimuzhi

Notice that the code of the arrow has been fixed; you can see it here: https://github.com/circuitikz/circuitikz/pull/810/files

I am not posting it because it really isn't that relevant --- the matter is adding a "not open" flag argument. I am using filled for circuitikz, but I'll happily adapt to what is felt to be better.

Rmano avatar Jul 10 '24 10:07 Rmano

I think Jack Tap[fill=.] does what you want.

hmenke avatar Jul 10 '24 16:07 hmenke

I think Jack Tap[fill=.] does what you want.

Ah, yes, probably it does. Hmmm... I still like more of a "flag" type, but yes, that's a good solution.

Rmano avatar Jul 10 '24 18:07 Rmano

Nope generally one needs Jack Tap[fill=pgfstrokecolor], in which pgfstrokecolor color name is never mentioned in the pgfmanual. But its corresponding pgffillcolor is mentioned in the doc of /pgf/arrow keys/fill.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}

\begin{document}

\pgfkeys{/pgf/arrow keys/filled/.code=\pgfarrowsaddtooptions{\pgfarrowopenfalse}}

\begin{tikzpicture}[nodes={anchor=west}, arrows={[scale=2]}]
  \draw[draw=red, fill=gray, -{Stealth}]
    (0, 0) -- (1, 0) -- (2,.5) node {default};
  \draw[draw=red, fill=gray, yshift=-0.5cm, -{Stealth[cyan]}]
    (0, 0) -- (1, 0) -- (2,.5) node {+ cyan};
  \draw[draw=red, fill=gray, yshift=-1.0cm, -{Stealth[open, cyan]}]
    (0, 0) -- (1, 0) -- (2,.5) node {+ open};
  \draw[draw=red, fill=gray, yshift=-1.5cm, -{Stealth[open, cyan, fill=.]}]
    (0, 0) -- (1, 0) -- (2,.5) node {current color ``.''};
  \draw[draw=red, fill=gray, yshift=-2.0cm, -{Stealth[open, cyan, fill=pgffillcolor]}]
    (0, 0) -- (1, 0) -- (2,.5) node {pgffillcolor};
  \draw[draw=red, fill=gray, yshift=-2.5cm, -{Stealth[open, cyan, fill=pgfstrokecolor]}]
    (0, 0) -- (1, 0) -- (2,.5) node {pgfstrokecolor};
  \draw[draw=red, fill=gray, yshift=-3.0cm, -{Stealth[open, cyan, filled]}]
    (0, 0) -- (1, 0) -- (2,.5) node {filled};
\end{tikzpicture}

\end{document}

image

muzimuzhi avatar Jul 10 '24 21:07 muzimuzhi

Well, the behavior is not really the same. Look at this (contrived) example:

\documentclass[]{article}
\usepackage[T1]{fontenc}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}

%% Jack Tap, see
%% https://github.com/circuitikz/circuitikz/issues/806
\pgfdeclarearrow{name=Jack Tap,
    parameters = {%
    \the\pgfarrowlength,%
    \the\pgfarrowwidth,%
    \ifpgfarrowswap s\fi%
    \ifpgfarrowopen o\fi%
    \ifpgfarrowroundjoin j\fi
    \ifpgfarrowroundcap c\fi%
    },
    setup code = {
        \pgfarrowssettipend{.5\pgfarrowlength}
        \pgfarrowssetlineend{-.6\pgfarrowlength}
        \pgfarrowssetvisualbackend{-.6\pgfarrowlength}
        \pgfarrowssetbackend{-.6\pgfarrowlength}
        % hull
        \pgfarrowshullpoint{.5\pgfarrowlength}{0pt}
        \pgfarrowshullpoint{0pt}{\pgfarrowwidth}
        \pgfarrowshullpoint{-.6\pgfarrowlength}{0pt}
        % Saves: Only the length:
        \pgfarrowssavethe\pgfarrowlength
        \pgfarrowssavethe\pgfarrowwidth
    },
    drawing code = {
        \pgfsetdash{}{+0pt}
        \pgfarrowlinewidth=\pgflinewidth
        \ifpgfarrowroundjoin\pgfsetroundjoin\else\pgfsetmiterjoin\fi
        \ifpgfarrowroundcap\pgfsetroundcap\else\pgfsetbuttcap\fi
        \pgfpathmoveto{\pgfqpoint{-.6\pgfarrowlength}{0pt}}
        \pgfpathlineto{\pgfqpoint{-.5\pgfarrowlength}{0pt}}
        \pgfpathlineto{\pgfqpoint{0pt}{\pgfarrowwidth}}
        \pgfpathlineto{\pgfqpoint{.5\pgfarrowlength}{0pt}}
        \ifpgfarrowopen
            \pgfusepathqstroke
        \else
            \pgfpathclose
            \ifdim\pgfarrowlinewidth>0pt\pgfusepathqfillstroke\else\pgfusepathqfill\fi
        \fi
    },
    defaults = {length = 0.3cm, width=0.15cm,open},
    % cache=false, % breaks everything
}
\pgfkeys{/pgf/arrow keys/filled/.code=\pgfarrowsaddtooptions{\pgfarrowopenfalse}}
\tikzset{v/.tip={Jack Tap[swap]}, ^/.tip={Jack Tap},
    vf/.tip={Jack Tap[swap,filled]}, ^f/.tip={Jack Tap[filled]}}

\begin{document}
\begin{tikzpicture}[color=blue]
    \draw[-{Jack Tap}] (0,2) edge ++(2,0) -- ++(0,1);
    \draw[red, -{Jack Tap[filled]}] (0,0) edge ++(2,0) -- ++(0,1);
    \draw[red, -{Jack Tap[fill=.]}] (0,-2) edge ++(2,0) -- ++(0,1);
\end{tikzpicture}
\end{document}

image

Rmano avatar Jul 11 '24 07:07 Rmano

I would like to avoid adding new keys. Currently Jack Tap[fill] (without argument) gives an error. We could make that work and make it behave like your proposed filled.

hmenke avatar Jul 11 '24 11:07 hmenke

Yes, that would be possible too --- although I think that having on/off for binary switch is also a clean interface.

My feeling would be to let fill as it is and add the possibility for open (and probably, for symmetry, swap) to take a boolean, like open=false (default to true).

Rmano avatar Jul 11 '24 12:07 Rmano

...thinking about it a bit more, probably the filled flag would be the easiest to add and document. It's just a sentence in the description of open ;-). I know it's a key more, but it's quite buried in the arrow tip hierarchy...

Rmano avatar Jul 15 '24 09:07 Rmano

Nice @hmenke, thank you. One little thing: I was trying to provide the fill with no argument for circuitikz now, and I was thinking something like checking for the version of tikz, and then if it's older than 3.1.11 install the code. But (apart from finding how to check the version, which I'll do ;-) later), there is a little problem: is it possible to "undo" the ...fill/.value required?

Rmano avatar Jul 15 '24 13:07 Rmano

@Rmano To undo /.value required, currently one has to make use of implementation details of pgfkeys: \cs_undefine:c {pgfk@/pgf/arrow~keys/fill/.@def} does the trick.

https://github.com/pgf-tikz/pgf/blob/44f8137449b34f62bc6371bd442ae5cc98f60f18/tex/generic/pgf/utilities/pgfkeys.code.tex#L853

Example undoing /.value required

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}

\begin{document}
\tikzset{/pgf/arrow keys/fill} % errors

\ExplSyntaxOn
\cs_undefine:c {pgfk@/pgf/arrow~keys/fill/.@def}
\ExplSyntaxOff
\tikzset{/pgf/arrow keys/fill} % passes
\end{document}

muzimuzhi avatar Jul 15 '24 17:07 muzimuzhi

Thanks @muzimuzhi ! I had managed to arrive at \pgfkeyssetvalue{/pgf/arrow keys/fill/.@def}{}% --- is that equivalent? It seems to work, and should work also for older formats without expl3...

Rmano avatar Jul 15 '24 17:07 Rmano

Also, I think that \expandafter\let\csname pgfk@/pgf/arrow keys/fill/.@def\endcsname\@undefined is doing the same thing as the \cs_undefine:c; am I correct?

Rmano avatar Jul 15 '24 18:07 Rmano

Also, I think that \expandafter\let\csname pgfk@/pgf/arrow keys/fill/.@def\endcsname\@undefined is doing the same thing as the \cs_undefine:c; am I correct?

Yes, that's correct.

Skillmon avatar Jul 15 '24 18:07 Skillmon

@Rmano

I had managed to arrive at \pgfkeyssetvalue{/pgf/arrow keys/fill/.@def}{}% --- is that equivalent?

From the point of view of pgfkeys, \pgfkeyssetvalue{/pgf/arrow keys/fill/.@def}{} is equivalent to setting /pgf/arrow keys/fill/.default={}, hence slightly different than just undoing effect of /.value required. But it might be the right final form, depending on how #1352 will be modified, see my review https://github.com/pgf-tikz/pgf/pull/1352/files#r1678191162.

Possible final form of workaround for tikz v3.1.10 or older

\IfPackageAtLeastTF{tikz}{2023/01/16}{}{% one day after v3.1.10 (2023-01-15)
  \pgfkeys{
    /pgf/arrow keys/fill/.code={...}, % new code in v3.1.11 pasted here
    /pgf/arrow keys/fill/.default={}
  }%
}

muzimuzhi avatar Jul 15 '24 18:07 muzimuzhi

@muzimuzhi Thanks - I suppose that the code in your last answer needs LaTeX, am I right? I mean, it would not work in ConTeXt for example...

Rmano avatar Jul 15 '24 20:07 Rmano

@Rmano Yes, \IfPackageAtLeastTF needs LaTeX (sorry). For a portable solution, it seems you have to parse the date or version stored in pgf.revision.tex yourself.

$ cat `kpsewhich pgf.revision.tex`
\def\pgfrevision{3.1.10}
\def\pgfversion{3.1.10}
\def\pgfrevisiondate{2023-01-15}
\def\pgfversiondate{2023-01-15}

muzimuzhi avatar Jul 15 '24 21:07 muzimuzhi

Thanks @muzimuzhi. Unfortunately, I found that the format of the date changed between 2021 and 2022 --- it was separated by / before... :-(

Rmano avatar Jul 16 '24 17:07 Rmano

Maybe I could just check if /pgf/arrow keys/fill/.@def is defined, and if yes, apply the change. Or surrender and just add the filled key and be done...

Rmano avatar Jul 16 '24 17:07 Rmano

@Rmano Here is a parser that parses three numbers separated by a single arbitrary non-digit token (or group). It assumes YYYY<sep>MM<sep>dd (optionally followed by another <sep>, <sep> doesn't have to be uniform, it's just gobbled).

\newcount\pdYear
\newcount\pdMonth
\newcount\pdDay
\begingroup
\catcode`\@=11
\unexpanded{\endgroup
% pgfutil replacement code >>>
\let\pgfutil@protected\protected
% <<<

\pgfutil@protected\def\parsedate#1%
  {%
    \afterassignment\parsedate@month
    \pdYear=#1\relax
  }
\pgfutil@protected\def\parsedate@month#1%
  {%
    \afterassignment\parsedate@day
    \pdMonth=%
  }
\pgfutil@protected\def\parsedate@day#1%
  {%
    \afterassignment\parsedate@cleanup
    \pdDay=%
  }
\def\parsedate@cleanup#1\relax{}
}

\def\test#1{\parsedate{#1}\the\pdYear-\the\pdMonth-\the\pdDay\par}

\test{2024-02-01}
\test{2024/02/01}
\test{2024.02.01}
\bye

Keep in mind that you don't have to alter the category code, so don't need the \unexpanded (and if you did, you'd want to use \pgfutil@unexpanded instead for Context support).

Skillmon avatar Jul 16 '24 19:07 Skillmon

@Skillmon thanks again. I was not sure about the \unexpanded thing and grouping there, but it seems that it works ok:

https://github.com/circuitikz/circuitikz/blob/5537c8f2420f43e49fb23887e8d417b96e4313bd/tex/pgfcirc.defines.tex#L269-L319

Rmano avatar Jul 17 '24 14:07 Rmano

@Rmano Your code looks good. The \begingroup\catcode`\@=11 \unravel{\endgroup...} is just a scoped \makeatletter...\makeatother and not necessary in package code (since your setup for your code level should already deal with @ being a letter).

Skillmon avatar Jul 18 '24 21:07 Skillmon