summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2023-03-27 20:09:45 +0000
committerKarl Berry <karl@freefriends.org>2023-03-27 20:09:45 +0000
commit13355b5a933418004b7779dd1eab79adf5dc565c (patch)
treed998b6b9e95a34543dc90ddc78fb12aa4f2e0470
parent833bcce1ec44296981a00f8b2f9c1a5d7bba8a64 (diff)
tikz-nfold (27mar23)
git-svn-id: svn://tug.org/texlive/trunk@66684 c570f23f-e606-0410-a88d-b1316a301751
-rw-r--r--Master/texmf-dist/doc/latex/tikz-nfold/README.md12
-rw-r--r--Master/texmf-dist/doc/latex/tikz-nfold/tikz-nfold-doc.pdfbin0 -> 289815 bytes
-rw-r--r--Master/texmf-dist/doc/latex/tikz-nfold/tikz-nfold-doc.tex486
-rw-r--r--Master/texmf-dist/tex/latex/tikz-nfold/pgflibrarybezieroffset.code.tex351
-rw-r--r--Master/texmf-dist/tex/latex/tikz-nfold/tikzlibrarynfold.code.tex785
-rwxr-xr-xMaster/tlpkg/bin/tlpkg-ctan-check2
-rw-r--r--Master/tlpkg/tlpsrc/collection-pictures.tlpsrc1
-rw-r--r--Master/tlpkg/tlpsrc/tikz-nfold.tlpsrc0
8 files changed, 1636 insertions, 1 deletions
diff --git a/Master/texmf-dist/doc/latex/tikz-nfold/README.md b/Master/texmf-dist/doc/latex/tikz-nfold/README.md
new file mode 100644
index 00000000000..305b8edecb7
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/tikz-nfold/README.md
@@ -0,0 +1,12 @@
+# tikz-nfold
+## Version 0.0.1
+
+This library adds higher-order paths to [TikZ](https://ctan.org/pkg/pgf) and also fixes some graphical issues with TikZ' `double` paths, used e.g. in wide arrows. It is also compatible with [tikz-cd](https://ctan.org/pkg/tikz-cd), adding support for triple and higher arrows. See the [documentation](tikz-nfold-doc.pdf) for full details.
+
+## License
+
+This work may be distributed and/or modified under the conditions of the LaTeX Project Public License, either version 1.3c of this license or (at your option) any later version.
+
+## Author
+
+This package is maintained by Jonathan Schulz. The repository and bug tracker can be found at https://github.com/jonschz/tikz-nfold.
diff --git a/Master/texmf-dist/doc/latex/tikz-nfold/tikz-nfold-doc.pdf b/Master/texmf-dist/doc/latex/tikz-nfold/tikz-nfold-doc.pdf
new file mode 100644
index 00000000000..e57496affc3
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/tikz-nfold/tikz-nfold-doc.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex/tikz-nfold/tikz-nfold-doc.tex b/Master/texmf-dist/doc/latex/tikz-nfold/tikz-nfold-doc.tex
new file mode 100644
index 00000000000..18bd047e7bf
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/tikz-nfold/tikz-nfold-doc.tex
@@ -0,0 +1,486 @@
+%% tikz-nfold.sty
+%% Copyright 2023 Jonathan Schulz
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3c
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3c or later is part of all distributions of LaTeX
+% version 2008-05-04 or later.
+%
+% This work has the LPPL maintenance status 'maintained'.
+%
+% The Current Maintainer of this work is Jonathan Schulz.
+%
+% This work consists of the files pgflibrarybezieroffset.code.tex,
+% tikzlibrarynfold.code.tex, tikz-nfold-doc.tex, and tikz-nfold-doc.pdf.
+
+
+\documentclass[12pt,a4paper]{article}
+
+\usepackage[utf8]{inputenc}
+\usepackage{tikz}
+\usepackage{tkzexample}
+\usetikzlibrary{calc}
+\usetikzlibrary{nfold}
+\usepackage{parskip}
+\usepackage[margin=2.5cm]{geometry}
+
+% recommended order: 1) hyperref 2) amsthm 3) cleveref 4) \newtheorem
+\usepackage[colorlinks]{hyperref}
+\usepackage{amsmath}
+\usepackage{amsthm}
+\usepackage[capitalize]{cleveref}
+
+\theoremstyle{definition}
+\newtheorem{definition}{Definition}[section]
+
+%\newcommand{\todo}[1]{{\color{red}\bfseries#1}}
+\newcommand{\tikzdouble}{\texttt{/tikz/double}}
+\newcommand{\nfold}{\texttt{nfold}}
+\newcommand{\tikznfold}{\texttt{/tikz/nfold}}
+
+\pgfkeys{/tkzexample/every tkzexample/.style={code=violet!15, graphic=orange!20, very small}}
+
+\newcommand{\tv}{\vec{t}}
+\newcommand{\uv}{\vec{u}}
+\newcommand{\vv}{\vec{v}}
+
+\begin{document}
+
+\title{The \textsf{tikz-nfold} package}
+\author{Jonathan Schulz}
+\date{March 2023}
+
+\maketitle
+
+\begin{abstract}
+ This package provides an alternative to TikZ' \verb|/tikz/double| option, avoiding some shortcomings of the original approach. It also provides an option to draw triple, quadruple, and n-fold paths.
+\end{abstract}
+
+\section*{Compatibility}
+This package has been tested with \texttt{pdflatex}, \texttt{lualatex} and \texttt{xelatex}. Support for plain \TeX{} could, in principle, be implemented in the future as well.
+
+\section{Quick start}
+
+Add
+\begin{verbatim}
+\usetikzlibrary{nfold}
+\end{verbatim}
+to your preamble. Now you can add the style \tikznfold{} to any path that uses \tikzdouble:
+
+\begin{tkzexample}[latex=4cm]
+\begin{tikzpicture}
+ \draw[double distance=3pt]
+ (0,2) to[out=0, in=180] (3,3);
+ \draw[double distance=3pt, nfold]
+ (0,0) to[out=0, in=180] (3,1);
+ \node[right] at (0,3) {double:};
+ \node[right] at (0,1) {nfold:};
+\end{tikzpicture}
+\end{tkzexample}
+While it appears that adding \nfold{} does not do much here, it avoids some rendering issues of \tikzdouble, hence I recommend using it in most cases (see \cref{subsec:doubleIssues} for details).
+
+Specify a number for n-fold lines:
+\begin{tkzexample}[latex=4cm]
+\begin{tikzpicture}
+ \draw[double distance=7pt, nfold=5]
+ (0,0) to[out=0, in=180] (3,1) -- (3,0);
+\end{tikzpicture}
+\end{tkzexample}
+
+The arrow tips \texttt{Implies} and \texttt{Bar} are supported (the latter can also be aliased by \texttt{|}):
+\begin{tkzexample}[, latex=4cm]
+\begin{tikzpicture}
+ \draw[double distance=7pt, nfold=5, arrows=Bar-Implies]
+ (0,1.5) to[bend left] (3,1.5);
+ \draw[double distance=7pt, nfold=5, arrows=Implies-Implies]
+ (0,0) to[bend left] (3,0);
+\end{tikzpicture}
+\end{tkzexample}
+
+Different line joins are supported:
+\begin{tkzexample}[latex=2cm]
+\begin{tikzpicture}[line join=bevel]
+ \draw[line join=miter, double distance=7pt, nfold=4]
+ (0,2) -- (1, 2) -- (1,2.5);
+ \draw[double distance=7pt, nfold=4]
+ (0,1) -- (1, 1) -- (1,1.5);
+ \draw[line join=round, double distance=7pt, nfold=4]
+ (0,0) -- (1, 0) -- (1,.5);
+\end{tikzpicture}
+\end{tkzexample}
+
+There is also support for \texttt{tikz-cd}:
+\begin{tkzexample}[pre={\usetikzlibrary{cd}}, latex=3cm]
+\begin{tikzcd}
+ a \ar[r, Rightarrow, bend right, nfold=3] &
+ b \ar[d, Mapsto, nfold=3, "\alpha", "\beta"'] \\
+ c \ar[r, Mapsfrom, double distance=4pt, nfold=4] &
+ d
+\end{tikzcd}
+\end{tkzexample}
+
+\section{Comparison to \tikzdouble}
+
+This package does \emph{not} aim to supersede \tikzdouble, as both the original and the \nfold{} approach have their own strengths and weaknesses. The main difference is that \tikzdouble{} achieves its goal by drawing the original path twice, once very thick with the foreground colour and then slightly less thick with the background colour. By contrast, \nfold{} offsets the path:
+\begin{equation}
+ \label{eq:compareApproaches}
+ \begin{tikzpicture}[baseline=.5cm]
+ \fill[orange!20] (-4.5,-.5) rectangle (6, 1.5);
+ \node[left] at (-1,1) {\tikzdouble:};
+ \draw[line width=7pt] (0,1) -- (1,1);
+ \node at (1.5,1) {+};
+ \draw[white, line width=5pt] (2,1) -- (3,1);
+ \node at (3.5,1) {=};
+ \draw[double distance=5pt] (4,1) -- (5,1);
+ \node[left] at (-1,0) {\tikznfold:};
+ \draw (0, 3pt) -- (1, 3pt);
+ \node at (1.5,0) {+};
+ \draw (2, -3pt) -- (3, -3pt);
+ \node at (3.5,0) {=};
+ \draw[double distance=5pt, nfold] (4,0) -- (5,0);
+ \end{tikzpicture}
+\end{equation}
+
+
+\subsection{Issues with \tikzdouble}
+\label{subsec:doubleIssues}
+While the approach of \tikzdouble{} is very robust and efficient, it does have a few pitfalls:
+\begin{itemize}
+ \item Different types of visual glitches can occur in PDF renderers:
+ \begin{itemize}
+ \item One common issue is that the white foreground piece completely covers the black background piece at certain zoom levels, leading to the top or bottom part of the doubled path missing (depending on your PDF viewer and zoom level, this issue might be visible in \cref{eq:compareApproaches}).
+ \item Another common glitch is the appearance of a thin horizontal line at the start and end of the doubled path (visible in most examples of curved paths if your viewer has this problem). The reason is that the larger black path in the background is not perfectly covered by the smaller white foreground piece, most likely due to rounding errors.
+ \end{itemize}
+ \item The approach assumes that the background has a uniform colour, and it is the user's responsibility to correctly set the background colour:
+\begin{tkzexample}[latex=3.5cm]
+\begin{tikzpicture}
+ \draw[red, line width=1pt] (2.5, 3.3) -- (2.5, 0);
+ \draw[line width=2pt, double distance=3pt]
+ (0,2) to[out=0, in=180] (3,3);
+ \node[right] at (0,3) {double:};
+ \draw[line width=2pt, double distance=3pt, nfold]
+ (0,0) to[out=0, in=180] (3,1);
+ \node[right] at (0,1) {nfold:};
+\end{tikzpicture}
+\end{tkzexample}
+ \item Transparency does not work correctly:
+\begin{tkzexample}[latex=3.5cm]
+\begin{tikzpicture}[line width=1pt]
+ \draw[double distance=3pt, opacity=.5]
+ (0,2) to[out=0, in=180] (3,3);
+ \draw[double distance=3pt, opacity=.5, nfold]
+ (0,0) to[out=0, in=180] (3,1);
+ \node[right] at (0,3) {double:};
+ \node[right] at (0,1) {nfold:};
+\end{tikzpicture}
+\end{tkzexample}
+ \item Triple and n-fold paths are not supported (although this could be implemented in principle).
+\end{itemize}
+
+\subsection{Issues with \tikznfold}
+
+This package is far from perfect, and even if it were, there would still be some cases where the approach of \tikzdouble{} is better suited. Here are some shortcomings of \nfold:
+\begin{itemize}
+ \item \nfold{} struggles with high curvatures and wide paths: Let $\kappa(t)$ be the curvature of the path in a given point, and let $\texttt{double distance} = \alpha$. If $\kappa(t) > \frac{2}{\alpha}$ (i.e. the radius of the osculating circle is smaller than half the width of the path) for some $0 \leq t \leq 1$, the output of \nfold{} will not be correct:
+\begin{tkzexample}[latex=3.5cm]
+\begin{tikzpicture}
+ \draw[double distance=5pt, line width=1pt]
+ (0,2) .. controls (4,2) and (0,3) .. (3,2.5);
+ \node[right] at (0,3) {double:};
+ \draw[double distance=5pt, line width=1pt, nfold]
+ (0,0) .. controls (4,0) and (0,1) .. (3,.5);
+ \node[right] at (0,1) {nfold:};
+\end{tikzpicture}
+\end{tkzexample}
+ Some, but not all of these cases raise warnings (this feature is on the wish list).
+ \item Some rare cases of curves are not offset correctly. The reasons for that are discussed below in \cref{subsec:subdivisionUsed}. Usually, slightly changing the control points or values of the curve will fix the problem.
+ \item Closing paths (i.e.\ using \texttt{-- cycle}) is not yet supported (this feature is on the wish list).
+ \item \nfold{} is significantly slower than \tikzdouble. Part of the reason is that the construction is far more complex, other reasons can be fixed in principle. Specifically, the use of the \texttt{decorations} library is rather inefficient for this purpose.
+ \item While I did my best trying to break \nfold{} with as many of TikZ' options as possible, there are most definitely some problematic options I have not tested. If you find any bugs, please report them \href{https://github.com/jonschz/tikz-nfold/issues/}{here}.
+\end{itemize}
+
+
+\section{Known issues}
+
+\subsection{Fixable / wish list}
+
+\begin{itemize}
+ \item Closing paths (e.g.\ \texttt{-- cycle}) is not yet properly supported.
+ \item The arrow detection is still quite rough and likely has some bugs. One known issue is that you cannot provide parameters to the arrow tips (e.g. \texttt{Implies[red]}).
+ \item Similarly, redefining arrows does not have the desired effect (usually, it has no effect at all).
+ \item It would not be very hard to check for too much curvature in the offsetting algorithm and throw warnings in these cases.
+ \item Migrating away from the \texttt{decorations} library and integrating this algorithm more tightly with the rendering pipeline would be possible. I expect that doing so will significantly improve the performance and also fix most arrow-related issues. This idea is on the back burner for now, as it likely requires changes to the TikZ rendering pipeline. If the TikZ team shows interest in integrating this library, I will reconsider this.
+ \item Discontinuous paths with an arrow tip at the start are rendered differently in \tikzdouble{} and \nfold. As I do not really see a use case for such paths (and a rendering pipeline integration would likely fix it anyway), this issue is not a priority for now.
+\end{itemize}
+
+\subsection{Impossible or very hard to fix}
+\begin{itemize}
+ \item Correctly rendering paths with too much curvature is borderline impossible with this approach. This is one of the cases where using \tikzdouble{} is the only viable option.
+ \item Curves of \nfold{} slightly deviate from the curves of \tikzdouble{} near joins with a non-zero angle:
+\begin{tkzexample}[latex=3.5cm]
+\begin{tikzpicture}[line width=1pt]
+ \draw[red, double distance=20pt]
+ (0,2) -- (2,2) to [out=-90, in=0] (.5,.5);
+ \draw[blue, double distance=20pt, opacity=.5, nfold]
+ (0,2) -- (2,2) to [out=-90, in=0] (.5,.5);
+ \node[right, red] at (0,3) {double};
+ \node[left, blue] at (3, 3) {nfold};
+\end{tikzpicture}
+\end{tkzexample}
+ This cannot be fixed without extensive use of the \texttt{intersections} library, hurting the performance, and the result might still not look great for orders $\geq 3$.
+ \item changing joins in \verb|\pgfsys@beginscope| without an accompanying \TeX{} group may cause inconsistent behaviour in the joins. For example,
+\begin{tkzexample}[latex=2cm]
+\makeatletter
+\begin{tikzpicture}[line join=miter, line width=2pt]
+ \pgfsys@beginscope
+ \pgfsetroundjoin
+ \pgfsys@endscope
+ \draw[double distance=5pt, nfold] (0,0) -- (.5,2) -- (1,0);
+\end{tikzpicture}
+\makeatother
+\end{tkzexample}
+ has \texttt{round} joins on the large path, but \texttt{miter} joins on the constituent paths. This problem does not occur with \verb|\pgfscope|.
+\end{itemize}
+
+\section{The basic layer \texttt{pgf} commands}
+
+This package also provides some basic layer commands for offsetting curves and straight lines. Use
+\begin{verbatim}
+\usepgflibrary{bezieroffset}
+\end{verbatim}
+to only import the base layer library. The following commands are provided:
+\begin{itemize}
+ \item \verb|\pgfoffsetcurve|: This macro draws the parallel of a Bézier curve. It takes five parameters, the first four being the four control points of the Bézier curve (e.g.\ in the form of \verb|\pgfpoint{}{}|), the fifth parameter is the distance by which the curve should be offset. A negative value offsets the curve in the opposite direction. This macro begins with a \verb|\pgfpointmoveto| to the offset first control point.
+ \item \verb|\pgfoffsetcurvenomove|: The only difference to the previous macro is that this version does not move to the offset first control point. This is useful if one wants to offset an uninterrupted path consisting of several curves. The output will only be correct if the previous path segment ends on the offset first control point.
+ \item \verb|\pgfoffsetline|: This macro offsets a straight line. It takes two points and the distance as parameters.
+ \item \verb|\pgfoffsetlinenomove|: This macro is analogous to \verb|\pgfoffsetcurvenomove|.
+\end{itemize}
+
+
+\appendix
+\newpage
+\section{The offsetting algorithm}
+
+This algorithm is based on an algorithm by \href{https://github.com/Pomax/}{Pomax}. See \href{https://pomax.github.io/bezierinfo/#offsetting}{A Primer on Bézier curves}, the source code can be found \href{https://github.com/Pomax/bezierinfo/blob/bcfce2149fa5e5540a2a2605986adab3b2a9a3bf/js/graphics-element/lib/bezierjs/bezier.js}{here}.
+
+\subsection{Simple and fully simple Bézier curves}
+
+Throughout this section the term ``Bézier curve'' refers to a cubic Bézier curve, which is defined by four points $(A_1, A_2, A_3, A_4)$.
+
+As explained in the aforementioned source, in almost all cases the parallel of a Bézier curve is not exactly a Bézier curve itself. To approximate the parallel using Bézier curves, we therefore first divide the given curve into ``simple'' segments which can be offset with reasonable accuracy. The following defines a simple segment:
+\begin{definition}
+ \label{def:simpleCurve}
+ A Bézier curve $(A_1, A_2, A_3, A_4)$ is \emph{simple} if
+ \begin{enumerate}
+ \item the points $A_2$ and $A_3$ lie on the same side of the line $\overline{A_1A_4}$,
+ \item the absolute angle between the tangents in $A_1$ and $A_4$ is at most $\pi/3$ (i.e.\ the cosine is no smaller than $0.5$), and
+ \item the distances fulfil $\overline{A_1A_2} + \overline{A_3A_4} \leq \overline{A_1A_4}$.\footnote{The reference only uses the first two conditions.}
+ \end{enumerate}
+\end{definition}
+\begin{definition}
+ \label{def:fullySimpleCurve}
+ A Bézier curve $(A_1, A_2, A_3, A_4)$ is \emph{fully simple}\footnote{This terminology is not used in the source.} if all of its segments are simple in the sense of \cref{def:simpleCurve}.
+\end{definition}
+In order to offset an arbitrary Bézier curve we split it into fully simple segments.
+
+\subsection{Subdivision}
+
+It is well known that at every point $0 < t < 1$, a Bézier curve $A = (A_1, A_2, A_3, A_4)$ can be subdivided into two Bézier curves $B$ and $C$ using de Casteljau's algorithm (which naturally fulfil $A_1 = B_1$ and $A_4 = C_4$). A more or less heuristic fact is that $B$ and $C$ are ``more likely'' to be simple than $A$ (if you can prove any of the statements here, please contact me). Hence, if one wants to offset a non-simple curve $A$, one could try to subdivide $A$ until all of its segments are simple, then offset each segment.
+
+\subsection{Pomax' approach}
+The original approach by Pomax consists of two passes. The first pass subdivides $A$ on all extrema in $x$ or $y$. In a second pass, each segment $A^{(i)}$ is made simple in steps of $t \mapsto t + 0.01$, roughly using the following pseudocode:
+\begin{samepage}
+\begin{verbatim}
+ t_1 = t_2 = 0.0
+ while t_2 < 1.0:
+ S = segment(A from t_1 to t_2+0.01)
+ if not isSimple(S):
+ segments += [S]
+ t_1 = t_2
+ t_2 += 0.01
+\end{verbatim}
+\end{samepage}
+Essentially, this verifies with great certainty that the segment is fully simple in the sense of
+
+The main reason this approach is not used in this library is performance, as the library is slow enough already. Other minor reasons include that the original approach is not invariant under reversals or rotations: Reversing and/or rotating a curve yields a different subdivision and hence potentially a slightly different-looking curve.
+
+\subsection{The approach used here}
+\label{subsec:subdivisionUsed}
+In this library, we instead take a recursive approach:
+\begin{verbatim}
+ def split(A, level):
+ if isSimple(A):
+ segments += [A]
+ else:
+ if level < 0:
+ Display a warning
+ segments += [A]
+ else:
+ first, second = split(A, t=0.5)
+ split(first, level-1)
+ split(second, level-1)
+\end{verbatim}
+The default maximum depth is 5, so the curve is split into at most $2^5 = 32$ segments. This has the downside that some simple but not fully simple curves may remain undetected and be offset slightly incorrectly. If you encounter examples of such curves with bad outputs or if you have any ideas for additional constraints to add to \cref{def:simpleCurve} that can be checked with reasonable computational effort, please be in touch.
+
+
+\subsection{Offsetting simple Bézier curves}
+
+Disregarding edge cases (which will be discussed later), offsetting the curve works as follows:
+\begin{enumerate}
+ \item Construct lines orthogonal to the tangent in $A_1$ and $A_4$ and find their intersection. This point is called the \emph{origin} of the curve.
+ \item The new control points $A'_1$ and $A'_4$ are given by $A_1$ and $A_4$ offset orthogonally to the tangent.
+ \item Construct a ray from $A'_1$ parallel to the tangent in $A_1$, and construct another ray from the origin through $A_2$. Now $A'_2$ is given by the intersection of those rays.
+ \item $A'_3$ can be constructed similarly.
+\end{enumerate}
+The construction is shown in the following picture:
+\[
+\begin{tikzpicture}[every node/.append style={inner sep=1.5pt, circle, fill}]
+ \coordinate (A1) at (-3, 0);
+ \coordinate (A2) at (-1, 2);
+ \coordinate (A3) at (1, 2);
+ \coordinate (A4) at (3, 0);
+ \coordinate (A1p) at (-4, 1);
+ \coordinate (A4p) at (4, 1);
+ \coordinate (A2p) at ($(A1p) + 1.33*(2,2)$);
+ \coordinate (A3p) at ($(A4p) + 1.33*(-2,2)$);
+ \coordinate (O) at (0, -3);
+ \draw[dashed] (A1) -- (A2) (A3) -- (A4);
+ \draw[dashed] (A1p) -- +(3,3) (A4p) -- +(-3,3);
+ \draw[dashed] (A1p) -- (O) node[pos=.12, below left, fill=none] {$d$} (A4p) -- (O) node[pos=.12, below right, fill=none] {$d$};
+ \draw[dashed] (A2p) -- (O) (A3p) -- (O);
+ \draw[line width=1pt, blue] (A1) .. controls (A2) and (A3) .. (A4);
+ \draw[line width=1pt, red] (A1p) .. controls (A2p) and (A3p) .. (A4p);
+ \node[label={below:$A_1$}] at (A1) {};
+ \node[label={right:$A_2$}] at (A2) {};
+ \node[label={right:$A_3$}] at (A3) {};
+ \node[label={below:$A_4$}] at (A4) {};
+ \node[label={left:$A'_1$}] at (A1p) {};
+ \node[label={right:$A'_2$}] at (A2p) {};
+ \node[label={right:$A'_3$}] at (A3p) {};
+ \node[label={right:$A'_4$}] at (A4p) {};
+ \node[label={right:$O$}] at (O) {};
+\end{tikzpicture}
+\]
+
+\subsection{Removing singularities}
+
+Clearly, as the angle between $\overrightarrow{A_1A_2}$ and $\overrightarrow{A_3A_4}$ decreases, the origin approaches infinity. Computing the control points $(A'_i)$ by computing the coordinates of $O$ is therefore not numerically stable for almost straight curves. However, we can get around this problem using elementary geometry. Constructing $A'_1$ and $A'_4$ is independent of the location of the origin, so the only difficult part is computing the distances $\overline{A'_1A'_2}$ and $\overline{A'_3A'_4}$. We find
+\begin{equation}
+ \frac{\overline{A'_1A'_2}}{\overline{A_1A_2}} = \frac{\overline{OA'_1}}{\overline{OA_1}} = 1 + \frac{d}{\overline{OA_1}} \implies \overline{A'_1A'_2} = \overline{A_1A_2} \left(1 + d \cdot \frac{1}{\overline{OA_1}}\right)
+\end{equation}
+which is regular as $O$ approaches infinity. Now we to determine the inverse of $\overline{OA_1}$, which can be computed using the law of sines:
+\[
+\begin{tikzpicture}[every node/.append style={inner sep=1.5pt, circle, fill}]
+ \coordinate (A1) at (-3, 0);
+ \coordinate (A2) at (-1, 2);
+ \coordinate (A3) at (1, 2);
+ \coordinate (A4) at (3, 0);
+ \coordinate (O) at (0, -3);
+ \draw[dashed] (A1) -- (A2) (A3) -- (A4);
+ \draw[dashed] (A1) -- (O) (A4) -- (O);
+ \draw (A1) -- (A4);
+ \draw (-2,-1) arc[start angle=-45, end angle=45, radius=1.41cm];
+ \draw ( 2,-1) arc[start angle=180+45, end angle=180-45, radius=1.41cm];
+ \draw (-1,-2) arc[start angle=180-45, end angle=45, radius=1.41cm];
+ \draw[line width=1pt, blue] (A1) .. controls (A2) and (A3) .. (A4);
+ \node[label={below:$A_1$}] at (A1) {};
+ \node[label={right:$A_2$}] at (A2) {};
+ \node[label={right:$A_3$}] at (A3) {};
+ \node[label={below:$A_4$}] at (A4) {};
+ \node[fill=none] at (-2,.3) {$\alpha$};
+ \node[fill=none] at (-2.1,-.3) {\scriptsize$\frac{\pi}{2}-\alpha$};
+ \node[fill=none] at ( 2,.3) {$\beta$};
+ \node[fill=none] at ( 2.1,-.3) {\scriptsize$\frac{\pi}{2}-\beta$};
+ \node[fill=none] at (0,-2.2) {$\alpha+\beta$};
+ \node[label={right:$O$}] at (O) {};
+\end{tikzpicture}
+\]
+\begin{equation}
+ \frac{\sin(\tfrac{\pi}{2} - \alpha)}{\overline{OA_4}} = \frac{\sin(\alpha + \beta)}{\overline{A_1A_4}} = \frac{\sin(\tfrac{\pi}{2} - \beta)}{\overline{OA_1}} \implies \frac{1}{\overline{OA_1}} = \frac{1}{\overline{A_1A_4}}\cdot\frac{\sin(\alpha + \beta)}{\cos(\beta)} \ .
+\end{equation}
+Note that $-\tfrac{\pi}{2} < \beta < \tfrac{\pi}{2}$ (and hence $\cos(\beta) > 0$) is guaranteed if the curve is simple --- in fact, simplicity guarantees $\alpha \cdot \beta > 0$ and $|\alpha| + |\beta| \leq \tfrac{\pi}{3}$. Using the sine addition theorem we can further rewrite the fraction to
+\begin{equation}
+ \frac{\sin(\alpha + \beta)}{\cos(\beta)} = \sin(\alpha) + \cos(\alpha) \frac{\sin(\beta)}{\cos(\beta)} \ ,
+\end{equation}
+and all of these terms can be computed directly from dot and cross products of vectors between the original control points. To summarise, let $\vv_{ij} := \overrightarrow{A_iA_j}$, and let $\vv_0$, $\vv_1$ be the normalised tangents at $t=0$ and $t=1$, respectively (see \cref{subsec:stabilisation} how they are computed). Then
+\begin{equation}
+% for checking
+ \overline{A'_1A'_2} = \overline{A_1A_2} + \frac{d}{\overline{A_1A_4}^2}\cdot\left(\vv_{12} \times \vv_{14} - \vv_{12}\cdot\vv_{14} \frac{\vv_{14}\times\tv_1}{\vv_{14}\cdot\tv_1} \right) \ .
+\end{equation}
+Let furthermore $\uv_{ij} := \vv_{ij}/|\vv_{ij}|$ be the normalised vectors. Then we find
+\begin{equation}%
+ \label{eq:finalPrimeDistance}
+ \begin{aligned}
+ \overline{A'_1A'_2} &= \overline{A_1A_2} + \frac{d}{\overline{A_1A_4}} \left(\vv_{12} \times \uv_{14} - \vv_{12}\cdot\uv_{14} \frac{\uv_{14}\times\tv_1}{\uv_{14}\cdot\tv_1} \right) \ , \\
+ \overline{A'_4A'_3} &= \overline{A_4A_3} + \frac{d}{\overline{A_1A_4}} \left(\vv_{43} \times \uv_{14} - \vv_{43}\cdot\uv_{14} \frac{\uv_{14}\times\tv_0}{\uv_{14}\cdot\tv_0} \right) \ .
+ \end{aligned}
+\end{equation}
+
+
+
+\subsection{Edge cases}
+
+\subsubsection{Overlaps: $A_i = A_{i+1}$}
+
+If there is one overlap $A_1=A_2$, $A_2=A_3$ or $A_3=A_4$, the cubic Bézier curve reduces to a quadratic one. For two overlaps, we get a linear Bézier curve (i.e.\ a straight line), and for three overlaps we get a point. The main problem to watch out for is that the tangents $\tv_0$ and $\tv_1$ need to be computed differently:
+\begin{itemize}
+ \item If $A_1 \neq A_2$, we find $\tv_0 = \uv_{12}$.
+ \item If $A_1 = A_2 \neq A_3$, we find $\tv_0 = \uv_{13}$.\footnote{This implicitly assumes a regular reparametrisation of the curve (e.g. a parametrisation over arc length); the usual parametrisation has a gradient of zero at $t=0$.}
+ \item If $A_1 = A_2 = A_3 \neq A_4$, we find $\tv_0 = \uv_{14}$.
+ \item If $A_1 = A_2 = A_3 = A_4$, the curve is just a point and the tangent is not defined. The implementation defaults to $\tv_0 = (1,0)$.
+\end{itemize}
+The analogous statement hold for $\tv_1$. In practice we test for approximate, not exact equality.
+
+\subsubsection{Overlaps $A_1 = A_4$}
+
+\Cref{eq:finalPrimeDistance} has one remaining singularity, namely for $A_1 \approx A_4$. This singularity is fundamental and not an artefact: As $A_1$ approaches $A_4$ while $\overline{A_1A_2}$ and $\overline{A_3A_4}$ stay constant, $O$ also approaches $A_4$, hence the angle between $\overrightarrow{A_1A_2}$ and $\overrightarrow{OA_2}$ approaches zero, sending the intersection point $A'_2$ to infinity.
+
+A closer inspection of this special case reveals a number of sub-cases:
+\begin{itemize}
+ \item Fully degenerate curves with $A_1 \approx A_2 \approx A_3 \approx A_4$. Offsetting these curves in a stable manner is impossible, as the gradient and hence the direction in which to offset is numerically unstable. Rendering such curves will hardly have any output at all anyway. See below in \cref{subsec:stabilisation} how this case is handled.
+ \item Non-trivial loops with $\overline{A_1A_2} \gg \overline{A_1A_4}$ and/or $\overline{A_3A_4} \gg \overline{A_1A_4}$, $|\alpha| + |\beta| \geq \tfrac{\pi}{3}$. Such curves violate the second and third condition of \cref{def:simpleCurve}, for example:
+ \[
+ \begin{tikzpicture}[every node/.append style={inner sep=1.5pt, circle, fill}]
+ \coordinate (A1) at (-.07, 0);
+ \coordinate (A2) at (-1, 2);
+ \coordinate (A3) at (1, 2);
+ \coordinate (A4) at (.07, 0);
+ \draw[dashed] (A1) -- (A2) (A3) -- (A4);
+ \draw[line width=1pt, blue] (A1) .. controls (A2) and (A3) .. (A4);
+ \node[label={left:$A_1$}] at (A1) {};
+ \node[label={left:$A_2$}] at (A2) {};
+ \node[label={right:$A_3$}] at (A3) {};
+ \node[label={right:$A_4$}] at (A4) {};
+ \end{tikzpicture}
+ \]
+ \item Curves with $\overline{A_1A_2} \gg \overline{A_1A_4}$ and/or $\overline{A_3A_4} \gg \overline{A_1A_4}$, $|\alpha| + |\beta| < \tfrac{\pi}{3}$: Such curves violate the third condition of \cref{def:simpleCurve}, for example:
+ \[
+ \begin{tikzpicture}[every node/.append style={inner sep=1.5pt, circle, fill}]
+ \coordinate (A1) at (-.07, 0);
+ \coordinate (A3) at (-2, 1);
+ \coordinate (A2) at (2, 1);
+ \coordinate (A4) at (.07, 0);
+ \draw[dashed] (A1) -- (A2) (A3) -- (A4);
+ \draw[line width=1pt, blue] (A1) .. controls (A2) and (A3) .. (A4);
+ \node[label={left:$A_1$}] at (A1) {};
+ \node[label={left:$A_2$}] at (A2) {};
+ \node[label={right:$A_3$}] at (A3) {};
+ \node[label={right:$A_4$}] at (A4) {};
+ \end{tikzpicture}
+ \]
+\end{itemize}
+
+\subsection{Stabilising the offsetting algorithm for non-simple curves}
+\label{subsec:stabilisation}
+
+As seen in \cref{subsec:subdivisionUsed}, there are cases where the offsetting algorithm will be called on non-simple curves. In such cases it is essential that the code does not crash (e.g. from division by zero), and that the output produced is at least somewhat sensible. The following measures are taken:
+\begin{itemize}
+ \item If $\overline{A_1A_4}$ is very close to zero,\footnote{Clamping the fraction $d/\overline{A_1A_4}$ to some maximum value did not work well, as it had a tendency of producing false positives.} we set $\overline{A'_1A'_2} = \overline{A_1A_2}$ and $\overline{A'_3A'_4} = \overline{A_3A_4}$, forming a rather rough approximation of the parallel curve. This causes a warning to be logged.
+ \item For simple curves we find $\uv_{14}\cdot\uv_{34} \geq 0.5$ in the denominator. Therefore, for non-simple curves, the denominator is clamped to $[0.5, 1.0]$, preventing division by zero.
+\end{itemize}
+
+
+\end{document}
diff --git a/Master/texmf-dist/tex/latex/tikz-nfold/pgflibrarybezieroffset.code.tex b/Master/texmf-dist/tex/latex/tikz-nfold/pgflibrarybezieroffset.code.tex
new file mode 100644
index 00000000000..e3391ed92c0
--- /dev/null
+++ b/Master/texmf-dist/tex/latex/tikz-nfold/pgflibrarybezieroffset.code.tex
@@ -0,0 +1,351 @@
+%% tikz-nfold.sty
+%% Copyright 2023 Jonathan Schulz
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3c
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3c or later is part of all distributions of LaTeX
+% version 2008-05-04 or later.
+%
+% This work has the LPPL maintenance status 'maintained'.
+%
+% The Current Maintainer of this work is Jonathan Schulz.
+%
+% This work consists of the files pgflibrarybezieroffset.code.tex,
+% tikzlibrarynfold.code.tex, tikz-nfold-doc.tex, and tikz-nfold-doc.pdf.
+
+
+% stores the current \pgf@x and \pgf@y in #1
+\def\pgfstorepoint#1{\edef#1{\noexpand\pgfpoint{\the\pgf@x}{\the\pgf@y}}}
+% global version in case we need it:
+\def\pgfglobalstorepoint#1{\xdef#1{\noexpand\pgfpoint{\the\pgf@x}{\the\pgf@y}}}
+
+% Split a Bezier curve (de Casteljau's algorithm)
+% #1 = time (between 0 and 1)
+% #2-#5: control points
+% Outputs the first part into \pgf@splitbezier@i@i, ... , \pgf@splitbezier@i@iv,
+% and the second part into \pgf@splitbezier@ii@i, ... , \pgf@splitbezier@ii@iv.
+
+% TODO this code can be simplified to splitting at 0.5 only;
+% this is partially implemented in some pgf file, possibly decorations or basic paths.
+% But maybe I will need the general case in the future, maybe with some advanced fully simple detection.
+% Leave it in for now
+\newcommand{\pgf@splitbezier}[5]{
+ % based on pgfcorepoints.code.tex, \pgfpointcurveattime
+ \pgfmathparse{#1}%
+ \let\pgf@time@s=\pgfmathresult%
+ \global\pgf@x=\pgfmathresult pt%
+ \global\pgf@x=-\pgf@x%
+ \advance\pgf@x by 1pt%
+ \edef\pgf@time@t{\pgf@sys@tonumber{\pgf@x}}%
+ % P^0_3
+ \pgf@process{#5}%
+ \pgf@xc=\pgf@x%
+ \pgf@yc=\pgf@y%
+ \pgfstorepoint{\pgf@splitbezier@ii@iv}
+ % P^0_2
+ \pgf@process{#4}%
+ \pgf@xb=\pgf@x%
+ \pgf@yb=\pgf@y%
+ % P^0_1
+ \pgf@process{#3}%
+ \pgf@xa=\pgf@x%
+ \pgf@ya=\pgf@y%
+ % P^0_0
+ \pgf@process{#2}%
+ \pgfstorepoint{\pgf@splitbezier@i@i}
+ % First iteration:
+ % P^1_0
+ \global\pgf@x=\pgf@time@t\pgf@x\global\advance\pgf@x by\pgf@time@s\pgf@xa%
+ \global\pgf@y=\pgf@time@t\pgf@y\global\advance\pgf@y by\pgf@time@s\pgf@ya%
+ \pgfstorepoint{\pgf@splitbezier@i@ii}
+ % P^1_1
+ \pgf@xa=\pgf@time@t\pgf@xa\advance\pgf@xa by\pgf@time@s\pgf@xb%
+ \pgf@ya=\pgf@time@t\pgf@ya\advance\pgf@ya by\pgf@time@s\pgf@yb%
+ % P^1_2
+ \pgf@xb=\pgf@time@t\pgf@xb\advance\pgf@xb by\pgf@time@s\pgf@xc%
+ \pgf@yb=\pgf@time@t\pgf@yb\advance\pgf@yb by\pgf@time@s\pgf@yc%
+ \edef\pgf@splitbezier@ii@iii{\noexpand\pgfpoint{\the\pgf@xb}{\the\pgf@yb}}
+ % P^2_0
+ \global\pgf@x=\pgf@time@t\pgf@x\global\advance\pgf@x by\pgf@time@s\pgf@xa%
+ \global\pgf@y=\pgf@time@t\pgf@y\global\advance\pgf@y by\pgf@time@s\pgf@ya%
+ \pgfstorepoint{\pgf@splitbezier@i@iii}
+ % P^2_1
+ \pgf@xa=\pgf@time@t\pgf@xa\advance\pgf@xa by\pgf@time@s\pgf@xb%
+ \pgf@ya=\pgf@time@t\pgf@ya\advance\pgf@ya by\pgf@time@s\pgf@yb%
+ \edef\pgf@splitbezier@ii@ii{\noexpand\pgfpoint{\the\pgf@xa}{\the\pgf@ya}}
+ % P^3_0
+ \global\pgf@x=\pgf@time@t\pgf@x\global\advance\pgf@x by\pgf@time@s\pgf@xa%
+ \global\pgf@y=\pgf@time@t\pgf@y\global\advance\pgf@y by\pgf@time@s\pgf@ya%
+ \pgfstorepoint{\pgf@splitbezier@i@iv}
+ \pgfstorepoint{\pgf@splitbezier@ii@i}
+}
+
+
+% computes the cross product and puts it into \pgfmathresult
+\newcommand{\pgfcrossproduct}[2]{
+ \pgf@process{#1}%
+ \pgf@xa=\pgf@x%
+ \pgf@ya=\pgf@y%
+ \pgf@process{#2}%
+ \pgfmathparse{\pgf@xa*\pgf@y-\pgf@ya*\pgf@x}%
+}
+
+\newcommand{\pgfdotproduct}[2]{
+ \pgf@process{#1}%
+ \pgf@xa=\pgf@x%
+ \pgf@ya=\pgf@y%
+ \pgf@process{#2}%
+ \pgfmathparse{\pgf@xa*\pgf@x+\pgf@ya*\pgf@y}%
+}
+
+\newcommand{\pgfcrossdot}[2]{
+ \pgf@process{#1}%
+ \pgf@xa=\pgf@x%
+ \pgf@ya=\pgf@y%
+ \pgf@process{#2}%
+ \pgfmathsetlengthmacro{\pgf@tmp@dot}{\pgf@xa*\pgf@x+\pgf@ya*\pgf@y}%
+ \pgfmathsetlengthmacro{\pgf@tmp@cross}{\pgf@xa*\pgf@y-\pgf@ya*\pgf@x}%
+}
+
+
+% Computes the normalised tangents of a given Bezier curve and stores them in \pgf@tmp@tang@i and \pgf@tmp@tang@ii.
+% All degenerate cases are covered. For a triple degenerate curve (all points equal), the vector (1,0) is returned.
+\newcommand{\pgf@offset@compute@tangents}[4]{
+ \pgfpointdiff{#1}{#2} % unintuitively, this is PTii - PTi
+ \pgfmathparse{abs(\pgf@x) + abs(\pgf@y)}
+ \ifdim\pgfmathresult pt<0.1pt\relax
+ % edge case: first point and first control point are equal
+ \pgfpointdiff{#1}{#3}
+ \pgfmathparse{abs(\pgf@x) + abs(\pgf@y)}
+ \ifdim\pgfmathresult pt<0.1pt\relax
+ % edge case: first three points are equal
+ \pgfpointdiff{#1}{#4}
+ \fi
+ \fi
+ \pgfpointnormalised{}
+ \pgfstorepoint\pgf@tmp@tang@i
+ \pgfpointdiff{#3}{#4}
+ \pgfmathparse{abs(\pgf@x) + abs(\pgf@y)}
+ \ifdim\pgfmathresult pt<0.1pt\relax
+ \pgfpointdiff{#2}{#4}
+ \pgfmathparse{abs(\pgf@x) + abs(\pgf@y)}
+ \ifdim\pgfmathresult pt<0.1pt\relax
+ \pgfpointdiff{#1}{#4}
+ \fi
+ \fi
+ \pgfpointnormalised{}
+ \pgfstorepoint\pgf@tmp@tang@ii
+}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Offsetting a simple section %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\newcommand{\pgf@offset@bezier@segment}[5]{%
+ % TODO would it make sense to use \pgf@process here?
+ % normalise tangents and normals; this avoids overflow issues later, and we need
+ % the normal vector to be of length 1 anyway
+ \pgf@offset@compute@tangents{#1}{#2}{#3}{#4}
+ % offset A1
+ % compute the normal
+ \pgf@tmp@tang@i
+ \pgf@xa=\pgf@x
+ \pgf@x=-\pgf@y
+ \pgf@y=\pgf@xa
+ \pgfstorepoint\pgf@tmp@normal@i
+ % Leaving this parameter empty amounts to working directly on the register
+ \pgfpointadd{\pgfpointscale{#5}{}}{#1}
+ \pgfstorepoint{\pgf@bezier@offset@i}
+ % offset A4
+ \pgf@tmp@tang@ii
+ \pgf@xa=\pgf@x
+ \pgf@x=-\pgf@y
+ \pgf@y=\pgf@xa
+ \pgfstorepoint\pgf@tmp@normal@ii
+ \pgfpointadd{\pgfpointscale{#5}{}}{#4}
+ \pgfstorepoint\pgf@bezier@offset@iv
+ % now compute A'_2 and A'_3
+ \pgfpointdiff{#1}{#4}
+ \pgfmathsetmacro{\pgf@tmp@secantlen}{veclen(\pgf@x,\pgf@y)}
+ \ifdim\pgf@tmp@secantlen pt<0.1pt\relax
+ % Edge case: Either the curve is degenerate to a point or it is not simple.
+ % Either way we offset A1 and A4, and preserve the vectors A1A2 and A3A4.
+ \pgfwarning{pgf-offset: first and last point are too close, expect glitches}
+ \pgfpointadd{\pgf@bezier@offset@i}{\pgfpointdiff{#1}{#2}}
+ \pgfstorepoint\pgf@bezier@offset@ii
+ \pgfpointadd{\pgf@bezier@offset@iv}{\pgfpointdiff{#4}{#3}}
+ \pgfstorepoint\pgf@bezier@offset@iii
+ \else
+ \pgfpointnormalised{}
+ \pgfstorepoint\pgf@tmp@secant
+ \pgfcrossdot{}{\pgf@tmp@tang@ii}
+ \ifdim\pgf@tmp@dot<.5pt\relax%
+ % this can only happen in non-simple curves
+ \pgfwarning{pgf-offset: cosine of \pgf@tmp@dot\space clamped to 0.5 in non-simple segment}%
+ \def\pgf@tmp@dot{.5pt}%
+ \fi%
+ \pgfmathsetmacro{\pgf@tmp@tanbeta}{\pgf@tmp@cross/\pgf@tmp@dot}%
+ \pgfcrossdot{\pgf@tmp@secant}{\pgfpointnormalised{\pgfpointdiff{#1}{#2}}}
+ % There are cases where we want #5/secantlen to be quite large, so we should not clamp the value here
+ \pgfmathparse{1 + #5/\pgf@tmp@secantlen*(\pgf@tmp@cross - \pgf@tmp@dot*\pgf@tmp@tanbeta)}
+ \pgfpointadd%
+ {\pgf@bezier@offset@i}%
+ {\pgfpointscale{\pgfmathresult pt}{\pgfpointdiff{#1}{#2}}}%
+ \pgfstorepoint\pgf@bezier@offset@ii
+ % third control point
+ \pgfcrossdot{\pgf@tmp@secant}{\pgf@tmp@tang@i}
+ \ifdim\pgf@tmp@dot<.5pt\relax%
+ \pgfwarning{pgf-offset: cosine of \pgf@tmp@dot\space clamped to 0.5 in non-simple segment}%
+ \def\pgf@tmp@dot{.5pt}%
+ \fi%
+ \pgfmathsetmacro{\pgf@tmp@tanbeta}{\pgf@tmp@cross/\pgf@tmp@dot}%
+ \pgfcrossdot{\pgf@tmp@secant}{\pgfpointnormalised{\pgfpointdiff{#4}{#3}}}%
+ \pgfmathparse{1 + #5/\pgf@tmp@secantlen*(\pgf@tmp@cross - \pgf@tmp@dot*\pgf@tmp@tanbeta)}%
+ \pgfpointadd%
+ {\pgf@bezier@offset@iv}%
+ {\pgfpointscale{\pgfmathresult pt}{\pgfpointdiff{#4}{#3}}}%
+ \pgfstorepoint\pgf@bezier@offset@iii%
+ \fi
+}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Subdividing and offsetting %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+% Maximum level of recursion. The theoretical limit to the number of subdivisions in the final curve
+% is given by 2^\pgf@offset@max@recursion
+\newcount\pgf@offset@max@recursion
+\pgf@offset@max@recursion=4
+
+%
+% Subdivides a Bezier curve into "simple" segments (according to the definition below),
+% offsets the segments, and draws them. Because offsetting also involves relocating
+% the starting points, these macros come in two variants: with and without a \pgfmoveto{}
+% to the new starting point.
+%
+% Interface:
+% #1-#4: control points of the whole Bezier curve
+% #5: offset
+
+\newcommand{\pgfoffsetcurve}[5]{%
+ \pgf@subdivideandoffsetcurve{#1}{#2}{#3}{#4}{#5}{\pgf@offset@max@recursion}{0}{\pgf@nfold@callback@move}%
+}
+\newcommand{\pgfoffsetcurvenomove}[5]{%
+ \pgf@subdivideandoffsetcurve{#1}{#2}{#3}{#4}{#5}{\pgf@offset@max@recursion}{0}{\pgf@nfold@callback@nomove}%
+}
+
+% Arguments:
+% #1-#4: control points of the segment
+% #5: =0 if this is the first segment of the curve, =1 otherwise
+% (checking for #5=0 allows us to draw the curve without interruptions)
+\newcommand{\pgf@nfold@callback@move}[5]{%
+ \ifnum#5=0\relax\pgfpathmoveto{#1}\fi%
+ \pgfpathcurveto{#2}{#3}{#4}%
+}
+% this version never does a moveto at the start. Useful for drawing a path consisting of
+% multiple Bezier curves.
+\newcommand{\pgf@nfold@callback@nomove}[5]{\pgfpathcurveto{#2}{#3}{#4}}
+
+% Like the previous macro, but with a custom callback macro for each segment instead of
+% executing \drawsegment as defined above. See \drawsegment for the arguments.
+\newcommand{\pgfoffsetcurvecallback}[6]{%
+ \pgf@subdivideandoffsetcurve{#1}{#2}{#3}{#4}{#5}{\pgf@offset@max@recursion}{0}{#6}%
+}
+
+
+% 6 parameters:
+% #1-#4: control points
+% #5: offset
+% #6: recursion limit (decreases on every recursive call)
+% #7: =0 if this is the start of the curve, =1 otherwise;
+% #8: callback for output (see above)
+\newif\ifpgf@offset@subdivide
+\newcommand{\pgf@subdivideandoffsetcurve}[8]{%
+ % we need a group to avoid overwriting variables in recursive calls
+ \begingroup%
+ \pgf@offset@subdividefalse%
+ \c@pgf@counta=#6%
+ \advance\c@pgf@counta by-1%
+ \pgf@process{#1}\pgfstorepoint{\pgf@ctrl@i}%
+ \pgf@process{#2}\pgfstorepoint{\pgf@ctrl@ii}%
+ \pgf@process{#3}\pgfstorepoint{\pgf@ctrl@iii}%
+ \pgf@process{#4}\pgfstorepoint{\pgf@ctrl@iv}%
+ % Use the non-degenerate tangents for the simplicity check
+ \pgf@offset@compute@tangents{\pgf@ctrl@i}{\pgf@ctrl@ii}{\pgf@ctrl@iii}{\pgf@ctrl@iv}%
+ \pgfpointdiff{\pgf@ctrl@i}{\pgf@ctrl@iv}\pgfstorepoint{\pgf@itoiv}%
+ \pgfcrossproduct{\pgf@itoiv}{\pgf@tmp@tang@i}%
+ \edef\firstcross{\pgfmathresult}%
+ \pgfcrossproduct{\pgf@itoiv}{\pgf@tmp@tang@ii}%
+ % First simplicity check: Are A2 and A3 on the same side of the A1-A4 line?
+ % -> compute the sign of the cross products, use the sign function to avoid overflows
+ \pgfmathparse{sign(\firstcross)*sign(\pgfmathresult)}%
+ \ifdim\pgfmathresult pt>0pt\relax%
+ \pgf@offset@subdividetrue%
+ \else%
+ % Second simplicity check: How large is the angle between the tangents in A1 and A4?
+ \pgfdotproduct{\pgf@tmp@tang@i}{\pgf@tmp@tang@ii}%
+ \ifdim\pgfmathresult pt<.5pt\relax%
+ \pgf@offset@subdividetrue%
+ \else
+ % Third simplicity check: Put a limit on the lengths of the i-ii and iii-iv vectors combined
+ \pgf@itoiv
+ \pgfmathsetmacro{\pgf@tmp@len@i@iv}{veclen(\pgf@x,\pgf@y)}
+ \pgfpointdiff{\pgf@ctrl@i}{\pgf@ctrl@ii}
+ \pgf@xb=\pgf@x\pgf@yb=\pgf@y
+ \pgfpointdiff{\pgf@ctrl@iii}{\pgf@ctrl@iv}
+ \pgfmathparse{\pgf@tmp@len@i@iv < veclen(\pgf@xb,\pgf@yb) + veclen(\pgf@x,\pgf@y)}
+ \ifnum\pgfmathresult=1\relax
+ \pgf@offset@subdividetrue%
+ \fi
+ \fi%
+ \fi%
+ \ifpgf@offset@subdivide%
+ \ifnum\c@pgf@counta<0%
+ % We hit the recursion limit but the segment is not simple
+ \pgfwarning{pgf-offset: Recursion limit reached, glitches may occur. %
+ Consider increasing \string\pgf@offset@max@recursion}%
+ % Try to offset the curve anyway. The result will not be precise,
+ % but the code is sufficiently robust to not crash
+ \pgf@offset@bezier@segment{\pgf@ctrl@i}{\pgf@ctrl@ii}{\pgf@ctrl@iii}{\pgf@ctrl@iv}{#5}%
+ #8{\pgf@bezier@offset@i}{\pgf@bezier@offset@ii}{\pgf@bezier@offset@iii}{\pgf@bezier@offset@iv}{#7}%
+ \else
+ % split the non-simple segment and execute recursive calls
+ \pgf@splitbezier{.5}{\pgf@ctrl@i}{\pgf@ctrl@ii}{\pgf@ctrl@iii}{\pgf@ctrl@iv}%
+ % pass on the "start of the curve flag" only to the first term
+ \pgf@subdivideandoffsetcurve{\pgf@splitbezier@i@i}{\pgf@splitbezier@i@ii}{\pgf@splitbezier@i@iii}{\pgf@splitbezier@i@iv}{#5}{\c@pgf@counta}{#7}{#8}%
+ \pgf@subdivideandoffsetcurve{\pgf@splitbezier@ii@i}{\pgf@splitbezier@ii@ii}{\pgf@splitbezier@ii@iii}{\pgf@splitbezier@ii@iv}{#5}{\c@pgf@counta}{1}{#8}%
+ \fi%
+ \else%
+ % curve is simple
+ \pgf@offset@bezier@segment{\pgf@ctrl@i}{\pgf@ctrl@ii}{\pgf@ctrl@iii}{\pgf@ctrl@iv}{#5}%
+ #8{\pgf@bezier@offset@i}{\pgf@bezier@offset@ii}{\pgf@bezier@offset@iii}{\pgf@bezier@offset@iv}{#7}%
+ \fi%
+ \endgroup%
+}
+
+
+%
+% Offsetting straight lines
+% -------------------------
+%
+% For convenience we also provide macros that offset straight lines. These also come in two variants
+% similar to the macros for curves.
+%
+\newcommand{\pgfoffsetline}[3]{
+ \pgfpointscale{#3}{\pgfpointnormalised{\pgfpointdiff{#1}{#2}}}
+ \pgf@xc=-\pgf@y
+ \pgf@yc=\pgf@x
+ \pgfpathmoveto{\pgfpointadd{#1}{\pgfqpoint{\pgf@xc}{\pgf@yc}}}
+ \pgfpathlineto{\pgfpointadd{#2}{\pgfqpoint{\pgf@xc}{\pgf@yc}}}
+}
+\newcommand{\pgfoffsetlinenomove}[3]{
+ \pgfpointscale{#3}{\pgfpointnormalised{\pgfpointdiff{#1}{#2}}}
+ \pgf@xc=-\pgf@y
+ \pgf@yc=\pgf@x
+ \pgfpathlineto{\pgfpointadd{#2}{\pgfqpoint{\pgf@xc}{\pgf@yc}}}
+}
diff --git a/Master/texmf-dist/tex/latex/tikz-nfold/tikzlibrarynfold.code.tex b/Master/texmf-dist/tex/latex/tikz-nfold/tikzlibrarynfold.code.tex
new file mode 100644
index 00000000000..8013d423f6c
--- /dev/null
+++ b/Master/texmf-dist/tex/latex/tikz-nfold/tikzlibrarynfold.code.tex
@@ -0,0 +1,785 @@
+%% tikz-nfold.sty
+%% Copyright 2023 Jonathan Schulz
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3c
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3c or later is part of all distributions of LaTeX
+% version 2008-05-04 or later.
+%
+% This work has the LPPL maintenance status 'maintained'.
+%
+% The Current Maintainer of this work is Jonathan Schulz.
+%
+% This work consists of the files pgflibrarybezieroffset.code.tex,
+% tikzlibrarynfold.code.tex, tikz-nfold-doc.tex, and tikz-nfold-doc.pdf.
+
+\usetikzlibrary{decorations}
+\usetikzlibrary{arrows.meta}
+%\usepgflibrary{bezieroffset}
+\usepgflibrary{bezieroffset}
+
+%
+%
+% Intercepting join settings
+% --------------------------
+%
+% The current settings of the line joins are not stored in any TeX registers; instead, direct system
+% calls are made to apply the settings. Therefore, we need to modify the pgf macros in order
+% to cache the current settings.
+%
+
+% According to the pgf documentation, miter limit=10 is the default value,
+% but I couldn't find and verify this setting in the pgf code
+\gdef\pgf@nfold@cached@miterlimit{10}
+\let\pgf@nfold@old@miterlimit\pgfsetmiterlimit
+\def\pgfsetmiterlimit#1{%
+ \pgf@nfold@old@miterlimit{#1}%
+ \edef\pgf@nfold@cached@miterlimit{#1}%
+}
+
+% default line join is "miter"
+\global\let\pgf@cached@linejoin=m% % b = bevel, m=miter, r=round
+\let\pgf@nfold@old@setbeveljoin\pgfsetbeveljoin
+\let\pgf@nfold@old@setmiterjoin\pgfsetmiterjoin
+\let\pgf@nfold@old@setroundjoin\pgfsetroundjoin
+\def\pgfsetbeveljoin{%
+ \pgf@nfold@old@setbeveljoin%
+ % do NOT change this globally! Needs to be changed back at the end of groups for scoping reasons
+ \let\pgf@cached@linejoin=b%
+}
+\def\pgfsetmiterjoin{%
+ \pgf@nfold@old@setmiterjoin%
+ \let\pgf@cached@linejoin=m%
+}
+\def\pgfsetroundjoin{%
+ \pgf@nfold@old@setroundjoin%
+ \let\pgf@cached@linejoin=r%
+}
+
+%
+% Various helper commands
+% -----------------------
+
+% check if a decoration segment is visible, i.e. not moveto or last
+\newif\ifpgf@nfold@segm@visible
+\def\ifpgfsegmentvisible#1{%
+ \pgf@nfold@segm@visiblefalse%
+ \ifx#1\pgfdecorationinputsegmentlineto%
+ \pgf@nfold@segm@visibletrue\fi%
+ \ifx#1\pgfdecorationinputsegmentcurveto%
+ \pgf@nfold@segm@visibletrue\fi%
+ \ifx#1\pgfdecorationinputsegmentclosepath%
+ \pgf@nfold@segm@visibletrue\fi%
+ \ifpgf@nfold@segm@visible%
+}
+
+
+% Joining offset lines
+% --------------------
+%
+% One of the more difficult aspects is joining the segments of an offset path. Without this step,
+% the path would be interrupted or self-intersect whenever there is a non-zero angle between
+% two segments. This code reproduces the existing line joins "bevel", "miter" and "round" as well
+% as possible.
+%
+
+\def\pgf@nfold@miterjoin{
+ % - Although it isn't obvious, this code is invariant under
+ % \pgf@nfold@deltaphi -> \pgf@nfold@deltaphi + 2pi
+ % - The tip of the miter join is computed starting from the unshifted point and then moving
+ \pgfpointadd%
+ {\pgf@decorate@inputsegment@first}%
+ {\pgfpointpolar%
+ {\pgf@nfold@cached@endangle+.5*\pgf@nfold@deltaphi+90}%
+ {\pgf@shiftdec@amount/cos(.5*\pgf@nfold@deltaphi)}%
+ }%
+ \pgfpathlineto{}%
+}
+
+\def\pgf@nfold@beveljoin{
+ % The subsequent formula needs deltaphi to be between -180 and 180
+ \pgfmathsetmacro{\pgf@nfold@deltaphi}{Mod(\pgf@nfold@deltaphi + 180, 360) - 180}
+ % For a good-looking result, inside lines should have a constant distance from each other all the way
+ % through the bevel join, which is a non-trivial condition. To achieve this result, the outer lines get
+ % a bevel-like join and the inner lines get a miter-like join; the threshold depends on the angle.
+ % We first compute by how much the outermost line must be continued from the beginning of the join.
+ % The protrusion amount must be lowered by a little bit for a rather complicated reason: The offset would be
+ % dead on if the outermost offset line were centered on the _edge_ of the wide line, but we want to draw
+ % the outside line _fully inside_ the wide line. The factor of tan(deltaphi/4) can be derived, but is not obvious.
+ \pgfmathsetmacro{\bevelouterprotrusion}{\pgf@nfold@shortenforjoin - .5*\pgflinewidth*abs(tan(.25*\pgf@nfold@deltaphi))}
+ % The following applies to middle lines only: We compute by how much they need to be shortened so the distance
+ % between the lines in the join is correct.
+ \pgfmathsetmacro{\bevelshorten}{2*\insidepercentage*abs(tan(.25*\pgf@nfold@deltaphi))}
+ % This theshold decides if the inside line is a bevel or a miter
+ \pgfmathparse{\bevelshorten < abs(sin(.5*\pgf@nfold@deltaphi))}
+ \ifnum\pgfmathresult=1\relax
+ \pgfmathsetmacro{\bevelextension}{\bevelouterprotrusion-\bevelshorten*\pgf@decoration@nfold@hwidth}
+ \pgfpointadd{\pgf@nfold@offset@start}{\pgfpointpolar{\pgf@nfold@cached@endangle}{\bevelextension}}
+ \pgfpathlineto{}
+ \pgfpointadd{\pgf@nfold@offset@end}{\pgfpointpolar{\pgfdecoratedinputsegmentstartangle}{-\bevelextension}}
+ \pgfpathlineto{}
+ \else
+ \pgf@nfold@miterjoin
+ \fi
+}
+
+\def\pgf@nfold@roundjoin{
+ % The outer half of the lines get arcs, the others get miters
+ \ifdim\insidepercentage pt<.5pt\relax
+ \pgfpointadd{\pgf@nfold@offset@start}{\pgfpointpolar{\pgf@nfold@cached@endangle}{\pgf@nfold@shortenforjoin}}
+ \pgfpathlineto{}
+ % Check if the angles are in the correct range; under some conditions we must add or subtract 360
+ \pgfmathparse{\turnindicator*(\pgfdecoratedinputsegmentstartangle-\pgf@nfold@cached@endangle)}
+ \ifdim\pgfmathresult pt>0pt\relax
+ \pgfmathsetmacro{\targetang}{\pgfdecoratedinputsegmentstartangle-\turnindicator*360}
+ \else
+ \let\targetang\pgfdecoratedinputsegmentstartangle
+ \fi
+ \pgfpatharc%
+ {\pgf@nfold@cached@endangle+90*\turnindicator}%
+ {\targetang+90*\turnindicator}%
+ {abs(\pgf@shiftdec@fraction)*\pgf@decoration@nfold@hwidth}%
+ \else
+ \pgf@nfold@miterjoin
+ \fi
+}
+
+\def\pgf@nfold@make@join{
+ % The code must be invariant under deltaphi -> deltaphi + 360, which can be verified experimentally
+ \pgfmathsetmacro{\pgf@nfold@deltaphi}{\pgfdecoratedinputsegmentstartangle-\pgf@nfold@cached@endangle}
+ % Offset the start and end of this segment
+ \pgfpointadd{\pgf@nfold@cached@endpoint}{\pgfpointpolar{\pgf@nfold@cached@endangle+90}{\pgf@shiftdec@amount}}
+ \pgfstorepoint\pgf@nfold@offset@start
+ \pgfpointadd{\pgf@nfold@segment@start}{\pgfpointpolar{\pgfdecoratedinputsegmentstartangle+90}{\pgf@shiftdec@amount}}
+ \pgfstorepoint\pgf@nfold@offset@end
+ \pgfpointdiff{\pgf@nfold@offset@start}{\pgf@nfold@offset@end}
+ % Check if the start of this segment is too close to the end of the previous segment.
+ % In that case we don't insert a join segment, as it would look rather glitchy.
+ % We use the Manhattan length for performance and to avoid 'dimension too large' errors.
+ \pgfmathparse{abs(\pgf@x) + abs(\pgf@y)}
+ \ifdim\pgfmathresult pt>0.1pt\relax
+ % First step: Check if left or right turn (-1 = left, 1=right)
+ \pgfmathsetmacro{\turnindicator}{sign(sin(\pgf@nfold@cached@endangle-\pgfdecoratedinputsegmentstartangle)}
+ % between 0 and 1; 0=no distance to cover in the join, 1=maximum distance to cover
+ \pgfmathsetmacro{\insidepercentage}{-.5*\turnindicator*\pgf@shiftdec@fraction+.5}
+ \if m\pgf@cached@linejoin % because both are only one character, \ifx is not needed
+ % miter join
+ % First we implement the miter limit: If the angle is too sharp, the miter join is replaced
+ % by a bevel join. This is controlled by /tikz/miter limit=..., initially 10.
+ \pgfmathsetmacro{\mitercosine}{abs(cos(.5*\pgf@nfold@deltaphi))}
+ \pgfmathparse{\mitercosine*\pgf@nfold@cached@miterlimit}
+ \ifdim\pgfmathresult pt>1pt\relax
+ \pgf@nfold@miterjoin
+ \else
+ \pgf@nfold@beveljoin
+ \fi
+ \else
+ \if b\pgf@cached@linejoin\relax
+ \pgf@nfold@beveljoin
+ \else
+ \if r\pgf@cached@linejoin
+ % round join
+ \pgf@nfold@roundjoin
+ \fi
+ \fi
+ \fi
+ % Common for all non-trivial joins: Connect to the starting point of the current segment.
+ % Note that this code is skipped by the global \ifdim if the start and end of the join coincide,
+ % so we never get a zero length segment here.
+ \pgfpathlineto{\pgf@nfold@offset@end}
+ \fi
+}
+
+
+%
+% Internal offset decoration
+% --------------------------
+%
+% This internal tikz decoration offsets a given path (but doesn't draw it multiple times).
+%
+
+% pgfkeys interface, used in the decorations below
+\newcount\pgf@nfold@dec@order
+\pgf@nfold@dec@order=2
+\pgfkeys{
+ % - use a counter so we get an error if something other than a number is provided
+ % - if needed, throw an error that the intersection library needs to be loaded
+ /pgf/decoration/nfold order/.code={
+ \pgf@nfold@dec@order=#1
+ \ifnum\pgf@nfold@dec@order>\tikz@arrow@intersec@numcached
+ \ifdefined\pgfintersectionofpaths\else
+ \pgferror{For 'nfold' larger than \tikz@arrow@intersec@numcached\space you need to say \string\usetikzlibrary{intersections}}
+ \fi
+ \fi
+ },
+ /pgf/decoration/nfold width/.code={\pgfmathsetlengthmacro{\pgf@decoration@nfold@hwidth}{.5*#1}},
+ % integer between 1 and \pgf@nfold@dec@order
+ /pgf/decoration/nfold index/.store in=\pgf@nfold@index
+}
+
+% In here we cache the end angle of the previous decorated segment, which is otherwise inaccessible
+\newdimen\pgf@nfold@cached@endangle
+% This stores whether the current segment should begin with a moveto to its offset
+\newif\ifpgf@nfold@continuesegment
+
+% some required computations for the current segment
+\def\pgf@nfold@shift@prepare@segment{%
+ \pgftransformreset % important! otherwise we work in the local coordinate system
+ % compute the relative deviation from the original path (between -1.0 and 1.0)
+ \pgfmathsetmacro{\pgf@shiftdec@fraction}%
+ {-1+2*(\pgf@nfold@index-1)/(\pgf@nfold@dec@order-1)}
+ \pgfmathsetlengthmacro{\pgf@shiftdec@amount}{\pgf@decoration@nfold@hwidth*\pgf@shiftdec@fraction}
+ % Set some default values; they might get overwritten later
+ \let\pgf@nfold@segment@start\pgf@decorate@inputsegment@first
+ \let\pgf@nfold@segment@end\pgf@decorate@inputsegment@last
+ % Set a default value for \ifpgf@nfold@continuesegment.
+ % It may be overwritten by \pgf@nfold@extendtotip
+ \ifx\pgfdecorationpreviousinputsegment\pgfdecorationinputsegmentmoveto
+ \pgf@nfold@continuesegmentfalse
+ \else
+ \pgf@nfold@continuesegmenttrue
+ \fi
+}
+
+% draw the current segment including a join at the start (if present)
+\def\pgf@nfold@shift@handle@segment{%
+ % In order to make space for the join, it may be necessary to shorten the current segment
+ % at the start and/or the end. In here we store by how much the segment needs to be shortened.
+ \def\pgf@nfold@shortenforjoin{0pt}
+ % Make a join only if two adjacent segments are both visible
+ \ifpgfsegmentvisible\pgfdecorationcurrentinputsegment
+ % Step 1: Make space for the join at the start if needed
+ \ifx\pgfdecorationpreviousinputsegment\pgfdecorationinputsegmentmoveto\else
+ \ifdefined\pgfdecorationpreviousinputsegment
+ % As far as I am aware, the previous input segment is either moveto or undefined
+ \pgfwarning{'nfold': Unexpected previous input segment in decoration 'nfold' (\meaning\pgfdecorationpreviousinputsegment)}
+ \fi
+ \pgf@xa=\pgfdecoratedinputsegmentstartangle pt\relax
+ \advance\pgf@xa by-\pgf@nfold@cached@endangle\relax
+ \pgfmathparse{abs(cos(0.5*\pgf@xa))}
+ \ifdim\pgfmathresult pt<0.02pt\relax
+ % we go full backwards, don't relocate the start
+ \pgfwarning{Angle too sharp in decoration 'nfold', expect visual errors}
+ \else
+ \pgfmathsetlengthmacro{\pgf@nfold@shortenforjoin}{\pgf@decoration@nfold@hwidth*abs(tan(0.5*\pgf@xa))}
+ \pgfpointadd{\pgf@decorate@inputsegment@first}{\pgfpointpolar{\pgfdecoratedinputsegmentstartangle}{\pgf@nfold@shortenforjoin}}
+ \pgfstorepoint\pgf@nfold@segment@start
+ \fi
+ \fi
+ % Step 2: Make space for the join at the end if needed
+ \ifpgfsegmentvisible\pgfdecorationnextinputsegmentobject
+ % for reasons unknown to me,
+ % \pgfdecoratedangletonextinputsegment has a "pt" but the others do not
+ \pgf@xa=\pgfdecoratedangle pt\relax
+ \advance\pgf@xa by \pgfdecoratedangletonextinputsegment\relax
+ \advance\pgf@xa by -\pgfdecoratedinputsegmentendangle pt\relax
+ \pgfmathparse{abs(cos(0.5*\pgf@xa))}
+ \ifdim\pgfmathresult pt<0.02pt\relax
+ \pgfwarning{Angle too sharp in decoration 'nfold', expect visual errors}
+ \else
+ \pgfmathparse{-\pgf@decoration@nfold@hwidth*abs(tan(0.5*\pgf@xa))}
+ \pgfpointadd{\pgf@decorate@inputsegment@last}{\pgfpointpolar{\pgfdecoratedinputsegmentendangle}{\pgfmathresult pt}}
+ \pgfstorepoint\pgf@nfold@segment@end
+ \fi
+ \fi
+ %
+ % Step 3: Draw the join at the start if applicable
+ %
+ \ifx\pgfdecorationpreviousinputsegment\pgfdecorationinputsegmentmoveto\else
+ \pgf@nfold@make@join
+ \fi
+ \fi
+ %
+ % Step 4: Draw the new segment.
+ %
+ % The value of \ifpgf@nfold@continuesegment decides whether we start with a moveto.
+ \ifx\pgfdecorationcurrentinputsegment\pgfdecorationinputsegmentlineto
+ \ifpgf@nfold@continuesegment
+ \pgfoffsetlinenomove{\pgf@nfold@segment@start}{\pgf@nfold@segment@end}{\pgf@shiftdec@amount}
+ \else
+ \pgfoffsetline{\pgf@nfold@segment@start}{\pgf@nfold@segment@end}{\pgf@shiftdec@amount}
+ \fi
+ \fi
+ \ifx\pgfdecorationcurrentinputsegment\pgfdecorationinputsegmentclosepath
+ \pgfwarning{'nfold': The option 'cycle' is not yet properly supported.}
+ \ifpgf@nfold@continuesegment
+ \pgfoffsetlinenomove{\pgf@nfold@segment@start}{\pgf@nfold@segment@end}{\pgf@shiftdec@amount}
+ \else
+ \pgfoffsetline{\pgf@nfold@segment@start}{\pgf@nfold@segment@end}{\pgf@shiftdec@amount}
+ \fi
+ \fi
+ \ifx\pgfdecorationcurrentinputsegment\pgfdecorationinputsegmentcurveto
+ \ifpgf@nfold@continuesegment
+ \pgfoffsetcurvenomove{\pgf@nfold@segment@start}{\pgf@decorate@inputsegment@supporta}{\pgf@decorate@inputsegment@supportb}{\pgf@nfold@segment@end}{\pgf@shiftdec@amount}
+ \else
+ \pgfoffsetcurve{\pgf@nfold@segment@start}{\pgf@decorate@inputsegment@supporta}{\pgf@decorate@inputsegment@supportb}{\pgf@nfold@segment@end}{\pgf@shiftdec@amount}
+ \fi
+ \fi
+ \ifx\pgfdecorationcurrentinputsegment\pgfdecorationinputsegmentlast
+ % Seems like this never happens
+ \pgfwarning{'nfold': Unexpected state (last part in segment state).}
+ \fi
+ % cache the end angle and end point because we need it on the next pass
+ \global\pgf@nfold@cached@endangle=\pgfdecoratedinputsegmentendangle pt\relax
+ \ifx\pgfdecorationnextinputsegmentobject\pgfdecorationinputsegmentmoveto\else
+ \global\let\pgf@nfold@cached@endpoint\pgf@nfold@segment@end
+ \fi
+ % Step 5: Extend into the arrow tip at the end (if present)
+ \ifx\pgfdecorationnextinputsegmentobject\pgfdecorationinputsegmentlast%
+ \pgf@nfold@check@tip@end%
+ \ifpgf@nfold@tip@implies%
+ \pgf@nfold@extendtotip{e}%
+ \fi%
+ \fi%
+}
+
+\pgfdeclaredecoration{pgf@nfold@shift}{start}{%
+ \state{start}[width=\pgfdecoratedinputsegmentremainingdistance, next state=segment]{%
+ \egroup\begingroup
+ \pgf@nfold@shift@prepare@segment
+ \pgf@nfold@check@tip@start%
+ \ifpgf@nfold@tip@implies%
+ \pgf@nfold@extendtotip{s}%
+ \fi
+ \pgf@nfold@shift@handle@segment
+ \endgroup\bgroup%
+ }
+ \state{segment}[width=\pgfdecoratedinputsegmentremainingdistance, next state=segment]{%
+ \egroup\begingroup
+ \pgf@nfold@shift@prepare@segment
+ \pgf@nfold@shift@handle@segment
+ \endgroup\bgroup%
+ }%
+}%
+
+
+
+% Internal pre-pass decoration
+% ----------------------------
+%
+% This internal decoration is run as the first step in the decoration 'nfold'.
+% It caches some data that won't be accessible later, and it also makes space for the arrow tips
+% if needed.
+%
+
+\def\pgf@nfold@storedata@handle@segment{%
+ \let\pgf@nfold@segment@end\pgf@decorate@inputsegment@last%
+ \ifx\pgfdecorationnextinputsegmentobject\pgfdecorationinputsegmentlast%
+ % Implementing shorten > and making space for the arrow tip (if present)
+ \pgf@nfold@check@tip@end%
+ \ifpgf@nfold@tip@implies%
+ \pgfmathparse{-\pgf@shorten@end@additional-2.06*\pgf@decoration@nfold@hwidth-0.5*\pgflinewidth}
+ \else
+ \pgfmathparse{-\pgf@shorten@end@additional}
+ \fi
+ % This is the point where the arrow body ends
+ \pgfpointadd%
+ {\pgf@decorate@inputsegment@last}%
+ {\pgfpointpolar{\pgfdecoratedinputsegmentendangle}{\pgfmathresult pt}}%
+ \pgfstorepoint\pgf@nfold@segment@end%
+ % This is the tip of the arrow, required for drawing the arrow head
+ \pgfpointadd%
+ {\pgf@decorate@inputsegment@last}%
+ {\pgfpointpolar%
+ {\pgfdecoratedinputsegmentendangle}%
+ {-\pgf@shorten@end@additional}}%
+ \pgfglobalstorepoint\pgf@nfold@original@last%
+ \fi%
+ \ifx\pgfdecorationcurrentinputsegment\pgfdecorationinputsegmentmoveto%
+ \pgfpathmoveto{\pgf@nfold@segment@end}%
+ \fi%
+ \ifx\pgfdecorationcurrentinputsegment\pgfdecorationinputsegmentlineto%
+ \pgfpathlineto{\pgf@nfold@segment@end}%
+ \fi%
+ \ifx\pgfdecorationcurrentinputsegment\pgfdecorationinputsegmentclosepath%
+ \pgfpathlineto{\pgf@nfold@segment@end}%
+ \fi%
+ \ifx\pgfdecorationcurrentinputsegment\pgfdecorationinputsegmentcurveto%
+ \pgfpathcurveto%
+ {\pgf@decorate@inputsegment@supporta}%
+ {\pgf@decorate@inputsegment@supportb}%
+ {\pgf@nfold@segment@end}%
+ \fi%
+}
+
+\pgfdeclaredecoration{pgf@nfold@storedata}{start}{%
+ \state{start}[width=\pgfdecoratedinputsegmentremainingdistance, next state=segment]{%
+ \egroup\begingroup
+ \pgftransformreset
+ \iftikz@mode@double\else
+ \pgferror{Must enable /tikz/double to use 'nfold' (e.g. using [double distance=5pt]).}
+ \fi
+ % Cache arrow settings
+ \pgf@nfold@storearrows
+ % Cache inner and outer line width from the '/tikz/double' settings. These are stored in
+ % the macro \tikz@double@setup. Internally, the value of \pgfinnerlinewidth determines whether
+ % double stroke is enabled or not. Calling \tikz@double@setup changes line widths globally, so
+ % encapsulating the calls in a group would still have side effects. What we do instead is call
+ % \tikz@double@setup and then restore \pgflinewidth.
+ \tikz@double@setup
+ \pgfmathsetlengthmacro{\pgf@nfold@temp@fullwidth}{.25*(\pgflinewidth+\pgfinnerlinewidth)}
+ \global\edef\pgf@decoration@nfold@hwidth{\pgf@nfold@temp@fullwidth}
+ % We can either store \pgflinewidth before calling \tikz@double@setup, or we can reconstruct
+ % its old value from the new values. We do the latter here.
+ \pgfmathparse{.5*(\pgflinewidth-\pgfinnerlinewidth)}
+ \pgfsetlinewidth{\pgfmathresult pt}
+ % shorten < and shorten > do not work well with this decoration for various reasons.
+ % We "bake" them into the path in the pre-pass and then disable them for the rendering passes.
+ \pgfpointadd%
+ {\pgf@decorate@inputsegment@first}%
+ {\pgfpointpolar{\pgfdecoratedinputsegmentstartangle}{\pgf@shorten@start@additional}}
+ % store the tip of the arrow
+ \pgfglobalstorepoint\pgf@nfold@original@first%
+ % make space if applicable
+ \pgf@nfold@check@tip@start%
+ \ifpgf@nfold@tip@implies%
+ \pgfmathparse{\pgf@shorten@start@additional+2.06*\pgf@decoration@nfold@hwidth+0.5*\pgflinewidth}
+ \else
+ \pgfmathparse{\pgf@shorten@start@additional}
+ \fi
+ \pgfpointadd%
+ {\pgf@decorate@inputsegment@first}%
+ {\pgfpointpolar{\pgfdecoratedinputsegmentstartangle}{\pgfmathresult pt}}
+ %
+ \pgfpathmoveto{}
+ \pgf@nfold@storedata@handle@segment
+ \endgroup\bgroup
+ }
+ \state{segment}[width=\pgfdecoratedinputsegmentremainingdistance, next state=segment]{%
+ \egroup\begingroup
+ \pgftransformreset
+ \pgf@nfold@storedata@handle@segment
+ \endgroup\bgroup
+ }%
+ \state{final}{}
+}%
+
+
+% Detecting arrow tips
+% --------------------
+
+% Old arrow matching
+%
+% This is the content of \pgf@arrow@tip@sequence for arrows | and Implies, respectively
+%
+%\edef\pgf@arrow@macro@verticalline{\noexpand\pgf@arrow@handle@shorthand@empty {\expandafter\noexpand\csname pgf@ar@means@|\endcsname }}
+%\def\pgf@arrow@macro@Implies{\pgf@arrow@handle {Implies}{}}
+% The next one is wrong; use \csname pgf@ar@means@tikzcd implies\endcsname
+%\def\tikzcd@arrow@implies{\pgf@arrow@handle@shorthand@empty {\pgf@ar@means@tikzcd implies }}
+
+% Intercept the arrows at definition
+\let\pgf@nfold@cachedarrows\pgfutil@empty % default value
+\let\pgf@nfold@oldsetarrows\pgfsetarrows
+\def\pgfsetarrows#1{%
+ \def\pgf@nfold@cachedarrows{#1}%
+ \pgf@nfold@oldsetarrows{#1}%
+}
+
+\let\pgf@nfold@cached@arrow@start\pgfutil@empty
+\let\pgf@nfold@cached@arrow@end\pgfutil@empty
+\def\pgf@nfold@storearrows{%
+ \ifx\pgf@nfold@cachedarrows\pgfutil@empty
+ % reset the cached values in case we don't have an arrow; this is important
+ % because the cached values are set globally
+ \global\let\pgf@nfold@cached@arrow@start\pgfutil@empty
+ \global\let\pgf@nfold@cached@arrow@end\pgfutil@empty
+ \else%
+ \expandafter\pgf@nfold@parsearrows@\pgf@nfold@cachedarrows\pgf@stop%
+ \fi%
+}
+\def\pgf@nfold@parsearrows@#1-#2\pgf@stop{%
+ % These must be set globally because \pgf@nfold@storearrows is called from within a decoration;
+ % also, this cached value must survive subsequent calls of \pgfsetarrows{} which disable arrows
+ \gdef\pgf@nfold@cached@arrow@start{#1}%
+ \gdef\pgf@nfold@cached@arrow@end{#2}%
+}
+
+% Here we match against the names of the arrows that were set in \pgfarrowsset{...}.
+% This does not respect aliases, i.e. redefining /tikzcd implies cap will not have
+% the desired effect. We may be able to fix this in the future, see below.
+\def\pgf@nfold@nonetip@i{tikzcd implies cap}
+\def\pgf@nfold@impliestip@i{Implies}
+\def\pgf@nfold@impliestip@ii{tikzcd implies}
+\def\pgf@nfold@mapstotip@i{|}
+\def\pgf@nfold@mapstotip@ii{tikzcd implies bar}
+\def\pgf@nfold@mapstotip@iii{Bar}
+\newif\ifpgf@nfold@tip@implies
+\newif\ifpgf@nfold@tip@mapsto
+
+\def\pgf@nfold@check@tip@start{%
+\pgf@nfold@check@tip{\pgf@nfold@cached@arrow@start}%
+}
+\def\pgf@nfold@check@tip@end{%
+\pgf@nfold@check@tip{\pgf@nfold@cached@arrow@end}%
+}
+
+\def\pgf@nfold@check@tip#1{%
+ \pgf@nfold@tip@impliesfalse%
+ \pgf@nfold@tip@mapstofalse%
+ \ifx#1\pgfutil@empty%
+ % no tip
+ \else
+ \ifx#1\pgf@nfold@nonetip@i%
+ % no tip
+ \else
+ \ifx#1\pgf@nfold@impliestip@i%
+ \pgf@nfold@tip@impliestrue%
+ \else
+ \ifx#1\pgf@nfold@impliestip@ii%
+ \pgf@nfold@tip@impliestrue%
+ \else
+ \ifx#1\pgf@nfold@mapstotip@i%
+ \pgf@nfold@tip@mapstotrue%
+ \else
+ \ifx#1\pgf@nfold@mapstotip@ii%
+ \pgf@nfold@tip@mapstotrue%
+ \else
+ \ifx#1\pgf@nfold@mapstotip@iii%
+ \pgf@nfold@tip@mapstotrue%
+ \else
+ \pgfwarning{'nfold': Unsupported arrow tip "#1"}
+ \fi
+ \fi
+ \fi
+ \fi
+ \fi
+ \fi
+ \fi
+}
+
+% Future: In principle, we can dereference aliases (means=...) the following way:
+%
+% \ifcsname pgf@ar@means@tikzcd implies bar\endcsname
+% \expandafter\let\expandafter\tempmacro\csname pgf@ar@means@tikzcd implies bar\endcsname
+% \pgfwarning{\meaning\tempmacro}
+% \fi
+%
+% However, the alias is not a name, but a macro, which in turn could call dereference aliases.
+% We could go down this rabbit hole in the future, but I won't do that for now.
+%
+% For now this means that redefining /tikz/commutative diagrams/tikzcd implies cap will not
+% have the desired effect.
+
+
+% Rendering arrow tips
+% --------------------
+
+% Precomputed intersections
+%
+% For arrows of order n > 2 with an Implies tip, the constituent parts of the n-fold arrow
+% end somewhere in the middle of the tip. The exact end point must be computed using
+% the intersections library. To speed up compilation times, the intersection points are precomputed
+% up to n = 5. If your document contains arrows of order 6 or larger, consider adding those
+% as well; the values are output in the log file.
+\expandafter\def\csname tikz@arrow@intersec@cache@2@3\endcsname{\pgfqpoint{2pt}{0pt}}
+\expandafter\def\csname tikz@arrow@intersec@cache@2@4\endcsname{\pgfqpoint{0.94063pt}{-0.33333pt}}
+\expandafter\def\csname tikz@arrow@intersec@cache@3@4\endcsname{\pgfqpoint{0.94063pt}{0.33333pt}}
+\expandafter\def\csname tikz@arrow@intersec@cache@2@5\endcsname{\pgfqpoint{0.64167pt}{-0.5pt}}
+\expandafter\def\csname tikz@arrow@intersec@cache@3@5\endcsname{\pgfqpoint{2pt}{0pt}}
+\expandafter\def\csname tikz@arrow@intersec@cache@4@5\endcsname{\pgfqpoint{0.64167pt}{0.5pt}}
+% intersections are precomputed up to this order
+\def\tikz@arrow@intersec@numcached{5}
+
+% Decoration for the start tip
+\pgfdeclaredecoration{pgf@nfold@tip@start}{start}{%
+ \state{start}[width=\pgfdecoratedinputsegmentremainingdistance, next state=segment]{%
+ \egroup\begingroup%
+ \pgf@nfold@check@tip@start%
+ \pgf@nfold@setup@start@tip%
+ \ifpgf@nfold@tip@implies%
+ \pgf@nfold@setupimplies%
+ \pgf@nfold@drawimplies%
+ \fi%
+ \ifpgf@nfold@tip@mapsto%
+ \pgf@nfold@setupmapsto%
+ \pgf@nfold@drawmapsto%
+ \fi%
+ \endgroup\bgroup%
+ }%
+ \state{segment}[width=\pgfdecoratedinputsegmentremainingdistance, next state=segment]{}%
+}%
+
+% Decoration for the end tip
+\pgfdeclaredecoration{pgf@nfold@tip@end}{segment}{%
+ \state{segment}[width=\pgfdecoratedinputsegmentremainingdistance, next state=segment]{}
+ \state{final}{%
+ \egroup\begingroup%
+ \pgf@nfold@check@tip@end% this step already checks for unsupported arrows
+ \pgf@nfold@setup@end@tip
+ \ifpgf@nfold@tip@implies%
+ \pgf@nfold@setupimplies%
+ \pgf@nfold@drawimplies%
+ \fi%
+ \ifpgf@nfold@tip@mapsto
+ \pgf@nfold@setupmapsto
+ \pgf@nfold@drawmapsto
+ \fi
+ \endgroup\bgroup%
+ }
+}%
+
+
+\def\pgf@nfold@setup@start@tip{
+ \pgftransformreset
+ \pgftransformshift{\pgf@nfold@original@first}
+ \pgftransformrotate{\pgfdecoratedinputsegmentstartangle}
+ \pgftransformshift{\pgfpoint{.5*\pgflinewidth}{0pt}}
+ \pgftransformscale{\pgf@decoration@nfold@hwidth}
+ \pgftransformxscale{-1.}
+}
+\def\pgf@nfold@setup@end@tip{
+ \pgftransformreset
+ \pgftransformshift{\pgf@nfold@original@last}
+ \pgftransformrotate{\pgf@nfold@cached@endangle}
+ \pgftransformshift{\pgfpoint{-.5*\pgflinewidth}{0pt}}
+ \pgftransformscale{\pgf@decoration@nfold@hwidth}
+}
+
+\def\pgf@nfold@setupimplies{%
+ \pgfsetroundjoin%
+ \pgfsetroundcap%
+}
+\def\pgf@nfold@drawimplies{
+ \pgftransformshift{\pgfqpoint{-2pt}{0pt}}
+ \pgfpathmoveto{\pgfqpoint{-1.4pt}{2.65pt}}
+ \pgfpathcurveto{\pgfqpoint{-0.75pt}{1.25pt}}{\pgfqpoint{1pt}{0.05pt}}{\pgfqpoint{2pt}{0pt}}
+ \pgfpathcurveto{\pgfqpoint{1pt}{-0.05pt}}{\pgfqpoint{-0.75pt}{-1.25pt}}{\pgfqpoint{-1.4pt}{-2.65pt}}
+}
+\def\pgf@nfold@setupmapsto{%
+ \pgfsetbuttcap%
+}
+\def\pgf@nfold@drawmapsto{
+ % Try: use the same width as the arrow head, minus the caps
+ \pgfpathmoveto{\pgfqpoint{0pt}{2.65pt}}
+ \pgfpathlineto{\pgfqpoint{0pt}{-2.65pt}}
+}
+
+% This macro extends the arrow body to the tips
+% parameter: s=start, e=end
+\def\pgf@nfold@extendtotip#1{
+ % Do not extend the arrow for index=1 and index=order, it already ends in the right place
+ \ifnum\pgf@nfold@index>1\ifnum\pgf@nfold@index<\pgf@nfold@dec@order\relax%
+ % Step 1: Find the intersection of the arrow's path with the head. This is is computationally
+ % expensive, so we first look if the value has been precomputed.
+ \ifcsname tikz@arrow@intersec@cache@\pgf@nfold@index @\the\pgf@nfold@dec@order\endcsname
+ \csname tikz@arrow@intersec@cache@\pgf@nfold@index @\the\pgf@nfold@dec@order\endcsname
+ \pgfstorepoint\pgf@nfold@arrowintersect
+ \else
+ % the intersection has not been precomputed, thus compute on the fly here
+ \pgfintersectionofpaths{
+ % specify the tip
+ \pgf@nfold@drawimplies
+ }{
+ % extend the body to intersect the tip
+ \pgfpathmoveto{\pgfqpoint{-3pt}{\pgf@shiftdec@fraction pt}}
+ \pgfpathlineto{\pgfqpoint{3pt}{\pgf@shiftdec@fraction pt}}
+ }
+ \ifnum\pgfintersectionsolutions>0
+ \pgfpointintersectionsolution{1}
+ \pgfstorepoint\pgf@nfold@arrowintersect
+ \makeatother
+ \typeout{tikz-nfold: computed intersection cache@\pgf@nfold@index @\the\pgf@nfold@dec@order: \string\pgfqpoint{\the\pgf@x}{\the\pgf@y}^^J}
+ \makeatletter
+ \else
+ % this is a failsafe and should never be reached
+ \pgfwarning{'nfold': did not find intersection}
+ \pgfqpoint{0pt}{\pgf@shiftdec@fraction pt}
+ \pgfstorepoint\pgf@nfold@arrowintersect
+ \fi
+ \fi% if precomputed
+ % Step 2: Extend the arrow body to the intersection point.
+ % If the tip is at the beginning of the path, we have to move to the intersection
+ % and then draw a line to the "regular" starting point. The subsequent segment then
+ % should omit its moveto.
+ % If the tip is at the end, we are already in the right position and only need to extend
+ % the current path to the intersection point.
+ \pgftransformreset
+ \if#1s
+ \pgftransformshift{\pgf@nfold@original@first}
+ \pgftransformrotate{\pgfdecoratedinputsegmentstartangle}
+ \pgftransformxscale{-1}
+ \else
+ \pgftransformshift{\pgf@nfold@original@last}
+ \pgftransformrotate{\pgfdecoratedinputsegmentendangle}
+ \fi
+ \pgftransformshift{\pgfpoint{-2*\pgf@decoration@nfold@hwidth-.5*\pgflinewidth}{0pt}}
+ \pgftransformscale{\pgf@decoration@nfold@hwidth}
+ \if#1s
+ \pgfpathmoveto{\pgf@nfold@arrowintersect}
+ % This is precisely the start of the body, shifted vertically
+ \pgfpathlineto{\pgfqpoint{-0.06pt}{\pgf@shiftdec@fraction pt}}
+ \pgf@nfold@continuesegmenttrue
+ \else\if#1e
+ \pgfpathlineto{\pgf@nfold@arrowintersect}
+ \else
+ \pgferror{Invalid argument to \string\pgf@nfold@extendtotip: \meaning#1}
+ \fi\fi
+ \pgftransformreset
+\fi\fi% if 1 < i < nArrows
+}
+
+
+%
+% n-fold decoration
+% -----------------
+%
+% This decoration is intended to be used by the end user. It replaces a given path by n parallel versions of the path.
+
+\newcount\tikz@nfold@loop@index
+\def\tikz@nfold@run@loop#1{%
+ \edef\tikz@nfold@postaction{%
+ \tikz@nfold@postaction% this already includes a comma
+ postaction={%
+ draw,arrows=-,shorten <=0pt,shorten >=0pt,% disable shorten, is implemented in the pre-pass
+ decorate,decoration={%
+ pgf@nfold@shift, nfold order=#1, nfold index=\the\tikz@nfold@loop@index%
+ }%
+ },%
+ }
+ \advance\tikz@nfold@loop@index by -1\relax
+ \ifnum\tikz@nfold@loop@index>0%
+ \tikz@nfold@run@loop{#1}%
+ \fi%
+}
+\tikzset{
+ nfold/.code={
+ \tikzset{draw=none}
+ \pgf@nfold@dec@order=#1\relax
+ \ifnum\pgf@nfold@dec@order<2
+ \pgferror{'nfold' must take a value of at least 2, got \the\pgf@nfold@dec@order}
+ \fi
+ \tikz@nfold@loop@index=\pgf@nfold@dec@order
+ % The final style has the following form:
+ % [draw=none,
+ % postaction={
+ % draw=none,decorate,decoration=pgf@nfold@storedata,
+ % postaction={draw,arrows=-,decorate,decoration={pgf@nfold@shift,nfold order=#1,nfold index=1}},
+ % [...],
+ % postaction={draw,arrows=-,decorate,decoration={pgf@nfold@shift,nfold order=#1,nfold index=#1}}
+ % }
+ % ]
+ % We build the postactions piece by piece in the macro \tikz@nfold@postaction. Then we apply it
+ % using \tikzset and .expand once, so potential future changes to \tikz@nfold@postaction do not
+ % affect the settings in tikz.
+ %
+ \def\tikz@nfold@postaction{}
+ \tikz@nfold@run@loop{#1}
+ \edef\tikz@nfold@postaction{%
+ draw=none,decorate,decoration=pgf@nfold@storedata,%
+ \tikz@nfold@postaction%
+ postaction={
+ draw,arrows=-,shorten <=0pt,shorten >=0pt,
+ decorate,decoration=pgf@nfold@tip@start},%
+ postaction={
+ draw,arrows=-,shorten <=0pt,shorten >=0pt,
+ decorate,decoration=pgf@nfold@tip@end},%
+ }
+ \tikzset{postaction/.expand once=\tikz@nfold@postaction}
+ },
+ nfold/.default=2
+}
+
+\endinput
diff --git a/Master/tlpkg/bin/tlpkg-ctan-check b/Master/tlpkg/bin/tlpkg-ctan-check
index 2b63d2ea95f..d813c89fa1c 100755
--- a/Master/tlpkg/bin/tlpkg-ctan-check
+++ b/Master/tlpkg/bin/tlpkg-ctan-check
@@ -811,7 +811,7 @@ my @TLP_working = qw(
tikz-cd tikz-dependency tikz-dimline tikz-ext
tikz-feynhand tikz-feynman tikz-imagelabels tikz-inet
tikz-kalender tikz-karnaugh tikz-ladder tikz-lake-fig tikz-layers
- tikz-mirror-lens tikz-nef tikz-network tikz-opm tikz-optics
+ tikz-mirror-lens tikz-nef tikz-network tikz-nfold tikz-opm tikz-optics
tikz-page tikz-palattice tikz-planets tikz-qtree
tikz-relay tikz-sfc tikz-swigs tikz-timing tikz-trackschematic tikz-truchet
tikzbricks tikzcodeblocks tikzducks tikzfill tikzinclude tikzlings
diff --git a/Master/tlpkg/tlpsrc/collection-pictures.tlpsrc b/Master/tlpkg/tlpsrc/collection-pictures.tlpsrc
index 51a992a86f1..b2c8ffc1fd7 100644
--- a/Master/tlpkg/tlpsrc/collection-pictures.tlpsrc
+++ b/Master/tlpkg/tlpsrc/collection-pictures.tlpsrc
@@ -186,6 +186,7 @@ depend tikz-layers
depend tikz-mirror-lens
depend tikz-nef
depend tikz-network
+depend tikz-nfold
depend tikz-opm
depend tikz-optics
depend tikz-page
diff --git a/Master/tlpkg/tlpsrc/tikz-nfold.tlpsrc b/Master/tlpkg/tlpsrc/tikz-nfold.tlpsrc
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/Master/tlpkg/tlpsrc/tikz-nfold.tlpsrc