2

Previously I have asked about passing options to pics to manipulate the way in which they are drawn. With this one may create pics with optional components and user controlled dimensions. I haven't asked about supporting optional parts of a pic. That is the topic of this question. Again this is targeting the best practices.

Requirements :

Optionally enabling additional detail within a pic. This allows one to draw a basic shape which might be modified or appended to as necessary.

Background:

From the pgfmanual a pic must define or set the key /tikz/pics/code, this is done by the short cut KEY/.pic or if a more elaborate pic must be defined via a style using pics/KEY/.style={code={...}}.

Code :

I have tried the following with the intention that each code segment might be stacked or packed together, instead the latest code overwrites the previous code segment. Whichever is last is the one that is executed.

\documentclass[tikz]{standalone}

\tikzset{
 pics/.cd,
 complex/.is family,
 complex/.search also={/tikz/pics},
 complex/lower/.style={
  code={
   \path[pic actions] (-1 em, -1 em) -- (1 em, 1 em) |- cycle;
   }
  },
 complex/upper/.style={
  code={
   \path[pic actions] (-1 em, -1 em) -- (1 em, 1 em) -| cycle;
   }
  },
 complex/center/.style={
  code={
   \path[pic actions] (-0.5 em, -0.5 em) rectangle (0.5 em, 0.5 em);
   }
  },
 complex/.style={complex/.cd, center, #1}, % source/.get=\complex@code, code={\complex@code}
}

\begin{document}
  \tikz \draw pic[draw] {complex, lower, upper};
\end{document}

Question:

Given that the code parts overwrite one another is there a way to enable a pic to have additional geometry ?

Answers :

I've provided my own answer for this question but am hoping for a better suggestion if there is one.

1

The first solution aims to provide flexibility as, by minor modification, it allows one to prepen/append code to the basic geometry by using the .add code/.append code/.prefix code handlers. The combined code created by app/prepending these snippets is then passed onto the /tikz/pics/code key to draw the pic. The second solution dumps everything into a draw command, which does not feel like a robust solution.

First Solution :

We start off by setting the appropriate keys for the shape. Note that upper and lower actually 'tack' code onto the original source, this allows us to make more complex shapes that incrementally add to some base shape.

\documentclass[tikz]{standalone}

\tikzset{
 pics/.cd,
 complex/.is family,
 complex/source/.code={\draw[pic actions] (-1 em, -1 em) -| (1 em, 1 em);},
 complex/upper/.style={
  source/.append code={
   \path[pic actions, rotate=45] (-1 em,-1 em) rectangle (1 em,1 em);
   }
  },
 complex/lower/.style={
  source/.append code={
   \path[pic actions] (-0.5 em, -0.5 em) rectangle (0.5 em, 0.5 em);
   }
  },
 }

I had to use fixed paths with the next code, relative paths just don't seem to work here due to the self reflection/invocation (I don't think this counts as recursive but it's close). (See the notes below)

    \tikzet{
     pics/.cd,
     complex/.style={
      /tikz/pics/code={
       \tikzset{pics/complex}                 % Executes the style
       \tikzset{pics/complex/source}          % Executes the code
      },
      complex/.append style={complex/.cd,#1}, % Sets up the style
     }, 
    }

Finally we create the body of the document as follows

\begin{document}
 \tikz draw pic[draw] {complex={upper}};
\end{document}

I believe it's best to enforce an interface like \draw [draw] pic {complex={lower}}; as is done above. If you need to supply a more liberal interface the notes provide some (most?) alternatives.

Notes:

By using the following code we can have rather fine grained control over the interface we expose to the pic user.

 \documentclass[tikz]{standalone}
 ...
 \tikzset{ 
 pics/.cd,
 %complex/.search also={/tikz,/pgf},     %[3]
 complex/.style={
  code={
   \tikzset{pics/complex/source}
   },
  %complex/.cd,                          %[1]
  %#1                                    %[2] 
  },
 }
...
\begin{document}
 % Standard interface    
 \tikz draw pic[draw] {complex};                       
 % Enable [1]
 \tikz draw pic[draw] {complex, upper};                
 % Enable [1] and [2]
 \tikz draw pic[draw] {complex={upper}, lower};        
 % Enable [1]-[3]
 \tikz draw pic[draw] {complex={upper,double}, lower, thin}; 
\end{document}

Uncommenting the lines marked [1]-[3] enable(s) or disable(s) different interfaces as follows :

  1. Keeping all three lines commented provides the standard pic behavior, allowing calls of the form \draw pic[draw] {complex};. Notably pic actions won't alter the geometry of the pic so \draw pic[draw, /pic/complex/upper] {complex}; will fail to produce the upper geometry but will still draw the base shape.

  2. Enabling [1] allows calls like \draw [draw] pic {complex, lower, upper}; this is because the style changed the current path to /tikz/pics/complex and the later arguments, upper, lower, now execute within this space.

  3. Enabling [1] and [3] allows calls like \draw [draw] pic {complex, lower, upper, double};, where upper, lower are dealt with as before in point 2. double on the other hand is a key under the /tikz root and not recognized under the /tikz/pics/complex root. However, we have allowed unknown keys in /tikz/pics/complex to fail back to /tikz and /pgf and so the key double is subsequently recognized. This is fussy about colours for some reason, \draw [draw] pic {complex={lower, red}, upper, double}; fails for example. As it is fussy about colours it will probably not like arrow specifications either. Apparently both colours and arrows are treated similarly within TikZ.

  4. Enabling [1] and [2] allows arguments to be passed to complex enabling calls like \draw [draw] pic {complex={lower}, upper};.

  5. Enabling [1], [2] and [3] allows arguments to be passed pretty much anywhere \draw [draw] pic {complex={lower, fill=red}, upper, double};. Preference is given to later arguments \draw [draw] pic {complex={fill=red}, fill=green}; results in a green symbol. Enabling this interface is probably a bad idea, it seems to encourages sloppy programming and bears far too much resemblance to XML.

Second Solution :

The second solution offers a little less flexibility but produces cleaner code.

\tikzset{
 pics/.cd,
 complex/.is family,
 complex/upper/.code={
   \path[pic actions] (-1 em, -1 em) -- (1 em, 1 em) -| cycle;
  },
 complex/lower/.code={
   \path[pic actions] (-1 em, -1 em) -- (1 em, 1 em) |- cycle;
  },
 complex/center/.code={
   \path[pic actions] (-0.5 em, -0.5 em) rectangle (0.5 em, 0.5 em);
  },
}

Though it is slightly hacky as it relies upon the draw command to invoke the other code segments contributing to the final shape.

\tikzset{
 pics/.cd,
 complex/.style={
  code={\draw [pics/complex/.cd, center, #1];}
  }
}

This only seems to allow a single interface

\begin{document}
 \tikz draw pic[draw] {complex={upper}};
\end{document}
  • you can make it parametric with a code that receives keys and you can activate different parts of the code with \ifupperisgiven...\fi so you can have one big pic and run different parts of it. – percusse Sep 2 '16 at 6:59
  • Does that test if a specific key was given by the user i.e. if one setup a key upper/.default=0 would \ifupperisgiven be true if the user specified upper = 3 and be false otherwise ? (Btw. thanks for this and prior help) – Carel Sep 2 '16 at 13:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.