2

I am trying to create a custom shape in Tikz in a way that would allow me to use the node's options to determine how such a shape should be drawn.

With the example Rectangle node with diagonal fill in mind, as a first attempt I tried to implement a simple rectangle myshape having a single custom anchor input, which shall be drawn either on its left side (option input west) or its right side (option input east). Of course, I know that Tikz already provides me ways to obtain that, but this was only meant to be a simple experiment before creating something more complex.

However, my mwe below (which also exploits some code taken from Example: D flip-flops and shift register) fails in computing the anchor based on the provided option. In fact, it behaves as though the option was not defined, i. e. the shape is always compiled according to the \else argument of my \if...\else...\fi construct, which results in the input anchor being drawn always on the right side of the rectangle in the example below.

\documentclass{beamer}
\usepackage{tikz}
\usetikzlibrary{positioning,}

\makeatletter

\newif\ifpgf@input@west

% Use these with PGF
\def\pgfsetinputwest{\pgf@input@westtrue}%
\def\pgfsetinputeast{\pgf@input@westfalse}%

% Use these with TikZ
\tikzoption{input west}[]{\pgfsetinputwest}
\tikzoption{input east}[]{\pgfsetinputeast}

% Custom shape (myshape)
\pgfdeclareshape{myshape}{
  % The 'minimum width' and 'minimum height' keys, not the content, determine the size
  \savedanchor\northeast{%
    \pgfmathsetlength\pgf@x{\pgfshapeminwidth}%
    \pgfmathsetlength\pgf@y{\pgfshapeminheight}%
    \pgf@x=0.5\pgf@x
    \pgf@y=0.5\pgf@y
  }
  % This is redundant, but makes some things easier:
  \savedanchor\southwest{%
    \pgfmathsetlength\pgf@x{\pgfshapeminwidth}%
    \pgfmathsetlength\pgf@y{\pgfshapeminheight}%
    \pgf@x=-0.5\pgf@x
    \pgf@y=-0.5\pgf@y
  }
  % Inherit from rectangle
  \inheritanchorborder[from=rectangle]

  % Define same anchor a normal rectangle has
  \anchor{center}{\pgfpointorigin}
  \anchor{north}{\northeast \pgf@x=0pt}
  \anchor{east}{\northeast \pgf@y=0pt}
  \anchor{south}{\southwest \pgf@x=0pt}
  \anchor{west}{\southwest \pgf@y=0pt}
  \anchor{north east}{\northeast}
  \anchor{north west}{\northeast \pgf@x=-\pgf@x}
  \anchor{south west}{\southwest}
  \anchor{south east}{\southwest \pgf@x=-\pgf@x}
  \anchor{text}{
    \pgfpointorigin
    \advance\pgf@x by -.5\wd\pgfnodeparttextbox%
    \advance\pgf@y by -.5\ht\pgfnodeparttextbox%
    \advance\pgf@y by +.5\dp\pgfnodeparttextbox%
  }

  % Define anchors for input
    \ifpgf@input@west
        \anchor{input}{
            \pgf@process{\northeast}%
            \pgf@x=-1\pgf@x%
            \pgf@y=0pt%
        }
    \else
        \anchor{input}{
            \pgf@process{\northeast}%
            \pgf@x=1\pgf@x%
            \pgf@y=0pt%
        }
    \fi

  % Draw the rectangle box and the port labels
  \backgroundpath{
    % Rectangle box
    \pgfpathrectanglecorners{\southwest}{\northeast}
  }
}


% Key to add font macros to the current font
\tikzset{add font/.code={\expandafter\def\expandafter\tikz@textfont\expandafter{\tikz@textfont#1}}} 

% Define default style for this node
\tikzset{every myshape node/.style={draw,minimum width=0.4cm,minimum 
height=0.4cm,inner sep=1mm,outer sep=0pt,cap=round,add 
font=\sffamily}}

\makeatother


\begin{document}

\begin{frame}{MWE}
\begin{figure}
\centering
\begin{tikzpicture}[auto, node distance=4mm and 4mm]%
    \node (start) at (0,0) {};
    \node [myshape, draw, input west] (myshape_w) [right=of start] {};
    \node [myshape, draw, input east] (myshape_e) [below=of myshape_w] {};
    \draw [-latex] (start.center) -- (myshape_w.input);
    \draw [-latex] (start.center) |- (myshape_e.input);
\end{tikzpicture}
\end{figure}

\end{frame}

\end{document}

Result of mwe

I can't quite understand why my mwe fails, I only know that it starts diverging from my expectations as soon as I introduce the \if...\else...\fi, so I'm afraid I'm lacking some basic knowledge on how this low-level Tex coding works. I would appreciate any help pointing me towards the right direction.

0
1

There are two problems. The anchor gets evaluated at a time when you did not yet decide whether it should be west or east, and also the \if needs to be made "known" to the shape. So basically you need \deferredanchor (see page 1129 of pgfmanual v3.1.4) and \savedmacro (see page 1128). I also find it more intuitive to change a simple number instead of using an \if.

\documentclass{beamer}
\usepackage{tikz}
\usetikzlibrary{positioning}

\makeatletter

% Use these with TikZ
\def\my@side{1}
\tikzset{input west/.code=\def\my@internal@side{-1}}
\tikzset{input east/.code=\def\my@internal@side{1}}

% Custom shape (myshape)
\pgfdeclareshape{myshape}{
  % The 'minimum width' and 'minimum height' keys, not the content, determine the size
  \savedanchor\northeast{%
    \pgfmathsetlength\pgf@x{\pgfshapeminwidth}%
    \pgfmathsetlength\pgf@y{\pgfshapeminheight}%
    \pgf@x=0.5\pgf@x
    \pgf@y=0.5\pgf@y
  }
  % This is redundant, but makes some things easier:
  \savedanchor\southwest{%
    \pgfmathsetlength\pgf@x{\pgfshapeminwidth}%
    \pgfmathsetlength\pgf@y{\pgfshapeminheight}%
    \pgf@x=-0.5\pgf@x
    \pgf@y=-0.5\pgf@y
  }
  % Inherit from rectangle
  \inheritanchorborder[from=rectangle]

  % Define same anchor a normal rectangle has
  \anchor{center}{\pgfpointorigin}
  \anchor{north}{\northeast \pgf@x=0pt}
  \anchor{east}{\northeast \pgf@y=0pt}
  \anchor{south}{\southwest \pgf@x=0pt}
  \anchor{west}{\southwest \pgf@y=0pt}
  \anchor{north east}{\northeast}
  \anchor{north west}{\northeast \pgf@x=-\pgf@x}
  \anchor{south west}{\southwest}
  \anchor{south east}{\southwest \pgf@x=-\pgf@x}
  \anchor{text}{
    \pgfpointorigin
    \advance\pgf@x by -.5\wd\pgfnodeparttextbox%
    \advance\pgf@y by -.5\ht\pgfnodeparttextbox%
    \advance\pgf@y by +.5\dp\pgfnodeparttextbox%
  }
  \savedmacro{\my@side}{\let\my@side\my@internal@side}
  % Define anchors for input
    \deferredanchor{input}{
           \pgf@process{\northeast}%
            \pgf@x=\my@side\pgf@x%
            \pgf@y=0pt%
        }

  % Draw the rectangle box and the port labels
  \backgroundpath{
    % Rectangle box
    \pgfpathrectanglecorners{\southwest}{\northeast}
  }
}


% Key to add font macros to the current font
\tikzset{add font/.code={\expandafter\def\expandafter\tikz@textfont\expandafter{\tikz@textfont#1}}} 

% Define default style for this node
\tikzset{every myshape node/.style={draw,minimum width=0.4cm,minimum 
height=0.4cm,inner sep=1mm,outer sep=0pt,cap=round,add 
font=\sffamily}}

\makeatother


\begin{document}

\begin{frame}{MWE}
\begin{figure}
\centering
\begin{tikzpicture}[auto, node distance=4mm and 4mm,line cap=rect]%
    \coordinate (start) at (0,0);
    \node [input west,myshape, draw] (myshape_w) [right=of start] {};
    \node [input east,myshape, draw] (myshape_e) [below=of myshape_w] {};
    \draw [-latex] (start) -- (myshape_w.input);
    \draw [-latex] (start) |- (myshape_e.input);
\end{tikzpicture}
\end{figure}

\end{frame}

\end{document}

I'd also like to draw your attention to the /.is choice key, which allows one to make things arguably more elegant here.

\documentclass{beamer}
\usepackage{tikz}
\usetikzlibrary{positioning}

\makeatletter

% Use these with TikZ
\def\my@side{1}
\tikzset{input/.is choice,
input/west/.code=\def\my@internal@side{-1},
input/east/.code=\def\my@internal@side{1},
input=east}

% Custom shape (myshape)
\pgfdeclareshape{myshape}{
  % The 'minimum width' and 'minimum height' keys, not the content, determine the size
  \savedanchor\northeast{%
    \pgfmathsetlength\pgf@x{\pgfshapeminwidth}%
    \pgfmathsetlength\pgf@y{\pgfshapeminheight}%
    \pgf@x=0.5\pgf@x
    \pgf@y=0.5\pgf@y
  }
  % This is redundant, but makes some things easier:
  \savedanchor\southwest{%
    \pgfmathsetlength\pgf@x{\pgfshapeminwidth}%
    \pgfmathsetlength\pgf@y{\pgfshapeminheight}%
    \pgf@x=-0.5\pgf@x
    \pgf@y=-0.5\pgf@y
  }
  % Inherit from rectangle
  \inheritanchorborder[from=rectangle]

  % Define same anchor a normal rectangle has
  \anchor{center}{\pgfpointorigin}
  \anchor{north}{\northeast \pgf@x=0pt}
  \anchor{east}{\northeast \pgf@y=0pt}
  \anchor{south}{\southwest \pgf@x=0pt}
  \anchor{west}{\southwest \pgf@y=0pt}
  \anchor{north east}{\northeast}
  \anchor{north west}{\northeast \pgf@x=-\pgf@x}
  \anchor{south west}{\southwest}
  \anchor{south east}{\southwest \pgf@x=-\pgf@x}
  \anchor{text}{
    \pgfpointorigin
    \advance\pgf@x by -.5\wd\pgfnodeparttextbox%
    \advance\pgf@y by -.5\ht\pgfnodeparttextbox%
    \advance\pgf@y by +.5\dp\pgfnodeparttextbox%
  }
  \savedmacro{\my@side}{\let\my@side\my@internal@side}
  % Define anchors for input
    \deferredanchor{input}{
           \pgf@process{\northeast}%
            \pgf@x=\my@side\pgf@x%
            \pgf@y=0pt%
        }

  % Draw the rectangle box and the port labels
  \backgroundpath{
    % Rectangle box
    \pgfpathrectanglecorners{\southwest}{\northeast}
  }
}


% Key to add font macros to the current font
\tikzset{add font/.code={\expandafter\def\expandafter\tikz@textfont\expandafter{\tikz@textfont#1}}} 

% Define default style for this node
\tikzset{every myshape node/.style={draw,minimum width=0.4cm,minimum 
height=0.4cm,inner sep=1mm,outer sep=0pt,cap=round,add 
font=\sffamily}}

\makeatother


\begin{document}

\begin{frame}{MWE}
\begin{figure}
\centering
\begin{tikzpicture}[auto, node distance=4mm and 4mm,line cap=rect]%
    \coordinate (start) at (0,0);
    \node [input=west,myshape, draw] (myshape_w) [right=of start] {};
    \node [input=east,myshape, draw] (myshape_e) [below=of myshape_w] {};
    \draw [-latex] (start) -- (myshape_w.input);
    \draw [-latex] (start) |- (myshape_e.input);
\end{tikzpicture}
\end{figure}

\end{frame}

\end{document}

enter image description here

2
  • Thank you for the answer. Sorry for the gap at the start node, I had corrected it before uploading the picture but forgot to update the code. Now I edited it and should compile just fine in the image I've shown in the question. – DavideM Nov 1 '19 at 16:19
  • 1
    @DavideM No problem! It might be easier to use \coordinate (start) at (0,0);, though. – user194703 Nov 1 '19 at 16:21

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.