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

4
  • Inherit means "Good luck finding the original definition!". Oct 11, 2018 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, 2018 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, 2018 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, 2018 at 15:30

3 Answers 3

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

1
  • 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, 2018 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}
4
  • 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, 2018 at 15:22
  • You have an empty {} text box. Translating thick into an actual line thickness was more obscure than i expected. Oct 11, 2018 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, 2018 at 7:52
  • Sorry, I wanted to print out some numbers as a test. I should have removed it from the final version. Oct 17, 2018 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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .