1

I have the following code modified from my previous question:


% https://tex.stackexchange.com/a/96970
% https://tex.stackexchange.com/a/762399
\documentclass[tikz,border=5pt]{standalone}
\usetikzlibrary{arrows.meta,bending,nfold,ext.nodes,decorations.markings}
\makeatletter
\tikzset{
  foo/.tip={Straight Barb[harpoon,swap,length=5pt]},
  mystyle/.style={thick,shorten >=2pt,shorten <=2pt},
  offset/.code=
    \tikz@addoption{%
      \pgfgetpath\tikz@temp
      \pgfsetpath\pgfutil@empty
      \pgfoffsetpath\tikz@temp{#1}
    },
  dualharpoon/.style={
    draw=none,
    postaction={path only,draw,foo-,mystyle,offset=+#1ex},
    postaction={path only,draw,-foo,mystyle,offset=-#1ex},
    postaction={decorate, decoration={markings, mark=at position 0.5 with {
                    \begin{scope}[yshift=+#1ex]
                        \draw[-,line cap=round,thick] ++ (-.15cm,-.15cm) -- (+.15cm,+.15cm);
                        % \draw[-,line cap=round,thick] ++ (-.15cm,+.15cm) -- (+.15cm,-.15cm);
                    \end{scope}
                    \begin{scope}[yshift=-#1ex]
                        \draw[-,line cap=round,thick] ++ (-.15cm,-.15cm) -- (+.15cm,+.15cm);
                        % \draw[-,line cap=round,thick] ++ (-.15cm,+.15cm) -- (+.15cm,-.15cm);
                    \end{scope}
                }},
            },
  },
  dualharpoon/.default=.75,
}
\makeatother
\begin{document}
\begin{tikzpicture}[
    Qstyle/.style={
        dualharpoon,
        ext/auto with offset, % default true
        ext/auto offset=1ex,
        auto,
    }]
    \node[draw,circle,fill=teal] (A) at (0,1) {A};
    \node[draw,circle,fill=magenta] (B) at (0,-1) {B};
    \node[draw,circle,fill=cyan] (C) at (2.5,.5) {C};
\draw[magenta,Qstyle] (A) to[bend right] node {$\Delta$} node[swap]{$\nabla$} (B);
\draw[teal,Qstyle] (B) to[bend right] node {$f(x)$} node[swap]{$g(x)$} (C);
\draw[orange,Qstyle] (A) -- node {$x$} node[swap]{$y$} (C);
\end{tikzpicture}
\end{document}

res

Now I want two control switches:

  • which one to place slash: positive offset one? negetive one? or both? or none?
  • which direction to put slash?
    • forward: \draw[-,line cap=round,thick] ++ (-.15cm,-.15cm) -- (+.15cm,+.15cm); and \draw[-,line cap=round,thick] ++ (-.15cm,-.15cm) -- (+.15cm,+.15cm);
    • backward: \draw[-,line cap=round,thick] ++ (-.15cm,+.15cm) -- (+.15cm,-.15cm); or \draw[-,line cap=round,thick] ++ (-.15cm,+.15cm) -- (+.15cm,-.15cm);?

Or in other word, I want the syntax for example:

\draw[magenta,Qstyle,slash side=both,slash direction=backward] (A) to[bend right] node {$\Delta$} node[swap]{$\nabla$} (B);
\draw[teal,Qstyle,slash side=positive,slash direction=forward] (B) to[bend right] node {$f(x)$} node[swap]{$g(x)$} (C);
\draw[orange,Qstyle,slash side=none] (A) -- node {$x$} node[swap]{$y$} (C);

I was after a neat option syntax to encapsulate the logic above. Any suggestions?

1
  • 1
    Thanks for the proposed syntax. This makes it much easier to understand the requirement. Two things: 1. The offset implementation of A763043 would shift the path before decorations which means yshifting in the markings wouldn't be necessary, they would however be moved towards the postactions of the offsetting. 2. An alternative to the markings would be a pic which would be easier to use for your syntax but can only be placed by time and not by distance. For symmetric curves, this will not be a problem. Commented 11 hours ago

1 Answer 1

4

I've opted for a slash pic instead of the markings decoration since it is a bit easier to handle. For symmetric Bézier curves where half time equals half distance, the output should be identical:

slash/.pic={\draw[-,line cap=round,thick,pic actions] (-.15cm,-.15cm) -- (+.15cm,+.15cm);},

The idea behind my answer is, to install a style for every to that does something:

slash/install/.style={
  every to/.append style={slash side/do something},
  slash/install/.code=, % and disable
},

That something is defined by the options of slash side and slash direction.

Value-keys

The easy choice is to use value-keys whose allowed values are used as part of a name of other keys. Thus, something would be the choice of slash side:

slash/install/.style={
  every to/.append style={slash side/do \pgfkeysvalueof{/tikz/slash side}},
  slash/install/.code=, % and disable
},

Depending on slash side one of these keys will be executed:

  • slash side/do none → does nothing
  • slash side/do positive → places only the positive slash
  • slash side/do negative → places only the negative slash
  • slash side/do both → places both slashes

The key slash/do adds a pic slash to the to. This is used by the mentioned keys, thus their definition is:

slash side/do     none/.code=,
slash side/do positive/.style={slash/do},
slash side/do negative/.style={slash/do=swap},
slash side/do     both/.style={slash/do, slash/do=swap},

The same approach is used for slash direction where forward does nothing and backward flips the pic. The slash/do implementation is then simply

slash/do/.style={
  edge node={
    pic[
      midway, sloped, allow upside down,#1,
      slash direction/do \pgfkeysvalueof{/tikz/slash direction},
    ]{slash}}
},

One drawback of this is that if the user uses an illegal option like slash side=left they will get the error message

I do not know the key '/tikz/slash side/do left' and I am going to ignore it. Perhaps you misspelled it.

which isn't the most helpful. (You can of course make some auxilliary keys that check whether the values of slash side and slash direction are valid and/or make it simply fail silently by using the .try handler on these dynamically used keys.)

Choices and value-keys

One alternative is to use an .is choice key where PGFKeys implements this validation itself. You can even adapt the previous implementation quite easily by overloading the slash side key:

slash side/.is choice,
slash side/none/.code=    \pgfkeyssetvalue{/tikz/slash side}{none},
slash side/positive/.code=\pgfkeyssetvalue{/tikz/slash side}{positive},
slash side/negative/.code=\pgfkeyssetvalue{/tikz/slash side}{negative},
slash side/both/.code=    \pgfkeyssetvalue{/tikz/slash side}{both},
slash side=none,

If now one uses slash side=left, the error message is

Choice 'left' unknown in choice key '/tikz/slash side'. I am going to ignore this key.

and the previous value will be used in your implementation.

This makes it also very easy to implement aliases, both

slash side/left/.code=\pgfkeyssetvalue{/tikz/slash side}{positive},
slash side/left/.style={slash side/positive},

would be valued while the second implementation can be left alone even if you choose to change positive's implementation.

Choices only

You can also skip a step and simply make the choices (re)define the key that has been installed. Instead of a key being installed that is directly dependent on slash side, we'll install a key that will be defined by the choices of slash side:

slash/install/.style={
  every to/.append style={slash side/do slashes},
  slash/install/.code=, % and disable
},
slash side/.is choice,
slash side/none/.style=    {slash side/do slashes/.code=},
slash side/positive/.style={slash side/do slashes/.style={slash/do}},
slash side/negative/.style={slash side/do slashes/.style={slash/do=swap}},
slash side/both/.style=    {slash side/do slashes/.style={slash/do, slash/do=swap}},
slash side=none,

Aliases are still possible of course, both

slash side/left/.style={slash side/do slashes/.style={slash/do}},
slash side/left/.style={slash side/positive},

would do the job while the second would not have you touch left if positive's implementation changes.

Summary

I like the first option (only value-keys) since it makes it very clear where your choices matter: That's where the value of your choice will be accessed. It also separates the choices from their implementation.

But it has bad error handling which makes the second option (choice of value-keys) slightly more preferrable. This could gain from a separate handler like .value choice={none, positive, negative, both} that defines the choices directly.

The third option (only choices) combines both the choosing and the implementation of the choices in one. It's less clear when choices matter but it has less overhead than the second option.

The code below has all options but only the third one is uncommented. The pic is treaded like a node making use of the auto offset value as well, meaning it gets shifted away from the non-offsetted path.


The remaining question is then where to use the installer: I've opted to ass it to the Qstyle style since without it, the slashes make no sense, don't they? Sicne slash/install deactivates itself it should be safe to be also used in slash side and slash direction but if you want to define a side or a direction on a scope, this can mess paths up that don't use Qstyle.

I've also moved the auto settings into dualharpoon so it can use #1ex as the ext/auto offset to move both nodes and pics in the proper direction.

Code

\documentclass[tikz,border=5pt]{standalone}
\usetikzlibrary{arrows.meta,bending,nfold,ext.nodes}
\makeatletter
\tikzset{
  foo/.tip={Straight Barb[harpoon,swap,length=5pt]},
  mystyle/.style={thick,shorten >=2pt,shorten <=2pt},
  offset/.code=
    \tikz@addoption{%
      \pgfgetpath\tikz@temp
      \pgfsetpath\pgfutil@empty
      \pgfoffsetpath\tikz@temp{#1}%
    },
  dualharpoon/.style={
    draw=none,
    postaction={path only,draw,foo-,mystyle,offset=+#1ex},
    postaction={path only,draw,-foo,mystyle,offset=-#1ex},
    ext/auto with offset,
    ext/auto offset=#1ex,
    auto,
  },
  dualharpoon/.default=.75,
%%% OPTION 1
%  % slash installer
%  slash/install/.style={
%    every to/.append style={slash side/do \pgfkeysvalueof{/tikz/slash side}},
%    slash/install/.code=, % and disable
%  },
%  % slash direction
%  slash direction/.initial=forward,
%  slash direction/do forward/.code=,
%  slash direction/do backward/.style={xscale=-1},
%  % slash side
%  slash side/.initial=none,
%  slash side/do     none/.code=,
%  slash side/do positive/.style={slash/do},
%  slash side/do negative/.style={slash/do=swap},
%  slash side/do     both/.style={slash/do, slash/do=swap},
%  % slash placer
%  slash/do/.style={
%    edge node={
%      pic[
%        midway, sloped,#1,
%        slash direction/do \pgfkeysvalueof{/tikz/slash direction},
%      ]{slash}}
%  },
%%% END OPTION 1
%%% OPTION 2
%  % slash installer
%  slash/install/.style={
%    every to/.append style={slash side/do \pgfkeysvalueof{/tikz/slash side}},
%    slash/install/.code=, % and disable
%  },
%  % slash direction
%  slash direction/.is choice,
%  slash direction/forward/.code=\pgfkeyssetvalue{/tikz/slash direction}{forward},
%  slash direction/backward/.code=\pgfkeyssetvalue{/tikz/slash direction}{backward},
%  slash direction=forward,
%  slash direction/do forward/.code=,
%  slash direction/do backward/.style={xscale=-1},
%  % slash side
%  slash side/.is choice,
%  slash side/none/.code=    \pgfkeyssetvalue{/tikz/slash side}{none},
%  slash side/positive/.code=\pgfkeyssetvalue{/tikz/slash side}{positive},
%  slash side/negative/.code=\pgfkeyssetvalue{/tikz/slash side}{negative},
%  slash side/both/.code=    \pgfkeyssetvalue{/tikz/slash side}{both},
%  slash side=none,
%  slash side/do     none/.code=,
%  slash side/do positive/.style={slash/do},
%  slash side/do negative/.style={slash/do=swap},
%  slash side/do     both/.style={slash/do, slash/do=swap},
%  % slash placer
%  slash/do/.style={
%    edge node={
%      pic[
%        midway, sloped,#1,
%        slash direction/do \pgfkeysvalueof{/tikz/slash direction}
%      ]{slash}}
%  },
%%% END OPTION 2
%%% OPTION 3
  % slash installer
  slash/install/.style={
    every to/.append style={slash side/do slashes},
    slash/install/.code=, % and disable
  },
  % slash direction
  slash direction/.is choice,
  slash direction/forward/.style={slash direction/do/.code=},
  slash direction/backward/.style={slash direction/do/.style={xscale=-1}},
  slash direction=forward,
  % slash side
  slash side/.is choice,
  slash side/none/.style=    {slash side/do slashes/.code=},
  slash side/positive/.style={slash side/do slashes/.style={slash/do}},
  slash side/negative/.style={slash side/do slashes/.style={slash/do=swap}},
  slash side/both/.style=    {slash side/do slashes/.style={slash/do, slash/do=swap}},
  slash side=none,
  % slash placer
  slash/do/.style={
    edge node={
      pic[midway,sloped,allow upside down,#1,slash direction/do]{slash}}
  },
%%% END OPTION 3
  slash/.pic={\draw[-,line cap=round,thick,pic actions] (-.15cm,-.15cm) -- (+.15cm,+.15cm);},
  Qstyle/.style={
    dualharpoon, % = .75(ex)
    slash/install
  },
}
\makeatother
\begin{document}
\begin{tikzpicture}
\node[draw,circle,fill=teal] (A) at (0,1) {A};
\node[draw,circle,fill=magenta] (B) at (0,-1) {B};
\node[draw,circle,fill=cyan] (C) at (2.5,.5) {C};

\draw[magenta, Qstyle, slash side=both,    slash direction=backward]
  (A) to[bend right] node {$\Delta$} node[swap]{$\nabla$} (B);
\draw[teal,    Qstyle, slash side=positive,slash direction=forward ]
  (B) to[bend right] node {$f(x)$}   node[swap]{$g(x)$}   (C);
\draw[orange,  Qstyle, slash side=none                             ]
  (A) -- node {$x$} node[swap]{$y$} (C);
\end{tikzpicture}
\end{document}
8
  • 1
    The pic isn't really needed, this should also be possible with any decoration, even one decoration that places one, the other or both slashes. But I was committed to the pic because it was much easier to handle. Commented 10 hours ago
  • Excellent with three approach play with pic, but I found that to put the \pic {slash} at the center of the offseted arrow, the dualharpoon/.default=.75, value shoule be the same as ext/auto offset=.75ex,? Maybe these two should always be the same in this specific case? Commented 6 hours ago
  • 1
    And if you don't want the dependency on ext.nodes for the slashes: since the pics are sloped – unlike the nodes –, the offset is just a yshift (just like in your markings declaration). The positive in one direction and the negative in the other one. You might want to look into allow upside down for the slashes. If #1371 does make it into a version of TikZ, the auto with offset stuff needs to be rewritten anyway. Commented 6 hours ago
  • "I think I got a bit confused with your ext/auto offset=1ex for the nodes. In a previous version" Because the setting was copied from here, which was used by label, not for slash, the .pic now, the new version. Commented 6 hours ago
  • 1
    I've updated the answer and added allow upside down to the pics. That will always make them appear the same way in relation the the path's direction. I've also moved the auto offset stuff into the definition of dualharpoon so that i can use #1ex as well. (I've also cleaned up some comments.) Commented 5 hours ago

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.