Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

In the following MWE, I declare a new shape myshape. In several instances, I plan to use a dimension that is calculated from different pgf keys (\my@width). One of these instances includes the declaration of the saved anchor \base. At the moment, I have to duplicate the code that calculated this dimension, as I cannot simply use \my@width inside the \savedanchor declaration (it doesn't seem to be available or default to 0/empty). So I am wondering if there is a way to achieve this or if I am simply putting the cart before the horse, i.e. should I ditch the dimension altogether (I'm assuming I can't use the saved anchor inside the \saveddimen declaration either) and rely only on the saved anchor definition?

\documentclass[a4paper]{article}
\usepackage{tikz}

\makeatletter
\pgfdeclareshape{myshape}
{
  %
  % Saved dimensions
  %
  \saveddimen{\my@width}{%
    \pgfmathsetlength{\pgf@xa}{\pgfkeysvalueof{/pgf/minimum width}}
    \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/inner xsep}}
    \pgfmathsetlength{\pgf@xc}{\pgfkeysvalueof{/pgf/outer xsep}}
    \pgf@x=.5\pgf@xa
    \advance\pgf@x by .5\pgf@xb
    \advance\pgf@x by .5\pgf@xc
  }
  \saveddimen{\my@height}{%
    \pgf@x=\pgfkeysvalueof{/pgf/minimum height}%
  }

  %
  % Saved anchors
  %
  \savedanchor\base{%
    % Duplicated code, can't use \my@width here
    \pgfmathsetlength{\pgf@xa}{\pgfkeysvalueof{/pgf/minimum width}}
    \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/inner xsep}}
    \pgfmathsetlength{\pgf@xc}{\pgfkeysvalueof{/pgf/outer xsep}}
    \pgf@x=.5\pgf@xa
    \advance\pgf@x by .5\pgf@xb
    \advance\pgf@x by .5\pgf@xc
    \pgf@y=0pt
  }

  %
  % Anchors
  %
  \anchor{center}{%
    \base
    \pgfutil@tempdima=\my@height
    \advance\pgf@y by .5\pgfutil@tempdima
  }
  \anchor{south}{\base}

  %
  % Background path
  %
  \backgroundpath{%
    \pgfutil@tempdima=\my@width
    \pgfutil@tempdimb=\my@height
    \pgf@xa=.5\pgfutil@tempdima
    \pgf@ya=0pt
    \pgfpathmoveto{\pgfpoint{\pgf@xa}{\pgf@ya}}
    \advance\pgf@ya by \pgfutil@tempdimb
    \pgfpathlineto{\pgfpoint{\pgf@xa}{\pgf@ya}}
    \advance\pgf@xa by -.5\pgfutil@tempdima
    \pgfpathmoveto{\pgfpoint{\pgf@xa}{\pgf@ya}}
    \advance\pgf@xa by \pgfutil@tempdima
    \pgfpathlineto{\pgfpoint{\pgf@xa}{\pgf@ya}}
  }
}
\tikzset{
  every myshape node/.style={
    draw=black, thick, minimum width=1cm, minimum height=2.5cm,
  }
}
\makeatother

\begin{document}
\begin{tikzpicture}
  \node[myshape] at (0,0) {};
  \node[myshape, inner sep=10pt] at (3,0) {};
\end{tikzpicture}
\end{document}
share|improve this question
add comment

2 Answers

This answer is based on a lengthy discussion in the chat.

This answer avoids having to save dimension and uses saved anchors.

The \northeast anchor stores the height (including the outer ysep) and half of the width (including half the outer xsep). This is \my@width and \my@height in one anchor.

The \innerwest anchor stores the outer separators (the default is .5\pgflinewidth which is the reason the anchors lie on the outer side of the line). This is properly discussed in Properly aligning drawn lines between the corners of 2 rectangular nodes in TikZ?.

A anchor is basically nothing else but two \saveddimen. Note that as I have used the both anchors subsequently in the calculation of all other anchors.

The node-placing on an arc only works with the current CVS version of TikZ.

Code

\documentclass[tikz,border=2pt]{standalone}
\tikzset{
  hdc/.style={draw,shape=half double crochet},
  crochet/.style={/crochet/.cd,#1},
}
\newcommand*{\crochetset}{\pgfqkeys{/crochet}}% use just like \tikzset{...}
\crochetset{
    height/.initial=2.5cm,
    width/.initial=1cm,
    %%% is there more?
}

\makeatletter
\pgfdeclareshape{half double crochet}{% hdc
  \savedanchor\northeast{%
    \pgfmathsetlength\pgf@x{\pgfkeysvalueof{/crochet/width}+2*\pgfkeysvalueof{/pgf/outer xsep}}%
    \pgf@x=.5\pgf@x
    \pgfmathsetlength\pgf@y{\pgfkeysvalueof{/crochet/height}+2*\pgfkeysvalueof{/pgf/outer ysep}}%
  }
  \savedanchor\innerwest{%
    \pgfmathsetlength\pgf@x{-\pgfkeysvalueof{/pgf/outer xsep}}%
    \pgfmathsetlength\pgf@y{\pgfkeysvalueof{/crochet/height}}%
  }
  \anchor{base}{\pgfpointorigin}%
  \anchor{south}{\pgfpointorigin}%
  \anchor{north}{\northeast\pgf@x\z@}%
  \anchor{west}{\innerwest\pgf@xa\pgf@x\northeast\pgf@y.5\pgf@y\pgf@x\pgf@xa}%
  \anchor{east}{\innerwest\pgf@xa-\pgf@x\northeast\pgf@y.5\pgf@y\pgf@x\pgf@xa}%
  \anchor{north east}{\northeast}%
  \anchor{north west}{\northeast\pgf@x-\pgf@x}%
  \anchor{south west}{\innerwest\pgf@y\z@}%
  \anchor{south east}{\innerwest\pgf@y\z@\pgf@x-\pgf@x}%
  \anchor{center}{\northeast\pgf@x\z@\pgf@y.5\pgf@y}%
  \anchor{bar east}{\innerwest\pgf@xa\pgf@x\northeast\advance\pgf@y\pgf@xa}%
  \anchor{bar west}{\innerwest\pgf@xa\pgf@x\northeast\advance\pgf@y\pgf@xa\pgf@x-\pgf@x}%
  \anchor{inner west}{\innerwest}%
  \anchor{inner east}{\innerwest\pgf@x-\pgf@x}%
  \anchor{bar south east}{\northeast\pgf@xa\pgf@x\innerwest\pgf@x\pgf@xa}
  \anchor{bar south west}{\northeast\pgf@xa-\pgf@x\innerwest\pgf@x\pgf@xa}

  \backgroundpath{% the path will only be needed the first time the shape is used,
                  % so we can use outer ysep without the need to save it.
    \northeast
    \pgf@xa=\pgf@x
    \pgfmathsetlength\pgf@ya{\pgf@y-\pgfkeysvalueof{/pgf/outer ysep}}%
    \pgfmathsetlength\pgf@xa{\pgf@x-\pgfkeysvalueof{/pgf/outer xsep}}%
    \pgfsetrectcap
    \pgfpathmoveto{\pgfqpoint{-\pgf@xa}{\pgf@ya}}%
    \pgfpathlineto{\pgfqpoint{ \pgf@xa}{\pgf@ya}}%
    \advance\pgf@ya-\pgflinewidth
    \pgfpathmoveto{\pgfqpoint{\z@}{\pgf@ya}}%
    \pgfmathsetlength\pgf@ya{\pgfkeysvalueof{/pgf/outer ysep}}%
    \pgfpathlineto{\pgfqpoint{\z@}{\pgf@ya}}%
  }
}
\makeatother

\begin{document}
\begin{tikzpicture}[line width=10pt,opacity=.5]
  \node[hdc] (a) at (0,0) {};
  \crochetset{width=2cm}
  \node[hdc,red] (b') at (5,0) {};
  \node[hdc, outer sep=10pt] (b) at (5,0) {};
  \foreach \anchor/\pos in {
    north east/right,
    north west/left,
    south west/left,
    south east/right,
    north/above,
    west/left,
    south/below,
    east/right,
    bar east/right,
    bar west/left,
    bar south east/right,
    bar south west/left,
    inner west/below left,
    inner east/below right,
%    base/below,%
    center/below%
  }{
    \fill[opacity=.7] (a.\anchor) circle (1pt) node[\pos,inner sep=-2pt,font=\tiny] {\anchor};
    \fill[opacity=.7] (b.\anchor) circle (1pt) node[\pos,inner sep=-2pt,font=\tiny] {\anchor};
  }
\end{tikzpicture}
\begin{tikzpicture}
\draw[gray] (0,0) arc[start angle=0, end angle=180, radius=1cm] \foreach \pos in {0,0.25,0.5,0.75,1} {node [sloped,hdc,black,pos=\pos,crochet={height=1cm,width=.25cm}] {}};
\end{tikzpicture}
\begin{tikzpicture}
\draw[gray] (0,0) arc[start angle=0, end angle=180, radius=1cm] \foreach \pos in {0,0.25,0.5,0.75,1} {node [sloped,hdc,black,anchor=south,pos=\pos,crochet={height=1cm,width=.25cm}] {}};
\end{tikzpicture}
\end{document}

Output

enter image description here

enter image description here

enter image description here

share|improve this answer
add comment

This is a long comment since I'll be trying to discourage you from that way doing things :) but the longer explanation can be found in Andrew Stacey's excellent answer in Usage of a saved dimension in another saved dimension in a \pgfdeclareshape command

I really don't know how or where to begin the explanation :) Let me first give a teaser. These declarations are first read but not executed. When everything is read, then TikZ starts slowly building up the required pieces by executing things in a very very carefully designed order.

This is the essence of the \saved<> macro family. Roughly, speaking, they harvest the code to be executed whenever needed, later on in the shape creation. They also collect the required length expressions which depend on the values of certain keys not at the time of code reading but at the time of node creation.

A concrete example is the regular anchor, let's turn to the manual (which is really amazing):

\anchor{<name>}{<code>}

This command declares an anchor named <name>. Unlike for saved anchors, the <code> will not be executed each time a node is declared. Rather, the <code> is only executed when the anchor is specically requested; either for anchoring the node during its creation or as a position in the shape referenced later on.

Now, we can see that some definitions are backed-up and stored for later execution, say, to save resources. Again, if everything was available at the instance of execution this wouldn't be possible.

Not my answer but my opinion is that this would be very very hackish to accomplish. Instead you can carry this method one step further. I don't have the expertise to fully explain the difference between a \definition and an expanded \edefinition but you can think of the former as the read but not executed yet type of definition.

So you define your code as follows

\def\my@widthcode{% This is the recycled code that will be used more than once.
    \pgfmathsetlength{\pgf@xa}{\pgfkeysvalueof{/pgf/minimum width}}
    \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/inner xsep}}
    \pgfmathsetlength{\pgf@xc}{\pgfkeysvalueof{/pgf/outer xsep}}
    \pgf@x=.5\pgf@xa
    \advance\pgf@x by .5\pgf@xb
    \advance\pgf@x by .5\pgf@xc
}

This will be read and will be available in any point of the shape declaration. Now we can use it

  \saveddimen{\my@width}{%
     \my@widthcode
  }

and also

  \savedanchor\base{%
    % Duplicated code, can't use \my@width here
    \my@widthcode
    \pgf@y=0pt
  }

So the overall code becomes,

\documentclass[a4paper]{article}
\usepackage{tikz}

\makeatletter
\def\my@widthcode{
    \pgf@xa=\pgfkeysvalueof{/pgf/minimum width}
    \pgf@xb=\pgfkeysvalueof{/pgf/inner xsep}
    \pgf@xc=\pgfkeysvalueof{/pgf/outer xsep}
    \pgf@x=.5\pgf@xa
    \advance\pgf@x by .5\pgf@xb
    \advance\pgf@x by .5\pgf@xc
}
\pgfdeclareshape{myshape}
{
  %
  % Saved dimensions
  %
  \saveddimen{\my@width}{%
     \my@widthcode
  }
  \saveddimen\my@height{%
    \pgf@x=\pgfkeysvalueof{/pgf/minimum height}%
  }
  %
  % Saved anchors
  %
  \savedanchor\base{%
    \my@widthcode
    \pgf@y=0pt
  }

  %
  % Anchors
  %
  \anchor{center}{%
    \base
    \pgfutil@tempdima=\my@height
    \advance\pgf@y by .5\pgfutil@tempdima
  }
  \anchor{south}{\base}

  %
  % Background path
  %
  \backgroundpath{%
    \pgfutil@tempdima=\my@width
    \pgfutil@tempdimb=\my@height
    \pgf@xa=.5\pgfutil@tempdima
    \pgf@ya=0pt
    \pgfpathmoveto{\pgfpoint{\pgf@xa}{\pgf@ya}}
    \advance\pgf@ya by \pgfutil@tempdimb
    \pgfpathlineto{\pgfpoint{\pgf@xa}{\pgf@ya}}
    \advance\pgf@xa by -.5\pgfutil@tempdima
    \pgfpathmoveto{\pgfpoint{\pgf@xa}{\pgf@ya}}
    \advance\pgf@xa by \pgfutil@tempdima
    \pgfpathlineto{\pgfpoint{\pgf@xa}{\pgf@ya}}
  }
}
\tikzset{
  every myshape node/.style={
    draw=black, thick, minimum width=1cm, minimum height=2.5cm,
  }
}
\makeatother

\begin{document}
\begin{tikzpicture}
  \node[myshape] at (0,0) {};
  \node[myshape, inner sep=10pt] at (3,0) {};
\end{tikzpicture}
\end{document}

enter image description here

share|improve this answer
    
Thanks for an incredibly patient answer. It never ceases to amaze me how different writing a document in Latex is from writing Latex. The latter is still light years beyond me but I don't give up trying ... –  ThomasH Mar 28 '13 at 21:47
    
The proper way do declare shapes in pgf in a nutshell so is "inside \saved..., declare everything from first principles (i.e. existing pgf commands) and only reuse any \saved... inside \anchor or \backgroundpath declarations? –  ThomasH Mar 28 '13 at 21:49
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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