1

Based on similar questions I found on SE, I have finally managed to create a relatively simple ‘folded document’ shape in tikz with a few horizontal lines inside:

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

\usepackage{multido}
\usetikzlibrary{positioning}

\newcommand\Repeat{\multido{}}

\pgfkeys{/tikz/folddist/.initial=10pt}
\pgfkeys{/tikz/linedist/.initial=5pt}
\makeatletter
\pgfdeclareshape{document}{%
    \inheritsavedanchors[from=rectangle]
    \inheritanchorborder[from=rectangle]
    \inheritanchor[from=rectangle]{center}
    \inheritanchor[from=rectangle]{north}
    \inheritanchor[from=rectangle]{east}
    \inheritanchor[from=rectangle]{south}
    \inheritanchor[from=rectangle]{west}
    \backgroundpath{%
        \southwest \pgf@xa=\pgf@x \pgf@ya=\pgf@y
        \northeast \pgf@xb=\pgf@x \pgf@yb=\pgf@y
        % main shape
        \pgf@xc=\pgf@xb
            \advance\pgf@xc by-\pgfkeysvalueof{/tikz/folddist}
        \pgf@yc=\pgf@yb
            \advance\pgf@yc by-\pgfkeysvalueof{/tikz/folddist}
        \pgfpathmoveto{\pgfpoint{\pgf@xa}{\pgf@ya}}
        \pgfpathlineto{\pgfpoint{\pgf@xa}{\pgf@yb}}
        \pgfpathlineto{\pgfpoint{\pgf@xc}{\pgf@yb}}
        \pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@yc}}
        \pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@ya}}
        \pgfpathclose
        % fold
        \pgfpathmoveto{\pgfpoint{\pgf@xb}{\pgf@yc}}
        \pgfpathlineto{\pgfpoint{\pgf@xc}{\pgf@yc}}
        \pgfpathlineto{\pgfpoint{\pgf@xc}{\pgf@yb}}
        % lines
        \advance\pgf@xa by+\pgfkeysvalueof{/tikz/linedist}
        \advance\pgf@xb by-\pgfkeysvalueof{/tikz/linedist}
        \pgfmathsetlength\pgf@yc{(\pgf@yb-\pgf@ya)/8}
        \pgf@ya=\pgf@yb
        \advance\pgf@ya by-\pgfkeysvalueof{/tikz/folddist}
        \Repeat{5}{%
            \advance\pgf@ya by-\pgf@yc
            \pgfpathmoveto{\pgfpoint{\pgf@xa}{\pgf@ya}}
            \pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@ya}}
        }
    }
}
\makeatother


\begin{document}

\begin{tikzpicture}[%
    doc/.style={%
        draw,shape=document,
        minimum width=1cm,minimum height=1.5cm
    }
]
    \node[doc] {};
\end{tikzpicture}

\end{document}

code output

Now I would like to improve it further for my needs: I would want to be able to use some kind of parameter (a PGF key?) that would make the horizontal lines dashed. I tried putting \pgfsetdash{{6pt}{1pt}}{0pt} before the \Repeat loop, but it seems that this setting applies to all the paths drawn by this shape.

My question is threefold:

  1. Should I even be defining my own shape for something like this? I plan to use this shape as a node in a larger diagram, a chain, multiple times: with and without the dashed lines, that is why I need the parameterization
  2. How can I make only the paths of the horizontal lines dashed?
  3. How can I create a parameter that enables/disables said line dashing?

I would also be grateful for any other insight/simplification to my code as it does seem a little weird to me.

1 Answer 1

3

As for your questions:

  1. Defining your own shapes can make a lot of sense, in particular if they should come with new anchors. Alternatives include the use of path pictures or append after command but this does not always work nicely if you, say, rotate the shape.
  2. You have to have two paths. I put the lines into \beforebackgroundpath{...}. Then you can stroke and dash those lines.
  3. One way to optionally turn off and on the dashing is to use the /.is if key handler. Other options include setting the dash pattern in a key.

Here is the code:

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

\usetikzlibrary{positioning}

\pgfkeys{/tikz/folddist/.initial=10pt}
\pgfkeys{/tikz/linedist/.initial=5pt}
\makeatletter
\newif\iftikz@if@folderlines@dashed
\tikzset{folder/.cd,lines dashed/.is if=tikz@if@folderlines@dashed,
    lines dashed/.default=true,
    lines dashed=false}
\pgfdeclareshape{document}{%
    \inheritsavedanchors[from=rectangle]
    \inheritanchorborder[from=rectangle]
    \inheritanchor[from=rectangle]{center}
    \inheritanchor[from=rectangle]{north}
    \inheritanchor[from=rectangle]{east}
    \inheritanchor[from=rectangle]{south}
    \inheritanchor[from=rectangle]{west}
    \backgroundpath{%
        \southwest \pgf@xa=\pgf@x \pgf@ya=\pgf@y
        \northeast \pgf@xb=\pgf@x \pgf@yb=\pgf@y
        % main shape
        \pgf@xc=\pgf@xb
            \advance\pgf@xc by-\pgfkeysvalueof{/tikz/folddist}
        \pgf@yc=\pgf@yb
            \advance\pgf@yc by-\pgfkeysvalueof{/tikz/folddist}
        \pgfpathmoveto{\pgfpoint{\pgf@xa}{\pgf@ya}}
        \pgfpathlineto{\pgfpoint{\pgf@xa}{\pgf@yb}}
        \pgfpathlineto{\pgfpoint{\pgf@xc}{\pgf@yb}}
        \pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@yc}}
        \pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@ya}}
        \pgfpathclose
        % fold
        \pgfpathmoveto{\pgfpoint{\pgf@xb}{\pgf@yc}}
        \pgfpathlineto{\pgfpoint{\pgf@xc}{\pgf@yc}}
        \pgfpathlineto{\pgfpoint{\pgf@xc}{\pgf@yb}}
        % lines
    }
    \beforebackgroundpath{%
        \southwest \pgf@xa=\pgf@x \pgf@ya=\pgf@y
        \northeast \pgf@xb=\pgf@x \pgf@yb=\pgf@y
        % main shape
        \pgf@xc=\pgf@xb
            \advance\pgf@xc by-\pgfkeysvalueof{/tikz/folddist}
        \pgf@yc=\pgf@yb
            \advance\pgf@yc by-\pgfkeysvalueof{/tikz/folddist}
        \advance\pgf@xa by+\pgfkeysvalueof{/tikz/linedist}
        \advance\pgf@xb by-\pgfkeysvalueof{/tikz/linedist}
        \pgfmathsetlength\pgf@yc{(\pgf@yb-\pgf@ya)/8}
        \pgf@ya=\pgf@yb
        \advance\pgf@ya by-\pgfkeysvalueof{/tikz/folddist}
        \iftikz@if@folderlines@dashed
        \pgfsetdash{{5pt}{2pt}}{0pt}%
        \fi 
        \c@pgf@counta0\relax
        \pgfutil@loop
            \advance\pgf@ya by-\pgf@yc
            \advance\c@pgf@counta by1\relax
            \pgfpathmoveto{\pgfpoint{\pgf@xa}{\pgf@ya}}
            \pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@ya}}
        \ifnum\c@pgf@counta<5\pgfutil@repeat%
        \pgfusepath{stroke}
    }
}
\makeatother


\begin{document}

\begin{tikzpicture}[%
    doc/.style={%
        draw,shape=document,
        minimum width=1cm,minimum height=1.5cm
    }
]
    \node[doc,fill=red] {};
    \node[doc,folder/lines dashed=true] at (2,0){};
    \node[doc,fill=blue,folder/lines dashed=true] at (4,0){};
\end{tikzpicture}

\end{document}

enter image description here

2
  • This is amazing, thank you! My only question is what is folder and whether I have to use tikzset instead of pgfkeys. It seems to me that they do similar things, shouldn’t I use one or the other? Also, thank you for ridding me of the unnecessary multido dependency.
    – bp99
    Nov 30, 2020 at 15:45
  • 1
    @bp99 This folder is, ironically, a pgf folder. That is, if you declare tons of shapes, you run out of name space, so it makes sense to store these keys in subfolders. I called it folder but this is more like a joke, you can call it whatever you find appropriate.
    – user229669
    Nov 30, 2020 at 15:48

You must log in to answer this question.

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