Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide a jigsaw puzzle decoration #1357

Open
gerion0 opened this issue Sep 4, 2024 · 2 comments
Open

Provide a jigsaw puzzle decoration #1357

gerion0 opened this issue Sep 4, 2024 · 2 comments

Comments

@gerion0
Copy link

gerion0 commented Sep 4, 2024

Brief outline of the proposed feature

For a picture, I needed a jigsaw puzzle decoration. I searched the internet but only found a package for complete puzzle pieces and some stackoverflow posts.

Therefore, I created such a decoration by myself based on the jigsaw package (and the brace decoration). Maybe you want to include this decoration directly into TikZ. There are, however, a few things left, so I hand it in as feature request and not as a PR:

  • My TeX/PGF skills on such a low level are not good. Probably I'm doing things more complicated than needed. For example, I did not get \pgf@xa etc. to work (TeX reports always that it is an unknown command).
  • The jigsaw puzzle pieces can be parametrized in several ways. Currently, I use only one aspect-argument for a generic scale. However, it is possible to scale height and width seperately of each other. Maybe, it is also meaningful to respect amplitude but the default of 2.5pt looks really bad for that decoration.

Usage example

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations}

% jigsaw puzzle decorations
%
% Parameters: \pgfdecorationegmentaspect

\pgfdeclaredecoration{jigsaw}{jigsaw}
{%
  \state{jigsaw}[width=+\pgfdecoratedremainingdistance,next state=final]
  {
    \pgfmathparse{.26*2*\pgfdecorationsegmentaspect * \pgfdecoratedremainingdistance}
    \edef\pgf@lib@dec@height{\pgfmathresult}
    \pgfmathparse{(.04/.26)*\pgf@lib@dec@height}
    \edef\ya{\pgfmathresult}
    \pgfmathparse{(.11/.26)*\pgf@lib@dec@height}
    \edef\yb{\pgfmathresult}
    \pgfmathparse{(.5-(.1*2*\pgfdecorationsegmentaspect))*\pgfdecoratedremainingdistance}
    \edef\xa{\pgfmathresult}
    \pgfmathparse{(.5+(.1*2*\pgfdecorationsegmentaspect))*\pgfdecoratedremainingdistance}
    \edef\xb{\pgfmathresult}
    \pgfmathparse{(.5-(.3*2*\pgfdecorationsegmentaspect))*\pgfdecoratedremainingdistance}
    \edef\xc{\pgfmathresult}
    \pgfmathparse{(.5+(.3*2*\pgfdecorationsegmentaspect))*\pgfdecoratedremainingdistance}
    \edef\xd{\pgfmathresult}
    %
    \pgfpathcurveto
    {\pgfqpoint{0pt}{0pt}}
    {\pgfpoint{\xa}{-\ya}}
    {\pgfpoint{\xa}{\ya}}
    %
    \pgfpathcurveto
    {\pgfpoint{\xa}{\yb}}
    {\pgfpoint{\xc}{\pgf@lib@dec@height}}
    {\pgfpoint{.5\pgfdecoratedremainingdistance}{\pgf@lib@dec@height}}
    %
    \pgfpathcurveto
    {\pgfpoint{\xd}{\pgf@lib@dec@height}}
    {\pgfpoint{\xb}{\yb}}
    {\pgfpoint{\xb}{\ya}}
    %
    \pgfpathcurveto
    {\pgfpoint{\xb}{-\ya}}
    {\pgfpoint{\pgfdecoratedremainingdistance}{0}}
    {\pgfpoint{\pgfdecoratedremainingdistance}{0}}
  }

  \state{final}
  {}%
}%

\begin{document}
\begin{tikzpicture};
	\draw[fill=red!20] (0,0)
		decorate[decoration=jigsaw] { -- (0,1)}
		decorate[decoration={jigsaw, mirror}] { -- (1,1)}
		decorate[decoration={jigsaw, mirror}] { -- (1,0)}
		decorate[decoration=jigsaw] { -- cycle};
\end{tikzpicture}
\end{document}

This gives this picture:
mwe

@Qrrbrbirlbel
Copy link
Contributor

If you want to use control sequence with @ in their name, you will need \makeatletter/\makeatother, i.e. set the catcode of @ to letter. (A PGF/TikZ library is executed in an environment where that is the case.)
So, you're not actually defining a macro with the name \pgf@lib@dec@height but a macro with the name \pgf which always expects @lib@dec@height to be followed.

Either way, here is the decoration with those hard-coded numbers in TeX-math (so, without PGFMath on top).

I've added a \pgfpathclose so that the piece is properly closed.

A part from adding more customization, it would be nice to just provide a list of piece-wise customization so that a full path can be decorated with just one decoration. (This would help to decorate nodes since you can't access the separate path segments.)

I don't see a built-in decoration that uses this but I have some ideas …

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations}

% jigsaw puzzle decorations
%
% Parameters: \pgfdecorationegmentaspect
\makeatletter
\pgfdeclaredecoration{jigsaw}{jigsaw}{%
  \state{jigsaw}[
    width=+\pgfdecoratedremainingdistance,
    next state=final,
    if input segment is closepath={next state=close}
  ]{%
    \pgf@yd=\pgfdecorationsegmentaspect\pgfdecoratedremainingdistance
    \pgf@yd=2\pgf@yd   %       2 * aspect * remaining dist
    \pgf@yc=.26\pgf@yd % .26 * 2 * aspect * remaining dist
    \pgf@ya=.04\pgf@yd % .04 * 2 * aspect * remaining dist
    \pgf@yb=.11\pgf@yd % .11 * 2 * aspect * remaining dist
    %
    \pgf@xd=\pgfdecorationsegmentaspect pt
    \pgf@xa=-.2\pgf@xd      %    - .2*aspect
    \advance\pgf@xa by .5pt % .5 - .2*aspect
    \pgf@xa=\pgf@sys@tonumber\pgf@xa\pgfdecoratedremainingdistance
    \pgf@xb=.2\pgf@xd       %      .2*aspect
    \advance\pgf@xb by .5pt % .5 + .2*aspect
    \pgf@xb=\pgf@sys@tonumber\pgf@xb\pgfdecoratedremainingdistance
    \pgf@xc=-.6\pgf@xd      %    - .6*aspect
    \advance\pgf@xc by .5pt % .5 - .6*aspect
    \pgf@xc=\pgf@sys@tonumber\pgf@xc\pgfdecoratedremainingdistance
    \pgf@xd=.6\pgf@xd       %      .6*aspect
    \advance\pgf@xd by .5pt % .5 + .6*aspect
    \pgf@xd=\pgf@sys@tonumber\pgf@xd\pgfdecoratedremainingdistance
    %
    \edef\pgf@marshal{%
      \noexpand\pgfpathcurveto
        {\noexpand\pgfqpoint{0pt}{0pt}}
        {\noexpand\pgfqpoint{\the\pgf@xa}{-\the\pgf@ya}}
        {\noexpand\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}%
      \noexpand\pgfpathcurveto
        {\noexpand\pgfqpoint{\the\pgf@xa}{\the\pgf@yb}}
        {\noexpand\pgfqpoint{\the\pgf@xc}{\the\pgf@yc}}
        {\noexpand\pgfqpoint{.5\pgfdecoratedremainingdistance}{\the\pgf@yc}}%
      \noexpand\pgfpathcurveto
        {\noexpand\pgfqpoint{\the\pgf@xd}{\the\pgf@yc}}
        {\noexpand\pgfqpoint{\the\pgf@xb}{\the\pgf@yb}}
        {\noexpand\pgfqpoint{\the\pgf@xb}{\the\pgf@ya}}%
      \noexpand\pgfpathcurveto
        {\noexpand\pgfqpoint{\the\pgf@xb}{-\the\pgf@ya}}
        {\noexpand\pgfpointdecoratedinputsegmentlast}
        {\noexpand\pgfpointdecoratedinputsegmentlast}}%
    \pgf@marshal
  }
  \state{close}[next state=final]{\pgfpathclose}%
  \state{final}{}%
}%
\makeatother
\begin{document}
\tikz[x=3cm, y=3cm, line width=2mm]
  \draw[fill=red!20] (0,0)
    decorate[decoration=jigsaw] { -- (0,1)}
    decorate[decoration={jigsaw, mirror}] { -- (1,1)}
    decorate[decoration={jigsaw, mirror}] { -- (1,0)}
    decorate[decoration=jigsaw] { -- cycle};
\end{document}

@gerion0
Copy link
Author

gerion0 commented Sep 15, 2024

If you want to use control sequence with @ in their name, you will need \makeatletter/\makeatother, i.e. set the catcode of @ to letter. (A PGF/TikZ library is executed in an environment where that is the case.) So, you're not actually defining a macro with the name \pgf@lib@dec@height but a macro with the name \pgf which always expects @lib@dec@height to be followed.

I experienced that error. It makes sense now, thanks.

Either way, here is the decoration with those hard-coded numbers in TeX-math (so, without PGFMath on top).

I've added a \pgfpathclose so that the piece is properly closed.

Thank you! This looks way cleaner.

Some words about parametrization:
Essentially, the decoration has two meaningful parameters:

  1. the height of the "puzzle nose"
  2. the width of the "puzzle nose"

With the current code, the height is set to $0.26 \cdot \text{length}$ (length is the length of the decorated path, \pgf@yd in your code). The width is not directly visible in the code, but also depends on the length (given by \pgf@xa, \pgf@xb, \pgf@xc and \pgf@xd).

My first attempt was this parametrization:

  • The height is set to the decoration's amplitude.
  • The width is dependent on the decoration's aspect.

Advantages: The argument effects fit to their description. Both, height and width can be scaled.
Disadvantages: The amplitude has a default value of 2.5 pt which is way to small for most lengths. The amplitude value does not depend on the length while most users probably expects such a dependency.

So my second attempt is the posted one:

  • Height and width are just controlled by the decoration's aspect. Height is set per default to $0.26 \cdot \text{length}$.

Advantages: Without arguments, the decoration looks as intended. The aspect argument can be used the control the size of the "puzzle nose".
Disadvantages: The aspect argument does not fit to its meaning. It doesn't control an aspect but the whole size (relative to length). It is not possible to control height and width seperately.

Without the argument presets in PGF, I would give the decoration two arguments: relative size and aspect. The relative size would have a default of 0.26 and would denote the height of the puzzle nose relative to the decoration length. aspect would control the aspect between width and height of the decoration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

3 participants