4

I am trying to make some own PGF shape to use it later in TikZ. The shape shall be a square with no text inside, and it shall use a specified border style. Consider the following MWE:

\documentclass[border=1mm]{standalone}
\usepackage{tikz}

\makeatletter

\newlength\mylength
\tikzset{
length/.code={\setlength{\mylength}{#1}},
length = 8mm,
border style/.store in=\borderstyle,
border style = thick,
}

\pgfdeclareshape{myshape}{
    \inheritsavedanchors[from=rectangle]
    \inheritanchorborder[from=rectangle]
    \inheritanchor[from=rectangle]{center}
    \inheritanchor[from=rectangle]{north}
    \inheritanchor[from=rectangle]{south}
    \inheritanchor[from=rectangle]{west}
    \inheritanchor[from=rectangle]{east}
    % define additional drawing commands
    \backgroundpath{
        % coordinates on frame
        \pgf@xa=-0.5\mylength \pgf@ya=-0.5\mylength
        \pgf@xb=0.5\mylength \pgf@yb=0.5\mylength

        % where can I specify that this path needs to use the \borderstyle?
        \pgfpathmoveto{\pgfpoint{\pgf@xa}{\pgf@ya}}
        \pgfpathlineto{\pgfpoint{\pgf@xa}{\pgf@yb}}
        \pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@yb}}
        \pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@ya}}
        \pgfpathclose
    }
}
\makeatother

\begin{document}
\begin{tikzpicture}
\node[draw,shape=myshape, label=above:label] at (0,0) (some node) {};
\draw[<-] (some node) -- ++(0,-1);
\end{tikzpicture}
\end{document}

It indeed creates a square with the side length length, which is exactly what I want. But it seems like the south anchor is not really at the south of the square, but somewhere inside it. I guess this is because I don't have a text inside the shape. How can I move all anchors such that they are on the square? and further, how can I make the square use the border style I defined above? the label position is also wrong. It should be outside the square, since I don't want any text inside it.

wrong shape

  • Inherit means "Good luck finding the original definition!". – John Kormylo Oct 11 '18 at 14:31
  • Why do you need all this? Does \node[draw,minimum size=8mm, label=above:label] at (0,0) (some node) {}; give you what you want? – user121799 Oct 11 '18 at 14:50
  • @marmot, it kind of does, however, I will later add more things to my shape, so I think it is more convenient to do it with a shape. – T. Pluess Oct 11 '18 at 15:20
  • OK, I see. (I also assume you know of path picture, which also allows you to add things, as well as pics, which allow you to draw little shapes without text inside.) – user121799 Oct 11 '18 at 15:30
4

Here is an alternative without declaring a new shape. I add an additional element using path picture. As I do not know what you are going to add, I just chose a diagonal, but obviously you can add whatever you want. (I personally would not store the style in a macro, but use .../.style=... for that, yet I kept your proposal here.)

\documentclass[border=1mm]{standalone}
\usepackage{tikz}

\newlength\mylength
\tikzset{
length/.code={\setlength{\mylength}{#1}},
length = 8mm,
border style/.store in=\borderstyle,
border style = thick,
}
\tikzset{myshape/.style={draw,\borderstyle,
minimum size=\mylength,
path picture={
\draw (path picture bounding box.south west) -- 
(path picture bounding box.north east);
}}}


\begin{document}
\begin{tikzpicture}
\node[myshape, label=above:label] at (0,0) (some node){};
\draw[<-] (some node) -- ++(0,-1);
\end{tikzpicture}
\end{document}

enter image description here

  • thanks for that. I would like to give the shape declaration a try, since it seems to offer more flexibility than the path picture. I want to add special anchors later. – T. Pluess Oct 18 '18 at 7:39
3

Is this what you wanted?

demo

\documentclass[border=1mm]{standalone}
\usepackage{tikz}

\makeatletter

\newlength\mylength
\tikzset{
length/.code={\setlength{\mylength}{#1}},
length = 8mm,
border style/.store in=\borderstyle,
border style = thick,
}

\pgfdeclareshape{myshape}{
    \savedanchor{\southwest}{\pgfpoint{-0.5\mylength}{-0.5\mylength}}
    \savedanchor{\northeast}{\pgfpoint{0.5\mylength}{0.5\mylength}}
    \inheritanchorborder[from=rectangle]
    \inheritanchor[from=rectangle]{center}
    \inheritanchor[from=rectangle]{north}
    \inheritanchor[from=rectangle]{south}
    \inheritanchor[from=rectangle]{west}
    \inheritanchor[from=rectangle]{east}
    \anchor{text}   % this is used to center the text in the node
        {\pgfpoint{-.5\wd\pgfnodeparttextbox}{-.5\ht\pgfnodeparttextbox}}
    % define additional drawing commands
    \backgroundpath{
        \pgfkeys{/tikz/.cd,
                 \borderstyle}
        \pgfsetlinewidth{\pgflinewidth}
        % coordinates on frame
        \pgf@xa=-0.5\mylength \pgf@ya=-0.5\mylength
        \pgf@xb=0.5\mylength \pgf@yb=0.5\mylength

        % where can I specify that this path needs to use the \borderstyle?
        \pgfpathmoveto{\pgfpoint{\pgf@xa}{\pgf@ya}}
        \pgfpathlineto{\pgfpoint{\pgf@xa}{\pgf@yb}}
        \pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@yb}}
        \pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@ya}}
        \pgfpathclose
    }
}
\makeatother

\begin{document}
\begin{tikzpicture}
\node[draw,shape=myshape, label=above:label] at (0,0) (some node) {};
\draw[<-] (some node) -- ++(0,-1);
\end{tikzpicture}
\end{document}
  • it almost does; the shape border is stil not correct. Besides that, I thought I don't have a pgfnodeparttextbox because my shape won't have text inside? – T. Pluess Oct 11 '18 at 15:22
  • You have an empty {} text box. Translating thick into an actual line thickness was more obscure than i expected. – John Kormylo Oct 11 '18 at 21:48
  • this is exactly what I wanted. What is the {\pgfkeysvalueof{/tikz/line width}} used for? I removed it, and it seems to make no difference. – T. Pluess Oct 17 '18 at 7:52
  • Sorry, I wanted to print out some numbers as a test. I should have removed it from the final version. – John Kormylo Oct 17 '18 at 13:20
0

Ok, I am pretty close to it. I got the following, thanks to John Kormylo:

\documentclass[border=1cm]{standalone}
\usepackage{tikz}
\usetikzlibrary{shapes}

\begin{document}



 \tikzset{
    shape example/.style={
    color=black!30,
    draw,
    fill=yellow!30,
    line width=.5cm,
    inner xsep=2.5cm,
    inner ysep=0.5cm}
}


\makeatletter

\pgfdeclareshape{myrect}{
    \savedanchor{\southwest}{\pgfpoint{-6cm-0.5\pgflinewidth}{-6cm-0.5\pgflinewidth}}
    \savedanchor{\northeast}{\pgfpoint{6cm+0.5\pgflinewidth}{6cm + 0.5\pgflinewidth}}
    \inheritanchorborder[from=rectangle]


    \inheritanchor[from=rectangle]{north}
    \inheritanchor[from=rectangle]{east}
    \inheritanchor[from=rectangle]{south}
    \inheritanchor[from=rectangle]{west}
    \inheritanchor[from=rectangle]{center}

    \anchor{text}   % this is used to center the text in the node
        {\pgfpoint{-.5\wd\pgfnodeparttextbox}{-.5\ht\pgfnodeparttextbox}}

    \backgroundpath{
    % Rectangle box

    \pgfpathrectanglecorners{\pgfpointadd{\southwest}{\pgfpoint{0.5\pgflinewidth}{0.5\pgflinewidth}}}{\pgfpointadd{\northeast}{\pgfpoint{-0.5\pgflinewidth}{-0.5\pgflinewidth}}}
    \pgfusepath{draw, fill}

  }

}




\pgfdeclareshape{mytri}{
    \savedanchor{\southwest}{\pgfpoint{-6cm-0.5\pgflinewidth}{-6cm-0.5\pgflinewidth}}
    \savedanchor{\northeast}{\pgfpoint{6cm+0.5\pgflinewidth}{6cm + 0.5\pgflinewidth}}
    \inheritanchorborder[from=rectangle]


    \inheritanchor[from=rectangle]{north}
    \inheritanchor[from=rectangle]{east}
    \inheritanchor[from=rectangle]{south}
    \inheritanchor[from=rectangle]{west}
    \inheritanchor[from=rectangle]{center}

    \anchor{text}   % this is used to center the text in the node
        {\pgfpoint{-.5\wd\pgfnodeparttextbox}{-.5\ht\pgfnodeparttextbox}}

    \backgroundpath{
    % Rectangle box

    \pgfpathmoveto{\pgfpoint{-6cm}{6cm}}
    \pgfpathlineto{\pgfpoint{6cm}{0}}
    \pgfpathlineto{\pgfpoint{-6cm}{-6cm}}
    \pgfpathclose
    \pgfusepath{draw, fill}
  }
}
\makeatother

\Huge

\begin{tikzpicture}
  \node at (0,0)  [name=s,myrect, minimum width=12cm, minimum height=12cm, inner sep = 0pt, shape example, label=above:label]  {};
  \node at (30,0)  [name=s2,mytri, minimum width=12cm, minimum height=12cm, inner sep = 0pt, shape example, label=above:label]  {};

  \draw (-6cm,-6cm) -- (6cm,-6cm);

  \foreach \x/\p in {north/above, east/right, south/below, west/left}
  \draw[shift=(s.\x)] plot[mark=x] coordinates{(0,0)} node[\p] {\color{red}\scriptsize\texttt{(s.\x)}};

  \draw[thin, red] (s2.west) |- (s2.north) -| (s2.east) |- (s2.south) -| cycle;
  \draw[thin, red] (s.west) |- (s.north) -| (s.east) |- (s.south) -| cycle;

  \foreach \x/\p in {north/above, east/right, south/below, west/left}
  \draw[shift=(s2.\x)] plot[mark=x] coordinates{(0,0)} node[\p] {\color{red}\scriptsize\texttt{(s2.\x)}};

\draw[>=latex, ->] (s) -- (s2);
\end{tikzpicture}

\end{document}

For the rectangle, the anchors seem now to be correctly aligned (i.e. the anchors are actually on the outer side of the lines, as for the normal rectangle in TikZ), which becomes obvious if one uses a ridiculously large line thickness. To achieve that, the \pgflinewidth has to be taken into account for the coordinates of the line endpoints.

enter image description here

I wonder whether there is a mechanism, which can automatically correct for the line thickness? for instance, if the triangle is considered, it becomes obvious that the correction of the path points becomes more cumbersome, it would be wrong to simply subtract the \pgflinewidth.

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.