summaryrefslogtreecommitdiff
path: root/graphics/pgf/contrib/spath3
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /graphics/pgf/contrib/spath3
Initial commit
Diffstat (limited to 'graphics/pgf/contrib/spath3')
-rw-r--r--graphics/pgf/contrib/spath3/README9
-rw-r--r--graphics/pgf/contrib/spath3/calligraphy_doc.pdfbin0 -> 288184 bytes
-rw-r--r--graphics/pgf/contrib/spath3/calligraphy_doc.tex258
-rw-r--r--graphics/pgf/contrib/spath3/knots_doc.pdfbin0 -> 384979 bytes
-rw-r--r--graphics/pgf/contrib/spath3/knots_doc.tex484
-rw-r--r--graphics/pgf/contrib/spath3/spath3.dtx4228
-rw-r--r--graphics/pgf/contrib/spath3/spath3_code.pdfbin0 -> 439282 bytes
7 files changed, 4979 insertions, 0 deletions
diff --git a/graphics/pgf/contrib/spath3/README b/graphics/pgf/contrib/spath3/README
new file mode 100644
index 0000000000..a9d1fe6210
--- /dev/null
+++ b/graphics/pgf/contrib/spath3/README
@@ -0,0 +1,9 @@
+----------------------------------------------------------------
+spath3 --- LaTeX3 functions for manipulating PGF soft paths
+E-mail: stacey@math.ntnu.no
+Released under the LaTeX Project Public License v1.3c or later
+See http://www.latex-project.org/lppl.txt
+----------------------------------------------------------------
+
+This package defines some functions used to manipulate PGFs soft paths.
+As applications of its use, included are a package for drawing calligraphic paths and a package for drawing knot diagrams.
diff --git a/graphics/pgf/contrib/spath3/calligraphy_doc.pdf b/graphics/pgf/contrib/spath3/calligraphy_doc.pdf
new file mode 100644
index 0000000000..12a0313d8a
--- /dev/null
+++ b/graphics/pgf/contrib/spath3/calligraphy_doc.pdf
Binary files differ
diff --git a/graphics/pgf/contrib/spath3/calligraphy_doc.tex b/graphics/pgf/contrib/spath3/calligraphy_doc.tex
new file mode 100644
index 0000000000..c1b88c9e8e
--- /dev/null
+++ b/graphics/pgf/contrib/spath3/calligraphy_doc.tex
@@ -0,0 +1,258 @@
+\immediate\write18{tex spath3.dtx}
+\documentclass{ltxdoc}
+\usepackage[T1]{fontenc}
+\usepackage{trace}
+\usepackage{lmodern}
+\usepackage{morefloats}
+\usepackage{tikz}
+\usetikzlibrary{decorations.pathreplacing,calligraphy,matrix}
+\usepackage[numbered]{hypdoc}
+\definecolor{lstbgcolor}{rgb}{0.9,0.9,0.9}
+
+\usepackage{listings}
+\lstloadlanguages{[LaTeX]TeX}
+\lstset{breakatwhitespace=true,breaklines=true,language=TeX}
+
+\usepackage{fancyvrb}
+
+\newenvironment{example}
+ {\VerbatimEnvironment
+ \begin{VerbatimOut}{example.out}}
+ {\end{VerbatimOut}
+ \begin{center}
+ \setlength{\parindent}{0pt}
+ \fbox{\begin{minipage}{.9\linewidth}
+ \lstset{breakatwhitespace=true,breaklines=true,language=TeX,basicstyle=\small}
+ \lstinputlisting[]{example.out}
+ \end{minipage}}
+
+ \fbox{\begin{minipage}{.9\linewidth}
+ \input{example.out}
+ \end{minipage}}
+\end{center}
+}
+
+\providecommand*{\url}{\texttt}
+\GetFileInfo{spath3.sty}
+
+\title{The \textsf{calligraphy} Package: Documentation}
+\author{Andrew Stacey \\ \url{stacey@math.ntnu.no}}
+\date{\fileversion~from \filedate}
+
+\begin{document}
+
+\maketitle
+% \section{Introduction}
+The \Verb+calligraphy+ TikZ library is designed to enable calligraphic style drawings in TikZ.
+The idea is to be able to ``stroke'' a line with a ``pen''.
+As a simple example, compare the two lines in the following picture.
+
+\begin{example}
+\begin{center}
+\begin{tikzpicture}
+\pen (-135:.25) -- (45:.25);
+\draw[line width=.5cm] (0,0) .. controls +(45:1) and +(-135:1) .. ++(3,0);
+\calligraphy (0,-1) .. controls +(45:1) and +(-135:1) .. ++(3,0);
+\end{tikzpicture}
+\end{center}
+\end{example}
+
+The paths are identical in definition but the first is drawn using the standard TikZ path with a line width of .5cm.
+The second is ``stroked'' with a calligraphic pen of width .5cm angled at 45 degrees.
+\section{How It Works}
+To know how to use this library, it is worth knowing a little about how it works.
+A ``pen'' is a path, as is the line that is the template for the pen stroke.
+The two paths are joined together to form a region which is filled.
+Thus in constructing the example given in the introduction, the following path is built.
+\begin{center}
+\begin{tikzpicture}
+\draw (0,-1) .. controls +(45:1) and +(-135:1) .. node (op) {} ++(3,0) -- node (pp) {} ++(45:.5) .. controls +(-135:1) and +(45:1) .. node (opr) {} ++(-3,0) -- node (ppr) {} (0,-1);
+\draw[<-] (op) -- ++(0,-.5) node[anchor=north] {original path};
+\draw[<-] (pp) -- ++(.5,0) node[anchor=west] {pen path};
+\draw[<-] (opr) -- ++(0,.5) node[anchor=south] {original path reversed};
+\draw[<-] (ppr) -- ++(-.5,0) node[anchor=east] {pen path reversed};
+\end{tikzpicture}
+\end{center}
+What is important to note about this is that the ``pen'' isn't \emph{actually} dragged along the path, it is merely a simulation.
+This can be shown with the following simple example.
+The first is a continuous path that goes past the angle of the pen and thus the upstroke would involve pushing the pen.
+The second is how it is meant to be done, the second line is drawn from top to bottom.
+However, as the direction of the path isn't important, the same effect can be obtained by ``lifting the nib'' between the lines.
+\begin{example}
+\begin{center}
+\begin{tikzpicture}
+\pen (-135:.125) -- (45:.125);
+\calligraphy (0,0) -- (1,0) -- (1,1);
+\calligraphy (2,0) -- (3,0) (3,1) -- (3,0);
+\calligraphy (4,0) -- (5,0) +(0,0) -- (5,1);
+\end{tikzpicture}
+\end{center}
+\end{example}
+It should work as expected providing the following golden rule is not violated:
+\begin{quotation}
+Never \emph{push} a calligraphic pen.
+\end{quotation}
+This is good advice for ordinary calligraphy as well, so a path that is realisable as an honest calligraphic path should be fine with this library.
+Actually since, as remarked above, the direction of the path isn't important, a more accurate golden rule would be that one should never swap from pushing to pulling or vice versa without lifting the pen off the paper; but that isn't as succinct.
+The paths for both pens and templates can be reasonably complicated.
+They can contain gaps, but should not contain closed paths, nor rectangles.
+The implementation works by breaking a path into its constituent pieces (broken up by ``move to''s) and working bit by bit.
+\begin{example}
+\begin{center}
+\begin{tikzpicture}
+\pen (-135:.25) -- (-135:.125) (45:.125) -- (45:.25);
+\calligraphy (0,0) .. controls +(45:1) and +(-135:1) .. +(3,0) ++(1.5,0) .. controls +(-135:2) and +(45:2) .. +(0,-4) (0,-4) .. controls +(45:1) and +(-135:1) .. +(3,0);
+\end{tikzpicture}
+\end{center}
+\end{example}
+\section{Copperplate}
+Copperplate pens are somewhat special.
+They are ``thin'' so don't need the same treatment as a ``thick'' pen, but one should be able to vary the pressure with a copperplate pen to get a variation of thickness.
+Specifying a copperplate pen is straightforward: it is a pen with no thickness.
+\begin{example}
+\begin{center}
+\begin{tikzpicture}[line width=2pt]
+\pen (0,0);
+\calligraphy[heavy] (0,0) .. controls +(45:1) and +(-135:1) .. +(3,0) ++(1.5,0) .. controls +(-135:2) and +(45:2) .. +(0,-3) (0,-3) .. controls +(45:1) and +(-135:1) .. +(3,0);
+\calligraphy[light] (4,0) .. controls +(45:1) and +(-135:1) .. +(3,0) ++(1.5,0) .. controls +(-135:2) and +(45:2) .. +(0,-3) (4,-3) .. controls +(45:1) and +(-135:1) .. +(3,0);
+\end{tikzpicture}
+\end{center}
+\end{example}
+With a copperplate pen, the segments of a path are tapered.
+Copperplate and normal pens can be mixed.
+Any part of the pen specification that has no length is treated as a copperplate pen.
+\begin{example}
+\begin{center}
+\begin{tikzpicture}[line width=1pt]
+\pen (-135:.125) -- (0,0) (45:.125);
+\calligraphy (0,0) .. controls +(45:1) and +(-135:1) .. +(3,0) ++(1.5,0) .. controls +(-135:2) and +(45:2) .. +(0,-3) (0,-3) .. controls +(45:1) and +(-135:1) .. +(3,0);
+\end{tikzpicture}
+\end{center}
+\end{example}
+%% \section{Annotations}
+%% As an addition, calligraphic paths can be annotated to show how they were constructed.
+%% A path with an arrow is drawn at a set offset from the last part of the path.
+%% \begin{example}
+%% \begin{center}
+%% \begin{tikzpicture}[line width=1pt]
+%% \pen (-135:.125) -- (0,0) (45:.125);
+%% \calligraphy[annotate] (0,0) .. controls +(45:1) and +(-135:1) .. +(3,0) ++(1.5,0) .. controls +(-135:2) and +(45:2) .. +(0,-3) (0,-3) .. controls +(45:1) and +(-135:1) .. +(3,0);
+%% \end{tikzpicture}
+%% \end{center}
+%% \end{example}
+\section{Style Options}
+There are plenty of options for styling the paths and pens.
+\subsection{Definition Options}
+Internally, making a pen is a two-step process.
+First a pen has to be \emph{defined} and then \emph{processed}.
+To define a pen, the user has to specify a path.
+That path is stored in a global macro and so can be accessed in throughout the document.
+However, before being used, the pen has to be processed.
+At this stage, the pen is converted from a macro in to a special object.
+These special objects are local and cannot (at present) be made global.
+Thus whilst a pen can be \emph{defined} inside a group, the \emph{processing} stage has to happen in the outermost group in which the pen is going to be used.
+There is a shortcut command that (via a bit of suspicious hackery) does all this within a \Verb+tikzpicture+ group.
+However, if a pen is to be used in several different pictures, it must be processed outside the group in which it is defined.
+The following macros and keys are used to set up and use a pen.
+%All the keys are in the \Verb+/pgf/calligraphy+ family.
+\begin{itemize}
+\item \DescribeMacro{define pen}If the \Verb+define pen+ key is specified on a path then that path will be used to define a pen.
+It can take one option which will be the pen name, if not specified then \Verb+default+ is assumed.
+The resulting path will not be counted for bounding box considerations.
+When the pen is used, the origin will correspond to the path along which it is dragged.
+\item \DescribeMacro{pen name}The key \Verb+pen name=name+ sets the name for the current pen.
+This can be used either when defining or using a pen.
+\item \DescribeMacro{\pen}\DescribeMacro{\definepen}The macros \Verb+\pen+ and \Verb+\definepen+ are analogous to the TikZ commands \Verb+\draw+ or \Verb+\fill+ in that they act like a path command but store the path as a pen.
+The difference between them is that \Verb+\definepen+ is to be used outside a TikZ picture (it contains its own \Verb+\tikz+ command) and \Verb+\pen+ inside.
+%% These macros also change the ``key family'' to \Verb+/pgf/calligraphy+ so that any further options do not need their full path.
+%% \item \DescribeMacro{\processpen}The macro \Verb+\processpen+ processes a pen so that it is set up for using later on.
+%% As remarked earlier, it has to be given at least at the outermost grouping of where it will be used.
+%% It takes an optional argument which is the pen name.
+%% This is only needed if the pen will be used in a group other than the one in which it is defined (or if the hackery that means this isn't needed with the \Verb+\pen+ command fails for some unknown reason).
+\item \DescribeMacro{use pen}The key \Verb+use pen=name+ on a path means that that path should be ``stroked'' with the pen (\Verb+default+ if no name is given, or none specified via the \Verb+pen name+ key).
+%% \item \DescribeMacro{\calligraphy}The macro \Verb+\calligraphy+ is a shortcut for specifying a path with the key \Verb+use pen+ already set.
+%% This macro also changes the ``key family'' to \Verb+/pgf/calligraphy+ so that any further options do not need their full path.
+\end{itemize}
+\subsection{Style Options}
+There are various options available for styling the calligraphic paths.
+%% All of these are in the \Verb+/pgf/calligraphy+ key family, but the \Verb+\calligraphy+ command automatically switches to that family so the key path is not needed.
+%% Any unknown keys are processed as follows:
+%% \begin{enumerate}
+%% \item A check is made to see if a pen of that name has been defined.
+%% If so, that pen is used.
+%% This is similar to the \Verb+\node[shape]+ syntax
+%% \item A check is made to see if the unknown key corresponds to a colour.
+%% If so, that colour is installed as the pen colour.
+%% This is similar to the colour handling of TikZ paths.
+%% (In fact, it uses the same code.)
+%% \item All other unknown options are passed on to TikZ.
+%% \end{enumerate}
+The style options are as follows.
+\begin{itemize}
+\item \DescribeMacro{pen colour}The \Verb+pen colour+ style defines the default colour to be used.
+Since calligraphic paths are sometimes filled and sometimes stroked, this ensures that the colour is used correctly.
+\item \DescribeMacro{nib style}It is possible to style particular ``nibs'' (i.e., segments of the pen path) separately.
+This is the \Verb+nib style+ option, which takes two arguments.
+The first is the index of the part of the nib and the second is the style options to be applied.
+\item \DescribeMacro{stroke style}It is also possible to style particular parts of the template path.
+One way to do this is to use the \Verb+stroke style+ key, which takes two arguments.
+The first is the index of the part of the stroke and the second is the style options to be applied.
+\item \DescribeMacro{this stroke style}It is also possible to style particular parts of the template path as the path is constructed.
+This is done by putting \Verb+[this stroke style={}]+ in the template path at the relevant part.
+The style is saved and applied to that segment of the template path.
+\item \DescribeMacro{taper}The tapering of copperplate paths can be controled by the \Verb+taper+ option.
+It takes arguments \Verb+none+, \Verb+both+, \Verb+start+, and \Verb+end+.
+\item \DescribeMacro{weight}\DescribeMacro{heavy}\DescribeMacro{light}Copperplate paths come in two ``weights'': \emph{heavy} and \emph{light}.
+The weight also affects the tapering: by default a light path is tapered to nothing whilst a heavy path is tapered to the width of a light path.
+Weights can be specified by either \Verb+weight=weight+ or just \Verb+heavy+ and \Verb+light+.
+It is possible to change the weight for different components of a path using the \Verb+stroke style+ key.
+With tapering, this means that one can easily vary from a light stroke to a heavy one.
+The relevant widths are controlled by the keys \DescribeMacro{heavy line width}\Verb+heavy line width+ and the \DescribeMacro{light line width}\Verb+light line width+.
+The \DescribeMacro{taper line width}\Verb+taper line width+, is set automatically by the weight but can be altered afterwards using the \Verb+taper line width+ key.
+%% \item \DescribeMacro{annotate}The key \Verb+annotate+ switches on annotations.
+%% The style of the annotations is controlled by the keys \DescribeMacro{annotation style}\Verb+annotation style+, \DescribeMacro{annotation shift}\Verb+annotation shift+ which controls how the annotation path is shifted from the final component of the calligraphic path, \DescribeMacro{annotation nodes style}\Verb+annotation nodes style+ for global style of the nodes, \DescribeMacro{annotation node style}\Verb+annotation node style+ for styling of particular nodes.
+%% This latter takes two options: the node number and the style to be applied.
+%% The annotation path and annotation node styles take ordinary TikZ styling options.
+\end{itemize}
+\begin{example}
+\begin{center}
+\begin{tikzpicture}
+\calligraphy[pen colour=green,nib style={2}{color=red}] (0,0) .. controls +(45:1) and +(-135:1) .. +(3,0) ++(1.5,0) .. controls +(-135:2) and +(45:2) .. +(0,-3) (0,-3) .. controls +(45:1) and +(-135:1) .. +(3,0);
+\calligraphy[line width=1pt] (0,-4) .. controls +(45:1) and +(-135:1) .. +(3,0) ++(1.5,0) .. controls +(-135:2) and +(45:2) .. +(0,-3) (0,-7) .. controls +(45:1) and +(-135:1) .. +(3,0);
+\end{tikzpicture}
+\end{center}
+\end{example}
+\section{Decorations}
+If a TikZ/PGF decorations library is loaded prior to this library, then the \Verb+calligraphy+ library defines some decorations that use the calligraphic paths, specifically with the copperplate nib.
+The current decorations are:
+\begin{itemize}
+\item \DescribeMacro{calligraphic brace}\Verb+calligraphic brace+ for a brace.
+\item \DescribeMacro{calligraphic straight parenthesis}\Verb+calligraphic straight parenthesis+ for a parenthesis with straight middle component.
+\item \DescribeMacro{calligraphic curved parenthesis}\Verb+calligraphic curved parenthesis+ for a parenthesis with a curved middle component.
+\end{itemize}
+All the above use the \Verb+amplitude+ option to specify their size.
+The following is an example of their use, together with the standard \Verb+brace+ and the \Verb+delimiter+ key from the \Verb+matrix+ library for comparison.
+\begin{example}
+\begin{center}
+\begin{tikzpicture}
+\draw[decorate,decoration={calligraphic brace,amplitude=4mm},ultra thick] (0,0) -- (0,8);
+\draw[line width=2pt,decorate,decoration={brace,amplitude=10},line cap=round] (1,0) -- ++(0,8);
+\node[anchor=south west,minimum height=8cm,outer sep=0pt,left delimiter=\{] (a) at (2,0) {};
+\draw[decorate,decoration={calligraphic straight parenthesis,amplitude=4mm},ultra thick] (3,0) -- ++(0,8);
+\draw[decorate,decoration={calligraphic curved parenthesis,amplitude=4mm},ultra thick] (4,0) -- ++(0,8);
+\node[anchor=south west,minimum height=8cm,outer sep=0pt,left delimiter=(] (a) at (5,0) {};
+\end{tikzpicture}
+\end{center}
+\end{example}
+\section{Pre-Defined Pens}
+The following pens are predefined:
+\begin{itemize}
+\item \DescribeMacro{copperplate}\Verb+copperplate+:
+\begin{example}
+\begin{center}
+\tikz \calligraphy[copperplate] (0,0) .. controls +(1,-1) and +(-1,1) .. ++(3,0) [this stroke style={light,taper=start}] +(0,0) .. controls +(1,-1) and +(-1,1) .. ++(3,0) [this stroke style={heavy}] +(0,0) .. controls +(1,-1) and +(-1,1) .. ++(3,0) [this stroke style={light,taper=end}];
+\end{center}
+\end{example}
+\end{itemize}
+
+\end{document} \ No newline at end of file
diff --git a/graphics/pgf/contrib/spath3/knots_doc.pdf b/graphics/pgf/contrib/spath3/knots_doc.pdf
new file mode 100644
index 0000000000..d818161e21
--- /dev/null
+++ b/graphics/pgf/contrib/spath3/knots_doc.pdf
Binary files differ
diff --git a/graphics/pgf/contrib/spath3/knots_doc.tex b/graphics/pgf/contrib/spath3/knots_doc.tex
new file mode 100644
index 0000000000..67c23ca128
--- /dev/null
+++ b/graphics/pgf/contrib/spath3/knots_doc.tex
@@ -0,0 +1,484 @@
+\immediate\write18{tex spath3.dtx}
+\documentclass{ltxdoc}
+\usepackage[T1]{fontenc}
+\usepackage{trace}
+\usepackage{lmodern}
+\usepackage{morefloats}
+\usepackage{tikz}
+\usetikzlibrary{knots}
+\usepackage[numbered]{hypdoc}
+\definecolor{lstbgcolor}{rgb}{0.9,0.9,0.9}
+
+\usepackage{listings}
+\lstloadlanguages{[LaTeX]TeX}
+\lstset{breakatwhitespace=true,breaklines=true,language=TeX}
+
+\usepackage{fancyvrb}
+
+\newenvironment{example}
+ {\VerbatimEnvironment
+ \begin{VerbatimOut}{example.out}}
+ {\end{VerbatimOut}
+ \begin{center}
+ \setlength{\parindent}{0pt}
+ \fbox{\begin{minipage}{.9\linewidth}
+ \lstset{breakatwhitespace=true,breaklines=true,language=TeX,basicstyle=\small}
+ \lstinputlisting[]{example.out}
+ \end{minipage}}
+
+ \fbox{\begin{minipage}{.9\linewidth}
+ \centering
+ \input{example.out}
+ \end{minipage}}
+\end{center}
+}
+
+\providecommand*{\url}{\texttt}
+\GetFileInfo{spath3.sty}
+
+\title{The \textsf{knots} Package: Documentation}
+\author{Andrew Stacey \\ \url{stacey@math.ntnu.no}}
+\date{\fileversion~from \filedate}
+
+\begin{document}
+
+\maketitle
+
+\section{Introduction}
+
+The \Verb+knots+ package is a TikZ library for drawing knot (and similar) diagrams.
+It provides a few useful styles and node shapes but its main contribution is the \Verb+knot+ environment.
+The \Verb+knot+ environment allows you to draw some strands of a knot (or braid or tangle or whatever -- we shall use the imprecise term ``knot'' to refer to any similar diagram) and then to modify the crossings via a simple interface.
+
+The main part of this package was developed in response to a question on the \href{http://tex.stackexchange.com}{TeX-SX} site by Jamie Vicary.
+The original question was \href{http://tex.stackexchange.com/q/32125/86}{Braid diagrams in TikZ}.
+Jamie's comment (quoted below) was the inspiration for the mechanism of the \Verb+knot+ environment.
+
+\begin{quotation}
+[It] would be really cool if it was possible to draw the curves, let TikZ calculate all the intersection points automatically, and then tell it to redraw the intersections according to an under/over specification... do you think this is in the realm of plausibility?
+\end{quotation}
+
+\section{Examples}
+
+Let us begin with an example.
+To use the library, simply load the \Verb+tikz+ package and add \Verb+knots+ to the list of TikZ libraries that you load.
+For example, the following in your preamble would work:
+
+\begin{verbatim}
+\usepackage{tikz}
+\usetikzlibrary{knots}
+\end{verbatim}
+
+Let's draw a simple tangle (actually a braid).
+
+\begin{example}
+\begin{tikzpicture}
+\draw[red,ultra thick] (0,0) .. controls +(1,0) and +(-1,0) .. (2,1) .. controls +(1,0) and +(-1,0) .. (4,0);
+\draw[blue,ultra thick] (0,1) .. controls +(1,0) and +(-1,0) .. (2,0) .. controls +(1,0) and +(-1,0) .. (4,1);
+\end{tikzpicture}
+\end{example}
+
+Now a common way to draw crossings for knots is to draw a gap in the under strand through which the over strand passes.
+One way to achieve this in TikZ is to draw the over strand twice, the first time with a thicker line width and the colour of the background.
+We'll draw it twice, once with background a different colour to illustrate this.
+
+\begin{example}
+\begin{tikzpicture}
+\draw[red,ultra thick] (0,0) .. controls +(1,0) and +(-1,0) .. (2,1) .. controls +(1,0) and +(-1,0) .. (4,0);
+\draw[pink,double=blue,ultra thick,double distance=1.6pt] (0,1) .. controls +(1,0) and +(-1,0) .. (2,0) .. controls +(1,0) and +(-1,0) .. (4,1);
+\draw[xshift=5cm,red,ultra thick] (0,0) .. controls +(1,0) and +(-1,0) .. (2,1) .. controls +(1,0) and +(-1,0) .. (4,0);
+\draw[xshift=5cm,white,double=blue,ultra thick,double distance=1.6pt] (0,1) .. controls +(1,0) and +(-1,0) .. (2,0) .. controls +(1,0) and +(-1,0) .. (4,1);
+\end{tikzpicture}
+\end{example}
+
+Now the problem with this method is that there is no way to draw the red and blue paths so that the blue is the over strand at the first crossing and the red at the second.
+Either the blue path is always on top (as shown) or the red.
+One way to resolve this is to split the paths and draw one of them in segments:
+
+\begin{example}
+\begin{tikzpicture}
+\draw[red,ultra thick] (0,0) .. controls +(1,0) and +(-1,0) .. (2,1);
+\draw[white,double=blue,ultra thick,double distance=1.6pt] (0,1) .. controls +(1,0) and +(-1,0) .. (2,0) .. controls +(1,0) and +(-1,0) .. (4,1);
+\draw[white,double=red,double distance=1.6pt,ultra thick] (2,1) .. controls +(1,0) and +(-1,0) .. (4,0);
+\end{tikzpicture}
+\end{example}
+
+Another method (employed by the \Verb+braids+ package) is to break the under path either side of the crossing and not draw it there.
+This means that the order of drawing doesn't matter.
+
+Both of these methods have their drawbacks (particularly for general knots as opposed to the more structured braids) in that they require a detailed knowledge of the pieces of the paths and the positions of the crossings.
+As pointed out by Jamie Vicary in the above-{}quoted comment, TikZ should be able to compute these itself.
+
+That's what this package does.
+
+Let's do the above example using this package.
+The main changes to the drawing are that we use the command \Verb+\strand+ rather than \Verb+\draw+ and we enclose it in the \Verb+knot+ environment.
+When initially drawing the strands it is useful to provide the option \Verb+draft mode=strands+.
+That's because the detailed computation can take a little time and so it is best only to do it when necessary.
+So on first run through we get the following.
+
+\begin{example}
+\begin{tikzpicture}
+\begin{knot}[
+ draft mode=strands
+]
+\strand[red,thick] (0,0) .. controls +(1,0) and +(-1,0) .. (2,1) .. controls +(1,0) and +(-1,0) .. (4,0);
+\strand[blue,thick] (0,1) .. controls +(1,0) and +(-1,0) .. (2,0) .. controls +(1,0) and +(-1,0) .. (4,1);
+\end{knot}
+\end{tikzpicture}
+\end{example}
+
+Once we're happy with the positioning of the strands, we change the option \Verb+draft mode=strands+ to \Verb+draft mode=crossings+.
+
+\begin{example}
+\begin{tikzpicture}
+\path (2,1.5) (2,-.5);
+\begin{knot}[
+ draft mode=crossings,
+ clip width=5,
+]
+\strand[red,ultra thick] (0,0) .. controls +(1,0) and +(-1,0) .. (2,1) .. controls +(1,0) and +(-1,0) .. (4,0);
+\strand[blue,ultra thick] (0,1) .. controls +(1,0) and +(-1,0) .. (2,0) .. controls +(1,0) and +(-1,0) .. (4,1);
+\end{knot}
+\end{tikzpicture}
+\end{example}
+
+The \Verb+\path (2,1.5) (2,-.5);+ is to extend the bounding box of the picture a little upwards.
+The extra pieces are not used when computing the bounding box of the picture so that it doesn't change position on the page.
+
+The extra information is that the strands and the crossings have been numbered.
+The crossings have also been rendered (the \Verb+clip width+ option enlarges the crossing gap to make it more obvious).
+Unfortunately, for both crossings the red path is on top.
+We need to flip one of these crossings (the first).
+To do this, we either use the command \Verb+flipcrossings+ or the \Verb+flip crossing+ key.
+
+\begin{example}
+\begin{tikzpicture}
+\path (2,1.5) (2,-.5);
+\begin{knot}[
+ draft mode=crossings,
+ clip width=5,
+ flip crossing=1,
+]
+\strand[red,ultra thick] (0,0) .. controls +(1,0) and +(-1,0) .. (2,1) .. controls +(1,0) and +(-1,0) .. (4,0);
+\strand[blue,ultra thick] (0,1) .. controls +(1,0) and +(-1,0) .. (2,0) .. controls +(1,0) and +(-1,0) .. (4,1);
+\end{knot}
+\end{tikzpicture}
+\end{example}
+
+Once we're happy with it, we remove the \Verb+draft mode+ option to render it in its final form.
+
+\begin{example}
+\begin{tikzpicture}
+\begin{knot}[
+ clip width=5,
+ flip crossing=1,
+]
+\strand[red,ultra thick] (0,0) .. controls +(1,0) and +(-1,0) .. (2,1) .. controls +(1,0) and +(-1,0) .. (4,0);
+\strand[blue,ultra thick] (0,1) .. controls +(1,0) and +(-1,0) .. (2,0) .. controls +(1,0) and +(-1,0) .. (4,1);
+\end{knot}
+\end{tikzpicture}
+\end{example}
+
+Here's a more complicated example.
+
+\begin{example}
+\begin{tikzpicture}
+\node (A) at (0,4) [draw,minimum width=30pt,minimum height=10pt,thick] {};
+\begin{knot}[
+ clip width=5,
+ clip radius=8pt,
+]
+\strand [thick,only when rendering/.style={dashed}] (0,0)
+to [out=up, in=down] (-1,1)
+to [out=up, in=down] (0,2)
+to [out=up, in=down] (-1.2,4)
+to [out=up, in=down, looseness=0.7] (0,5.5)
+to [out=up, in=down] (-2,7);
+\strand [thick] (-1,0)
+to [out=up, in=down] (1,2)
+to [out=up, in=down] (A.south);
+\strand [thick,blue] (1,0)
+to [out=up, in=down] (-1,2)
+to [out=up, in=down] (1.5,4)
+to [out=up, in=right] (0,5.5)to [out=left, in=up] (-2,4)
+to [out=down, in=up] (-2,0);
+\strand [thick] (A.150)
+to [out=up, in=down] (0.7,5.5)
+to [out=up, in=down] (0,7);
+\strand [thick] (A.30)
+to [out=up, in=down] (-1,6)
+to [out=up, in=down] (2,7);
+\flipcrossings{6,2,9,5,11}
+\end{knot}
+\end{tikzpicture}
+\end{example}
+
+One feature about this example is the \Verb+only when rendering+ key.
+The gaps are made by drawing the strand again with extra thickness in the background colour.
+If the \Verb+dashed+ option were always in play for that strand, the gap would be dashed which would spoil the effect.
+So the \Verb+only when rendering+ key gathers those options (such as a dash pattern) which should only be applied to the rendered strand and not to the redraw that creates the gap.
+
+Here's another example.
+
+\begin{example}
+\newcommand{\motif}[1]{
+ to ++(180+#1:0.50) arc (270+#1:150+#1:0.15)
+ to ++( 60+#1:0.50) arc (-30+#1:150+#1:0.15)
+ to ++(240+#1:0.25) arc (150+#1:330+#1:0.25)
+ to ++( 60+#1:0.55) arc (150+#1: 30+#1:0.20)
+}
+\newcommand{\celticknot}{\motif{0}\motif{120}\motif{240}}
+\begin{tikzpicture}
+\begin{knot}[
+ line width=2pt,
+ line join=round,
+ clip width=2,
+ scale=5,
+ consider self intersections,
+ ignore endpoint intersections=false,
+ background color=white,
+ only when rendering/.style={
+ draw=red,
+ double=white,
+ double distance=6pt,
+ line cap=round,
+ }
+]
+\strand (0,0) \celticknot;
+\flipcrossings{1,3,6,8,10}
+\end{knot}
+\end{tikzpicture}
+\end{example}
+
+In this case the strand is a single path.
+In the standard case crossings are only considered between separate strands (since the algorithm used by TikZ means that a strand intersects itself infinitely often).
+The key \Verb+consider self intersections+ gets round this by ``exploding'' the strand into segments and considering each as a separate path with regard to finding the intersections.
+
+A path consists of a series of lines and B\'ezier cubics.
+The ``explosion'' of a path uses this decomposition.
+Unfortunately, even that is not always enough as it is possible for a B\'ezier cubic to self-intersect.
+The \Verb+consider self intersections+ also splits these B\'ezier curves in two to ensure that this doesn't happen\footnote{Computing when this is strictly necessary is difficult so it splits more than it needs to to ensure that enough are done.}.
+To disable this, use the \Verb+consider self intersections=no splits+ option.
+This is the recommended option.
+
+\begin{example}
+\begin{tikzpicture}
+\begin{knot}[
+ consider self intersections,
+ draft mode=crossings,
+]
+\strand (0,0) .. controls +(3,1) and +(-3,1) .. (1,0);
+\end{knot}
+\begin{knot}[
+ xshift=3cm,
+ consider self intersections=no splits,
+ draft mode=crossings,
+]
+\strand (0,0) .. controls +(3,1) and +(-3,1) .. (1,0);
+\end{knot}
+\end{tikzpicture}
+\end{example}
+
+Finally, given that TikZ has to do some heavy computation to find the intersections, it is worth considering using the \Verb+external+ library of TikZ to avoid having to do this on every run.
+
+\section{Usage}
+
+\subsection{The \texttt{knot} Environment}
+
+\DescribeMacro{knot}
+\DescribeMacro{\strand}
+This package provides a \Verb+knot+ environment for including in a \Verb+tikzpicture+ to render a knot.
+This takes an optional argument which is passed to \Verb+\tikzset+ and can be used to configure the knot.
+Within that environment, specific strands are defined using the \Verb+\strand+ command (in place of a \Verb+\path+ or \Verb+\draw+).
+Further keys can be specified on the strands.
+
+\DescribeMacro{flip crossing}
+\DescribeMacro{\flipcrossings}
+Specifying the crossings to be flipped can be done either using the key \Verb+flip crossing+ or the macro \Verb+\flipcrossings+.
+The latter can take a comma separated list of crossings to flip.
+The former takes a single crossing but can be extended to a comma separated list using the \Verb+.list+ handler as in \Verb+flip crossing/.list={1,2,3}+ (this is what \Verb+\flipcrossings+ does internally).
+
+\DescribeMacro{\redraw}
+There is also a macro \Verb+\redraw+ which redraws a strand in the neighbourhood of a point.
+This is effectively what happens for the crossings and can be used to fix something that wasn't done correctly by the main algorithm.
+It takes two arguments, the strand number and the point at which to render the strand, as in \Verb+\redraw{2}{(1,1)}+.
+
+\subsection{Keys}
+
+The various keys are as follows.
+The majority of the keys are in the \Verb+/tikz/knot diagram+ family, but it does its best to pass unknown keys down to \Verb+/tikz/+.
+The keys processed by the \Verb+knot+ environment are automatically in this family but the keys processed by the \Verb+\strand+ command are not.
+If a standard key (in the option to the \Verb+knot+ environment) doesn't work, try prefixing it with \Verb+/tikz/+ or \Verb+/pgf/+.
+
+\begin{itemize}
+\item \DescribeMacro{every knot diagram}
+The style \Verb+every knot diagram+ is executed at the start of the \Verb+knot+ environment.
+
+Note that it is inside the \Verb+knot diagram+ family so if setting it outside (say, in the preamble) use \Verb+\tikzset{knot diagram/every knot diagram}+.
+
+\item \DescribeMacro{name}
+The crossings of a knot are given coordinates of the form \Verb+<name> <number>+.
+The default name is \Verb+knot+.
+The \Verb+name+ key renames it.
+
+\item \DescribeMacro{every strand}
+The contents of \Verb+every strand+ are applied to every strand.
+By default this contains the \Verb+draw+ key so if resetting it you should probably ensure that it still has the \Verb+draw+ key.
+
+Note that it is inside the \Verb+knot diagram+ family so if setting it outside (say, in the preamble) use \Verb+\tikzset{knot diagram/every knot diagram}+.
+
+\item \DescribeMacro{only when rendering}
+The key \Verb+only when rendering={<style>}+ is applied to the strand only when it is actually drawn and not when it is used to cut out part of the underlying path.
+
+Note that there are actually two versions of this key: one in the \Verb+knot diagram+ family and one in the \Verb+/tikz+ family.
+This is so that it works equally well in the argument to the \Verb+knot+ environment and the \Verb+\strand+ command.
+
+\item \DescribeMacro{ignore endpoint intersections}
+When a strand is split into pieces then the intersection library finds ``false positives'' in that neighbouring pieces intersect at their endpoints.
+The code ignores such intersections between neighbouring pieces.
+The \Verb+ignore endpoint intersections+ (which is true by default) means that all intersections that are near an endpoint are ignored whether or not they are with the next or previous piece of that strand.
+The celtic knot example of the previous section sets this to false to ensure that it gets all intersections.
+
+\item \DescribeMacro{consider self intersections}
+The key \Verb+consider self intersections=<option>+ handles the splitting facility so that strands can self intersect.
+The options are \Verb+true+, \Verb+false+, and \Verb+no splits+.
+It is \Verb+false+ at the start, and the default option is \Verb+true+.
+The difference between \Verb+true+ and \Verb+no splits+ is as to whether segments are further split to avoid \emph{all} self intersections.
+
+\item \DescribeMacro{clip radius}
+The \Verb+clip radius=<length>+ is the radius of the clip region around each crossing.
+
+\item \DescribeMacro{end tolerance}
+The \Verb+end tolerance=<length>+ is the distance at which an intersection is considered as being near an endpoint (for simplicity, it uses the \(\ell^1\)--metric).
+
+\item \DescribeMacro{clip width}
+The \Verb+clip width=<factor>+ is the multiplier for the thickness of the ``wipeout'' path relative to the line width of the actual path.
+
+\item \DescribeMacro{flip crossing}
+The \Verb+flip crossing=<number>+ key flips the \Verb+<number>+ crossing.
+
+\item \DescribeMacro{background colour} \DescribeMacro{background color}
+The keys \Verb+background colour=<colour>+ and \Verb+background color=<color>+ set the background colour for the crossings.
+
+\item \DescribeMacro{intersection <number>}
+The style in \Verb+intersection <number>+ is applied just before that crossing is drawn so can be used to modify the crossing style just for that crossing.
+
+
+\item \DescribeMacro{draft mode}
+The key \Verb+draft mode=<option>+ sets the different styles for aiding with rendering the knot.
+The options are \Verb+strands+, \Verb+crossings+, or \Verb+off+.
+The \Verb+strands+ option just renders the strands with no crossings.
+The \Verb+crossings+ option renders the crossings and labels the strands and crossings.
+The \Verb+off+ option renders the crossings without the labels.
+
+\item \DescribeMacro{draft/crossing label}
+The style \Verb+draft/crossing label+ is applied to each of the crossing labels.
+The labels are actually \Verb+pin+s attached to coordinates at the crossings.
+This style is applied to the \Verb+pin+ itself.
+The default is:
+
+\begin{lstlisting}
+overlay,
+fill=white,
+fill opacity=.5,
+text opacity=1,
+text=blue,
+pin edge={blue,<-}
+\end{lstlisting}
+
+\item \DescribeMacro{draft/strand label}
+The style \Verb+draft/strand label+ is applied to each of the strand labels.
+The default is:
+
+\begin{lstlisting}
+overlay,
+circle,
+draw=purple,
+fill=white,
+fill opacity=.5,
+text opacity=1,
+text=purple,
+inner sep=0pt
+\end{lstlisting}
+\end{itemize}
+
+\subsection{Other Styles}
+
+The other things defined by this package are for drawing knot diagrams when the user knows in advance either the locations of the crossings or can arrange that the paths are drawn in the correct order.
+In these circumstances the \Verb+knot+ environment is overkill.
+
+\DescribeMacro{knot}
+\DescribeMacro{knot gap}
+This \Verb+knot=<colour>+ style sets up a doubled path with inner colour the given colour (or the current draw colour if not given) and outer colour the knot background colour.
+The width of the inner line is the current line width and the full width is controlled by the \Verb+knot gap=<factor>+ key which is initially set to \(3\) (thus giving a line's width either side).
+
+\begin{example}
+\begin{tikzpicture}[knot gap=7]
+\draw[thin,knot=red] (-1,-1) -- (1,1);
+\draw[thin,knot=red] (-1,1) -- (1,-1);
+\begin{scope}[xshift=2.5cm]
+\draw[knot=red] (-1,-1) -- (1,1);
+\draw[knot=red] (-1,1) -- (1,-1);
+\end{scope}
+\begin{scope}[xshift=5cm]
+\draw[thick,knot=red] (-1,-1) -- (1,1);
+\draw[thick,knot=red] (-1,1) -- (1,-1);
+\end{scope}
+\end{tikzpicture}
+\end{example}
+
+\DescribeMacro{knot crossing}
+\DescribeMacro{knot over cross}
+\DescribeMacro{knot under cross}
+\DescribeMacro{knot horiz}
+\DescribeMacro{knot vert}
+This package also defines some node shapes to help draw knot and link diagrams.
+The idea with these is to place a node of the appropriate type at each crossing and then link them accordingly.
+The node shapes are \Verb+knot crossing+, \Verb+knot over cross+, \Verb+knot under cross+, \Verb+knot vert+, \Verb+knot horiz+.
+The two styles \Verb+knot over cross+ and \Verb+knot under cross+ are crossings, \Verb+knot vert+ and \Verb+knot horiz+ are for when resolving the crossings in a diagram.
+By judicious use of the \Verb+\foreach+ command, a family of resolved link diagrams can be produced.
+
+\begin{example}
+\begin{tikzpicture}[every node/.style={draw,red}]
+\node[knot over cross] at (1,0) {};
+\node[knot under cross] at (2,0) {};
+\node[knot vert] at (3,0) {};
+\node[knot horiz] at (4,0) {};
+\end{tikzpicture}
+\end{example}
+
+The node \Verb+knot crossing+ is not meant to be drawn, it is an empty shape.
+Its value is in that it defines more anchors than the usual rectangle shape.
+For each of the 8 main compass directions, it defines anchors at 2, 4, 8, 16, and 32 times further out.
+This can be useful for designing curves that enter and exit the crossing gracefully at particular directions.
+When using this node shape, the crossing itself is easiest to draw by using the \Verb+center+ anchor for the strands that form the over cross.
+
+\begin{example}
+\begin{tikzpicture}[every path/.style={red,thick}, every node/.style={transform shape, knot crossing, inner sep=1.5pt}]
+\node[rotate=45] (tl) at (-1,1) {};
+\node[rotate=-45] (tr) at (1,1) {};
+\node (m) at (0,-1) {};
+\node (b) at (0,-2) {};
+\draw (b) .. controls (b.4 north west) and (m.4 south west) .. (m.center);
+\draw (b.center) .. controls (b.4 north east) and (m.4 south east) .. (m);
+\draw (m) .. controls (m.8 north west) and (tl.3 south west) .. (tl.center);
+\draw (m.center) .. controls (m.8 north east) and (tr.3 south east) .. (tr);
+\draw (tl.center) .. controls (tl.16 north east) and (tr.16 north west) .. (tr);
+\draw (b) .. controls (b.16 south east) and (tr.16 north east) .. (tr.center);
+\draw (b.center) .. controls (b.16 south west) and (tl.16 north west) .. (tl);
+\draw (tl) -- (tr.center);
+\end{tikzpicture}
+\end{example}
+
+\section{Other Relevant Packages by the Same Author}
+
+Another tool for drawing knot diagrams is provided by the \Verb+hobby+ package which draws smooth curves through a prescribed set of points.
+This can be combined with the facilities of this package but also has some features of its own that make it suitable for drawing knot diagrams.
+
+For braids themselves, there is the \Verb+braids+ package which allows input specification in the form of a word in the braid group.
+
+\end{document}
+% Local Variables:
+% tex-output-type: "pdf18"
+% End:
diff --git a/graphics/pgf/contrib/spath3/spath3.dtx b/graphics/pgf/contrib/spath3/spath3.dtx
new file mode 100644
index 0000000000..9d6ae2825d
--- /dev/null
+++ b/graphics/pgf/contrib/spath3/spath3.dtx
@@ -0,0 +1,4228 @@
+% \iffalse meta-comment
+%<*internal>
+\iffalse
+%</internal>
+%<*readme>
+----------------------------------------------------------------
+spath3 --- LaTeX3 functions for manipulating PGF soft paths
+E-mail: stacey@math.ntnu.no
+Released under the LaTeX Project Public License v1.3c or later
+See http://www.latex-project.org/lppl.txt
+----------------------------------------------------------------
+
+This package defines some functions used to manipulate PGFs soft paths.
+As applications of its use, included are a package for drawing calligraphic paths and a package for drawing knot diagrams.
+%</readme>
+%<*internal>
+\fi
+\def\nameofplainTeX{plain}
+\ifx\fmtname\nameofplainTeX\else
+ \expandafter\begingroup
+\fi
+%</internal>
+%<*install>
+\input docstrip.tex
+\keepsilent
+\askforoverwritefalse
+\preamble
+----------------------------------------------------------------
+spath3 --- Functions for manipulating PGF soft paths
+E-mail: stacey@math.ntnu.no
+Released under the LaTeX Project Public License v1.3c or later
+See http://www.latex-project.org/lppl.txt
+----------------------------------------------------------------
+
+\endpreamble
+\postamble
+
+Copyright (C) 2011-2019 by Andrew Stacey <stacey@math.ntnu.no>
+
+This work may be distributed and/or modified under the
+conditions of the LaTeX Project Public License (LPPL), either
+version 1.3c of this license or (at your option) any later
+version. The latest version of this license is in the file:
+
+http://www.latex-project.org/lppl.txt
+
+This work is "maintained" (as per LPPL maintenance status) by
+Andrew Stacey.
+
+This work consists of the files spath3.dtx
+ calligraphy_doc.tex
+ knots_doc.tex
+and the derived files spath3.ins,
+ spath3_code.pdf,
+ spath3.sty,
+ tikzlibrarycalligraphy.code.tex
+ tikzlibraryknots.code.tex
+ calligraphy.pdf
+ knots.pdf
+ README
+
+\endpostamble
+\usedir{tex/latex/spath3}
+\generate{
+ \file{\jobname.sty}{\from{\jobname.dtx}{spath3}}
+}
+\generate{
+ \file{tikzlibrarycalligraphy.code.tex}{\from{\jobname.dtx}{calligraphy}}
+}
+\generate{
+ \file{tikzlibraryknots.code.tex}{\from{\jobname.dtx}{knots}}
+}
+%</install>
+%<install>\endbatchfile
+%<*internal>
+\usedir{source/latex/spath3}
+\generate{
+ \file{\jobname.ins}{\from{\jobname.dtx}{install}}
+}
+\nopreamble\nopostamble
+\usedir{doc/latex/demopkg}
+\generate{
+ \file{README.txt}{\from{\jobname.dtx}{readme}}
+}
+\ifx\fmtname\nameofplainTeX
+ \expandafter\endbatchfile
+\else
+ \expandafter\endgroup
+\fi
+%</internal>
+%<*driver>
+\documentclass[full]{l3doc}
+\usepackage[T1]{fontenc}
+\usepackage{lmodern}
+%\usepackage{morefloats}
+\usepackage{tikz}
+\usepackage{trace}
+\usepackage{spath3}
+%\traceoff
+%\usepackage[numbered]{hypdoc}
+\definecolor{lstbgcolor}{rgb}{0.9,0.9,0.9}
+
+\usepackage{listings}
+\lstloadlanguages{[LaTeX]TeX}
+\lstset{breakatwhitespace=true,breaklines=true,language=TeX}
+
+\usepackage{fancyvrb}
+
+\newenvironment{example}
+ {\VerbatimEnvironment
+ \begin{VerbatimOut}[gobble=2]{example.out}}
+ {\end{VerbatimOut}
+ \begin{center}
+% \setlength{\parindent}{0pt}
+ \fbox{\begin{minipage}{.9\linewidth}
+ \lstset{breakatwhitespace=true,breaklines=true,language=TeX,basicstyle=\small}
+ \lstinputlisting[]{example.out}
+ \end{minipage}}
+
+ \fbox{\begin{minipage}{.9\linewidth}
+ \input{example.out}
+ \end{minipage}}
+\end{center}
+}
+\EnableCrossrefs
+\CodelineIndex
+\RecordChanges
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \CheckSum{4488}
+%
+% \CharacterTable
+% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
+% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
+% Digits \0\1\2\3\4\5\6\7\8\9
+% Exclamation \! Double quote \" Hash (number) \#
+% Dollar \$ Percent \% Ampersand \&
+% Acute accent \' Left paren \( Right paren \)
+% Asterisk \* Plus \+ Comma \,
+% Minus \- Point \. Solidus \/
+% Colon \: Semicolon \; Less than \<
+% Equals \= Greater than \> Question mark \?
+% Commercial at \@ Left bracket \[ Backslash \\
+% Right bracket \] Circumflex \^ Underscore \_
+% Grave accent \` Left brace \{ Vertical bar \|
+% Right brace \} Tilde \~}
+%
+%
+% \changes{1.0}{2011/05/03}{Converted to DTX file}
+% \changes{1.1}{2016/02/19}{Fixes due to changes in LaTeX3}
+% \changes{1.2}{2019/02/12}{More fixes due to changes in LaTeX3}
+%
+% \DoNotIndex{\newcommand,\newenvironment}
+%
+% \providecommand*{\url}{\texttt}
+% \GetFileInfo{spath3.sty}
+% \title{The \textsf{spath3} package}
+% \author{Andrew Stacey \\ \url{stacey@math.ntnu.no}}
+% \date{\fileversion~from \filedate}
+%
+%
+% \maketitle
+%
+%
+% \section{Introduction}
+%
+% The \Verb+spath3+ package is intended as a library for manipulating PGF's \emph{soft paths}.
+% In between defining a path and using it, PGF stores a path as a \emph{soft path} where all the defining structure has been resolved into the basic operations but these have not yet been written to the output file.
+% They can therefore still be manipulated by \TeX, and as they have a very rigid form (and limited vocabulary), they are relatively easy to modify.
+% This package provides some methods for working with these paths.
+% It is not really intended for use by end users but as a foundation on which other packages can be built.
+% As examples, the \Verb+calligraphy+ package and the \Verb+knot+ package are included.
+% The first of these simulates a calligraphic pen stroking a path.
+% The second can be used to draw knot (and similar) diagrams.
+%
+% The format of a soft path is a sequence of triples of the form \Verb+\macro {dimension}{dimension}+.
+% The macro is one of a short list, the dimensions are coordinates in points.
+% There are certain further restrictions, particularly that every path must begin with a \Verb+move to+, and B\'ezier curves consist of three triples.
+%
+% \StopEventually{}
+%
+% \section{Implementation}
+%
+% \iffalse
+%<*spath3>
+% \fi
+% \subsection{Initialisation}
+%
+% Load the \LaTeX3 foundation and register us as a \LaTeX3\ package.
+% \begin{macrocode}
+\NeedsTeXFormat{LaTeX2e}
+\RequirePackage{expl3}
+\RequirePackage{pgf}
+\ProvidesExplPackage {spath3} {2019/02/12} {1.2} {Functions for
+manipulating PGF soft paths}
+\RequirePackage{xparse}
+% \end{macrocode}
+%
+% We need a slew of temporary variables.
+% \begin{macrocode}
+\tl_new:N \l__spath_tmpa_tl
+\tl_new:N \l__spath_tmpb_tl
+\tl_new:N \l__spath_tmpc_tl
+\tl_new:N \l__spath_tmpd_tl
+\tl_new:N \l__spath_smuggle_tl
+\dim_new:N \l__spath_tmpa_dim
+\dim_new:N \l__spath_tmpb_dim
+\fp_new:N \l__spath_tmpa_fp
+\fp_new:N \l__spath_tmpb_fp
+\int_new:N \l__spath_tmpa_int
+\int_new:N \g__spath_map_int
+% \end{macrocode}
+%
+% We need to be able to compare against the macros that can occur in a soft path so these token lists contain them.
+% \begin{macrocode}
+\tl_new:N \g__spath_moveto_tl
+\tl_new:N \g__spath_lineto_tl
+\tl_new:N \g__spath_curveto_tl
+\tl_new:N \g__spath_curvetoa_tl
+\tl_new:N \g__spath_curvetob_tl
+\tl_new:N \g__spath_closepath_tl
+\tl_set:Nn \g__spath_moveto_tl {\pgfsyssoftpath@movetotoken}
+\tl_set:Nn \g__spath_lineto_tl {\pgfsyssoftpath@linetotoken}
+\tl_set:Nn \g__spath_curveto_tl {\pgfsyssoftpath@curvetotoken}
+\tl_set:Nn \g__spath_curvetoa_tl {\pgfsyssoftpath@curvetosupportatoken}
+\tl_set:Nn \g__spath_curvetob_tl {\pgfsyssoftpath@curvetosupportbtoken}
+\tl_set:Nn \g__spath_closepath_tl {\pgfsyssoftpath@closepathtoken}
+% \end{macrocode}
+%
+% \subsection{Basic Structure and Methods}
+%
+% A soft path is a \Verb+prop+.
+% These are lists of the attributes that we define.
+% The first consists of all attributes, the second of those that are ``moveable'' in the sense that they change if we transform the path, the third are the ones that contain actual paths.
+%
+% Note that if using these attributes outside an \Verb+expl3+ context, the spaces should be omitted.
+% \begin{macrocode}
+\tl_new:N \g__spath_attributes
+\tl_new:N \g__spath_moveable_attributes
+\tl_new:N \g__spath_path_attributes
+\tl_set:Nn \g__spath_attributes {
+ {path}
+ {reverse path}
+ {length}
+ {real length}
+ {number of components}
+ {initial point}
+ {final point}
+ {initial action}
+ {final action}
+ {min bb}
+ {max bb}
+}
+\tl_set:Nn \g__spath_moveable_attributes {
+ {initial point}
+ {final point}
+ {min bb}
+ {max bb}
+}
+\tl_set:Nn \g__spath_path_attributes {
+ {path}
+ {reverse path}
+}
+% \end{macrocode}
+%
+% An \Verb+spath+ object is actually a \Verb+prop+.
+% The following functions are wrappers around the underlying \Verb+prop+ functions.
+% We prefix the names to avoid clashing with other \Verb+prop+s that might be lying around, this is why all the \Verb+spath+ methods take argument \Verb+:n+ and not \Verb+:N+.
+% Given that \Verb+spath+ objects might be created inside a group but used outside it, we work globally throughout.
+% \begin{macro}{\spath_new:n}
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_new:n #1
+{
+ \prop_new:c {l__spath_#1}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_clear:n}
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_clear:n #1
+{
+ \prop_gclear:c {l__spath_#1}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_clear_new:n}
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_clear_new:n #1
+{
+ \prop_gclear_new:c {l__spath_#1}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_show:n}
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_show:n #1
+{
+ \prop_show:c {l__spath_#1}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_put:nnn}
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_put:nnn #1#2#3
+{
+ \prop_gput:cnn {l__spath_#1} {#2} {#3}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_remove:nn}
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_remove:nn #1#2
+{
+ \prop_gremove:cn {l__spath_#1} {#2}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\__spath_get:nn}
+% This function is an internal one since the real \Verb+get+ function will generate its data if it does not already exist.
+% \begin{macrocode}
+\cs_new_nopar:Npn \__spath_get:nn #1#2
+{
+ \prop_item:cn {l__spath_#1} {#2}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\__spath_get:nnN}
+% \begin{macrocode}
+\cs_new_nopar:Npn \__spath_get:nnN #1#2#3
+{
+ \prop_get:cnN {l__spath_#1} {#2} #3
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_if_in:nn}
+% \begin{macrocode}
+\prg_new_conditional:Npnn \spath_if_in:nn #1#2 {p, T, F, TF}
+{
+ \prop_if_in:cnTF {l__spath_#1} {#2}
+ { \prg_return_true: }
+ { \prg_return_false: }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\__spath_get:nnN}
+% \begin{macrocode}
+\cs_generate_variant:Nn \__prop_split:NnTF {cnTF}
+\prg_new_protected_conditional:Npnn \__spath_get:nnN #1#2#3 {T, F, TF}
+{
+ \__prop_split:cnTF {l__spath_#1} {#2}
+ {
+ \tl_set:Nn #3 {##2}
+ \prg_return_true:
+ }
+ { \prg_return_false: }
+}
+\cs_generate_variant:Nn \spath_put:nnn {nnV, nnx, nno}
+\cs_generate_variant:Nn \__spath_get:nn {Vn}
+\cs_generate_variant:Nn \__spath_get:nnN {VnN}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_if_exist:n}
+% \begin{macrocode}
+\prg_new_conditional:Npnn \spath_if_exist:n #1 {p,T,F,TF}
+{
+ \prop_if_exist:cTF {l__spath_#1}
+ {
+ \prg_return_true:
+ }
+ {
+ \prg_return_false:
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_clone:nn}
+% Clones an \Verb+spath+.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_clone:nn #1 #2
+{
+ \spath_clear_new:n {#2}
+ \tl_map_inline:Nn \g__spath_attributes
+ {
+ \spath_if_in:nnT {#1} {##1}
+ {
+ \__spath_get:nnN {#1} {##1} \l__spath_tmpa_tl
+ \spath_put:nnV {#2} {##1} \l__spath_tmpa_tl
+ }
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_get_current_path:n}
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \spath_get_current_path:n #1
+{
+ \pgfsyssoftpath@getcurrentpath\l__spath_tmpa_tl
+ \spath_clear_new:n {#1}
+ \spath_put:nnV {#1} {path} \l__spath_tmpa_tl
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_set_current_path:n}
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \spath_set_current_path:n #1
+{
+ \spath_get:nnN {#1} {min bb} \l__spath_tmpa_tl
+ \exp_last_unbraced:NV \pgf@protocolsizes\l__spath_tmpa_tl
+
+ \spath_get:nnN {#1} {max bb} \l__spath_tmpa_tl
+ \exp_last_unbraced:NV \pgf@protocolsizes\l__spath_tmpa_tl
+
+ \spath_get:nnN {#1} {path} \l__spath_tmpa_tl
+ \pgfsyssoftpath@setcurrentpath\l__spath_tmpa_tl
+ \pgfsyssoftpath@flushcurrentpath
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_use_path:nn}
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \spath_use_path:nn #1#2
+{
+ \spath_set_current_path:n {#1}
+ \pgfusepath{#2}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_protocol_path:n}
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \spath_protocol_path:n #1
+{
+ \spath_get:nnN {#1} {min bb} \l__spath_tmpa_tl
+ \exp_last_unbraced:NV \pgf@protocolsizes\l__spath_tmpa_tl
+
+ \spath_get:nnN {#1} {max bb} \l__spath_tmpa_tl
+ \exp_last_unbraced:NV \pgf@protocolsizes\l__spath_tmpa_tl
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_tikz_path:nn}
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \spath_tikz_path:nn #1#2
+{
+ \path[#1] \pgfextra{
+ \spath_get:nnN {#2} {path} \l__spath_tmpa_tl
+ \pgfsyssoftpath@setcurrentpath \l__spath_tmpa_tl
+ };
+}
+\cs_generate_variant:Nn \spath_tikz_path:nn {Vn}
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Computing Information}
+%
+% \begin{macro}{\spath_get:nn}
+% The information that we store along with a soft path can be computed from it, but computing it every time is wasteful.
+% So this is the real \Verb+\spath_get:nn+ function which checks to see if we have already computed it and then either retrieves it or computes it.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_get:nn #1#2
+{
+ \spath_if_in:nnF {#1} {#2}
+ {
+ \cs_if_exist_use:cT {spath_generate_#2:n} {{#1}}
+ }
+ \__spath_get:nn {#1} {#2}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_get:nnN}
+% As above but leaves the result in a token list rather than in the stream.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_get:nnN #1#2#3
+{
+ \spath_if_in:nnF {#1} {#2}
+ {
+ \cs_if_exist_use:cT {spath_generate_#2:n} {{#1}}
+ }
+ \__spath_get:nnN {#1} {#2} #3
+}
+\cs_generate_variant:Nn \spath_get:nnN {VnN}
+% \end{macrocode}
+% \end{macro}
+%
+% The next slew of functions generate data from the original path, storing it in the \Verb+prop+ for further retrieval.
+% \begin{macro}{\spath_generate_length:n}
+% Counts the number of triples in the path.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_generate_length:n #1
+{
+ \__spath_get:nnN {#1} {path} \l__spath_tmpa_tl
+ \spath_put:nnx {#1} {length} {\int_eval:n {\tl_count:N \l__spath_tmpa_tl /3 }}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_generate_reallength:n}
+% The real length of a path is the number of triples that actually draw something (that is, the number of lines and curves).
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_generate_reallength:n #1
+{
+ \__spath_get:nnN {#1} {path} \l__spath_tmpa_tl
+ \int_set:Nn \l__spath_tmpa_int {0}
+ \tl_map_inline:Nn \l__spath_tmpa_tl {
+ \tl_if_eq:nnT {##1} {\pgfsyssoftpath@linetotoken}
+ {
+ \int_incr:N \l__spath_tmpa_int
+ }
+ \tl_if_eq:nnT {##1} {\pgfsyssoftpath@curvetotoken}
+ {
+ \int_incr:N \l__spath_tmpa_int
+ }
+ }
+ \spath_put:nnx {#1} {real length} {\int_use:N \l__spath_tmpa_int}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_generate_numberofcomponents:n}
+% A component is a continuous segment of the path, separated by moves.
+% Successive moves are not collapsed, and zero length moves count.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_generate_numberofcomponents:n #1
+{
+ \__spath_get:nnN {#1} {path} \l__spath_tmpa_tl
+ \int_set:Nn \l__spath_tmpa_int {0}
+ \tl_map_inline:Nn \l__spath_tmpa_tl {
+ \tl_if_eq:nnT {##1} {\pgfsyssoftpath@movetotoken}
+ {
+ \int_incr:N \l__spath_tmpa_int
+ }
+ }
+ \spath_put:nnx {#1} {number of components} {\int_use:N \l__spath_tmpa_int}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_generate_initialpoint:n}
+% The starting point of the path.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_generate_initialpoint:n #1
+{
+ \__spath_get:nnN {#1} {path} \l__spath_tmpa_tl
+ \tl_clear:N \l__spath_tmpb_tl
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \tl_put_right:Nx \l__spath_tmpb_tl {{\tl_head:N \l__spath_tmpa_tl}}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \tl_put_right:Nx \l__spath_tmpb_tl {{\tl_head:N \l__spath_tmpa_tl}}
+ \spath_put:nnV {#1} {initial point} \l__spath_tmpb_tl
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_generate_finalpoint:n}
+% The final point of the path.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_generate_finalpoint:n #1
+{
+ \tl_clear:N \l__spath_tmpb_tl
+ \spath_if_in:nnTF {#1} {reverse path}
+ {
+ \__spath_get:nnN {#1} {reverse path} \l__spath_tmpa_tl
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \tl_put_right:Nx \l__spath_tmpb_tl {{\tl_head:N \l__spath_tmpa_tl}}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \tl_put_right:Nx \l__spath_tmpb_tl {{\tl_head:N \l__spath_tmpa_tl}}
+ }
+ {
+ \__spath_get:nnN {#1} {path} \l__spath_tmpa_tl
+ \tl_reverse:N \l__spath_tmpa_tl
+ \tl_put_left:Nx \l__spath_tmpb_tl {{\tl_head:N \l__spath_tmpa_tl}}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \tl_put_left:Nx \l__spath_tmpb_tl {{\tl_head:N \l__spath_tmpa_tl}}
+ }
+ \spath_put:nnV {#1} {final point} \l__spath_tmpb_tl
+}
+\cs_generate_variant:Nn \spath_generate_finalpoint:n {V}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_generate_reversepath:n}
+% This computes the reverse of the path.
+% TODO: handle closed paths, possibly rectangles.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_generate_reversepath:n #1
+{
+ \__spath_get:nnN {#1} {path} \l__spath_tmpa_tl
+ \tl_clear:N \l__spath_tmpb_tl
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \dim_set:Nn \l__spath_tmpa_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \dim_set:Nn \l__spath_tmpb_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \tl_put_left:Nx \l__spath_tmpb_tl
+ {
+ {\dim_use:N \l__spath_tmpa_dim}
+ {\dim_use:N \l__spath_tmpb_dim}
+ }
+ \bool_until_do:nn {
+ \tl_if_empty_p:N \l__spath_tmpa_tl
+ }
+ {
+ \tl_set:Nx \l__spath_tmpc_tl {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nn \l__spath_tmpd_tl {}
+ \tl_case:NnF \l__spath_tmpc_tl
+ {
+ \g__spath_moveto_tl {\tl_set_eq:NN \l__spath_tmpd_tl \g__spath_moveto_tl }
+ \g__spath_lineto_tl {\tl_set_eq:NN \l__spath_tmpd_tl \g__spath_lineto_tl }
+ \g__spath_curveto_tl {\tl_set_eq:NN \l__spath_tmpd_tl \g__spath_curvetoa_tl }
+ \g__spath_curvetoa_tl {\tl_set_eq:NN \l__spath_tmpd_tl \g__spath_curveto_tl }
+ \g__spath_curvetob_tl {\tl_set_eq:NN \l__spath_tmpd_tl \g__spath_curvetob_tl }
+ }
+ {
+ \tl_show:N \l__spath_tmpc_tl
+ }
+ \tl_put_left:NV \l__spath_tmpb_tl \l__spath_tmpd_tl
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \dim_set:Nn \l__spath_tmpa_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \dim_set:Nn \l__spath_tmpb_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \tl_put_left:Nx \l__spath_tmpb_tl
+ {
+ {\dim_use:N \l__spath_tmpa_dim}
+ {\dim_use:N \l__spath_tmpb_dim}
+ }
+ }
+ \tl_put_left:NV \l__spath_tmpb_tl \g__spath_moveto_tl
+ \spath_put:nnV {#1} {reverse path} \l__spath_tmpb_tl
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_generate_initialaction:n}
+% This is the first thing that the path does (after the initial move).
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_generate_initialaction:n #1
+{
+ \__spath_get:nnN {#1} {path} \l__spath_tmpa_tl
+ \tl_clear:N \l__spath_tmpb_tl
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \tl_if_empty:NF \l__spath_tmpa_tl {
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_head:N \l__spath_tmpa_tl}
+ }
+ \spath_put:nnV {#1} {initial action} \l__spath_tmpb_tl
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_generate_final action:n}
+% This is the last thing that the path does.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_generate_finalaction:n #1
+{
+ \tl_clear:N \l__spath_tmpb_tl
+ \spath_if_in:nnTF {#1} {reverse path}
+ {
+ \__spath_get:nnN {#1} {reverse path} \l__spath_tmpa_tl
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ }
+ {
+ \__spath_get:nnN {#1} {path} \l__spath_tmpa_tl
+ \tl_reverse:N \l__spath_tmpa_tl
+ }
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \tl_if_empty:NF \l__spath_tmpa_tl {
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_head:N \l__spath_tmpa_tl}
+ }
+ \tl_if_eq:NNT \l__spath_tmpa_tl \g__spath_curvetoa_tl
+ {
+ \tl_set_eq:NN \l__spath_tmpa_tl \g__spath_curveto_tl
+ }
+ \spath_put:nnV {#1} {final action} \l__spath_tmpb_tl
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_generate_minbb:n}
+% This computes the minimum (bottom left) of the bounding box of the path.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_generate_minbb:n #1
+{
+ \__spath_get:nnN {#1} {path} \l__spath_tmpa_tl
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \dim_set:Nn \l__spath_tmpa_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \dim_set:Nn \l__spath_tmpb_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \bool_until_do:nn {
+ \tl_if_empty_p:N \l__spath_tmpa_tl
+ }
+ {
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \dim_set:Nn \l__spath_tmpa_dim {\dim_min:nn {\tl_head:N \l__spath_tmpa_tl} {\l__spath_tmpa_dim}}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \dim_set:Nn \l__spath_tmpb_dim {\dim_min:nn {\tl_head:N \l__spath_tmpa_tl} {\l__spath_tmpb_dim}}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ }
+ \tl_clear:N \l__spath_tmpb_tl
+ \tl_put_right:Nx \l__spath_tmpb_tl
+ {
+ {\dim_use:N \l__spath_tmpa_dim}
+ {\dim_use:N \l__spath_tmpb_dim}
+ }
+ \spath_put:nnV {#1} {min bb} \l__spath_tmpb_tl
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_generate_max bb:n}
+% This computes the maximum (top right) of the bounding box of the path.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_generate_maxbb:n #1
+{
+ \__spath_get:nnN {#1} {path} \l__spath_tmpa_tl
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \dim_set:Nn \l__spath_tmpa_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \dim_set:Nn \l__spath_tmpb_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \bool_until_do:nn {
+ \tl_if_empty_p:N \l__spath_tmpa_tl
+ }
+ {
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \dim_set:Nn \l__spath_tmpa_dim {\dim_max:nn {\tl_head:N \l__spath_tmpa_tl} {\l__spath_tmpa_dim}}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \dim_set:Nn \l__spath_tmpb_dim {\dim_max:nn {\tl_head:N \l__spath_tmpa_tl} {\l__spath_tmpb_dim}}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ }
+ \tl_clear:N \l__spath_tmpb_tl
+ \tl_put_right:Nx \l__spath_tmpb_tl
+ {
+ {\dim_use:N \l__spath_tmpa_dim}
+ {\dim_use:N \l__spath_tmpb_dim}
+ }
+ \spath_put:nnV {#1} {max bb} \l__spath_tmpb_tl
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_generate_all:n}
+% This function generates all of the data in one fell swoop.
+% By traversing the path just once it is quicker than doing each one individually.
+% However, it does need to store a lot of data as it goes.
+% \begin{itemize}
+% \item \Verb+\l__spath_rp_tl+ will hold the reversed path
+% \item \Verb+\l__spath_l_int+ will hold the length
+% \item \Verb+\l__spath_rl_int+ will hold the real length
+% \item \Verb+\l__spath_nc_int+ will hold the number of components
+% \item \Verb+\l__spath_ip_tl+ will hold the initial point
+% \item \Verb+\l__spath_fp_tl+ will hold the final point
+% \item \Verb+\l__spath_ia_tl+ will hold the initial action
+% \item \Verb+\l__spath_fa_tl+ will hold the final action
+% \item \Verb+\l__spath_minx_dim+ will hold the min x bb
+% \item \Verb+\l__spath_miny_dim+ will hold the min y bb
+% \item \Verb+\l__spath_maxx_dim+ will hold the max x bb
+% \item \Verb+\l__spath_maxy_dim+ will hold the max y bb
+% \end{itemize}
+% \begin{macrocode}
+\tl_new:N \l__spath_rp_tl
+\int_new:N \l__spath_l_int
+\int_new:N \l__spath_rl_int
+\int_new:N \l__spath_nc_int
+\tl_new:N \l__spath_ip_tl
+\tl_new:N \l__spath_fp_tl
+\tl_new:N \l__spath_ia_tl
+\tl_new:N \l__spath_fa_tl
+\dim_new:N \l__spath_minx_dim
+\dim_new:N \l__spath_miny_dim
+\dim_new:N \l__spath_maxx_dim
+\dim_new:N \l__spath_maxy_dim
+
+\cs_new_nopar:Npn \spath_generate_all:n #1
+{
+ \__spath_get:nnN {#1} {path} \l__spath_tmpa_tl
+
+ \tl_clear:N \l__spath_rp_tl
+ \int_set:Nn \l__spath_l_int {1}
+ \int_zero:N \l__spath_rl_int
+ \int_set:Nn \l__spath_nc_int {1}
+ \tl_clear:N \l__spath_ip_tl
+ \tl_clear:N \l__spath_fp_tl
+ \tl_clear:N \l__spath_ia_tl
+ \tl_clear:N \l__spath_fa_tl
+ \dim_zero:N \l__spath_minx_dim
+ \dim_zero:N \l__spath_miny_dim
+ \dim_zero:N \l__spath_maxx_dim
+ \dim_zero:N \l__spath_maxy_dim
+
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \dim_set:Nn \l__spath_tmpa_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \dim_set:Nn \l__spath_tmpb_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+
+ \tl_clear:N \l__spath_ip_tl
+ \tl_put_right:Nx \l__spath_ip_tl
+ {
+ {\dim_use:N \l__spath_tmpa_dim}
+ {\dim_use:N \l__spath_tmpb_dim}
+ }
+
+ \dim_set_eq:NN \l__spath_minx_dim \l__spath_tmpa_dim
+ \dim_set_eq:NN \l__spath_miny_dim \l__spath_tmpb_dim
+ \dim_set_eq:NN \l__spath_maxx_dim \l__spath_tmpa_dim
+ \dim_set_eq:NN \l__spath_maxy_dim \l__spath_tmpb_dim
+
+ \tl_put_left:Nx \l__spath_rp_tl
+ {
+ {\dim_use:N \l__spath_tmpa_dim}
+ {\dim_use:N \l__spath_tmpb_dim}
+ }
+
+ \tl_set:Nx \l__spath_ia_tl {\tl_head:N \l__spath_tmpa_tl}
+
+ \bool_until_do:nn {
+ \tl_if_empty_p:N \l__spath_tmpa_tl
+ }
+ {
+ \tl_set:Nx \l__spath_tmpc_tl {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nn \l__spath_tmpd_tl {}
+ \tl_set_eq:NN \l__spath_fa_tl \l__spath_tmpc_tl
+ \int_incr:N \l__spath_l_int
+
+ \tl_case:NnF \l__spath_tmpc_tl
+ {
+ \g__spath_moveto_tl {
+ \tl_set_eq:NN \l__spath_tmpd_tl \g__spath_moveto_tl
+ \int_incr:N \l__spath_nc_int
+ }
+ \g__spath_lineto_tl {
+ \tl_set_eq:NN \l__spath_tmpd_tl \g__spath_lineto_tl
+ \int_incr:N \l__spath_rl_int
+ }
+ \g__spath_curveto_tl {
+ \tl_set_eq:NN \l__spath_tmpd_tl \g__spath_curvetoa_tl
+ \int_incr:N \l__spath_rl_int
+ }
+ \g__spath_curvetoa_tl {
+ \tl_set_eq:NN \l__spath_tmpd_tl \g__spath_curveto_tl
+ }
+ \g__spath_curvetob_tl {
+ \tl_set_eq:NN \l__spath_tmpd_tl \g__spath_curvetob_tl
+ }
+ }
+ {
+ \tl_show:N \l__spath_tmpc_tl
+ }
+ \tl_put_left:NV \l__spath_rp_tl \l__spath_tmpd_tl
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+
+ \dim_set:Nn \l__spath_tmpa_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \dim_set:Nn \l__spath_tmpb_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+
+ \dim_set:Nn \l__spath_minx_dim {\dim_min:nn { \l__spath_minx_dim} {\l__spath_tmpa_dim}}
+ \dim_set:Nn \l__spath_miny_dim {\dim_min:nn { \l__spath_miny_dim} {\l__spath_tmpb_dim}}
+ \dim_set:Nn \l__spath_maxx_dim {\dim_max:nn { \l__spath_maxx_dim} {\l__spath_tmpa_dim}}
+ \dim_set:Nn \l__spath_maxy_dim {\dim_max:nn { \l__spath_maxy_dim} {\l__spath_tmpb_dim}}
+
+ \tl_put_left:Nx \l__spath_rp_tl
+ {
+ {\dim_use:N \l__spath_tmpa_dim}
+ {\dim_use:N \l__spath_tmpb_dim}
+ }
+
+ \tl_set:Nx \l__spath_fp_tl
+ {
+ {\dim_use:N \l__spath_tmpa_dim}
+ {\dim_use:N \l__spath_tmpb_dim}
+ }
+
+ }
+
+ \tl_put_left:NV \l__spath_rp_tl \g__spath_moveto_tl
+
+ \spath_put:nnV {#1} {reverse path} \l__spath_rp_tl
+ \spath_put:nnV {#1} {length} \l__spath_l_int
+ \spath_put:nnV {#1} {real length} \l__spath_rl_int
+ \spath_put:nnV {#1} {number of components} \l__spath_nc_int
+ \spath_put:nnV {#1} {initial point} \l__spath_ip_tl
+ \spath_put:nnV {#1} {final point} \l__spath_fp_tl
+ \spath_put:nnV {#1} {initial action} \l__spath_ia_tl
+ \spath_put:nnV {#1} {final action} \l__spath_fa_tl
+
+ \tl_clear:N \l__spath_tmpb_tl
+ \tl_put_right:Nx \l__spath_tmpb_tl
+ {
+ {\dim_use:N \l__spath_minx_dim}
+ {\dim_use:N \l__spath_miny_dim}
+ }
+ \spath_put:nnV {#1} {min bb} \l__spath_tmpb_tl
+
+ \tl_clear:N \l__spath_tmpb_tl
+ \tl_put_right:Nx \l__spath_tmpb_tl
+ {
+ {\dim_use:N \l__spath_maxx_dim}
+ {\dim_use:N \l__spath_maxy_dim}
+ }
+ \spath_put:nnV {#1} {max bb} \l__spath_tmpb_tl
+
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Path Manipulation}
+%
+% \begin{macro}{\spath_translate:nnn}
+% Translates a path.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_translate:nnn #1#2#3
+{
+ \tl_map_inline:Nn \g__spath_moveable_attributes
+ {
+ \spath_if_in:nnT {#1} {##1}
+ {
+ \__spath_get:nnN {#1} {##1} \l__spath_tmpa_tl
+
+ \dim_set:Nn \l__spath_tmpa_dim {\tl_head:N \l__spath_tmpa_tl + #2}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \dim_set:Nn \l__spath_tmpb_dim {\tl_head:N \l__spath_tmpa_tl + #3}
+ \tl_clear:N \l__spath_tmpb_tl
+ \tl_put_right:Nx \l__spath_tmpb_tl
+ {
+ {\dim_use:N \l__spath_tmpa_dim}
+ {\dim_use:N \l__spath_tmpb_dim}
+ }
+ \spath_put:nnV {#1} {##1} \l__spath_tmpb_tl
+ }
+ }
+ \tl_map_inline:Nn \g__spath_path_attributes
+ {
+ \spath_if_in:nnT {#1} {##1}
+ {
+ \__spath_get:nnN {#1} {##1} \l__spath_tmpa_tl
+ \tl_clear:N \l__spath_tmpb_tl
+ \bool_until_do:nn {
+ \tl_if_empty_p:N \l__spath_tmpa_tl
+ }
+ {
+ \tl_put_right:Nx \l__spath_tmpb_tl {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+
+ \dim_set:Nn \l__spath_tmpa_dim {\tl_head:N \l__spath_tmpa_tl + #2}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+
+ \dim_set:Nn \l__spath_tmpb_dim {\tl_head:N \l__spath_tmpa_tl + #3}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+
+ \tl_put_right:Nx \l__spath_tmpb_tl
+ {
+ {\dim_use:N \l__spath_tmpa_dim}
+ {\dim_use:N \l__spath_tmpb_dim}
+ }
+ }
+ \spath_put:nnV {#1} {##1} \l__spath_tmpb_tl
+ }
+ }
+}
+
+\cs_generate_variant:Nn \spath_translate:nnn {nxx}
+% \end{macrocode}
+%
+% This variant allows for passing the coordinates as a single braced group as it strips off the outer braces of the second argument.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_translate:nn #1#2
+{
+ \spath_translate:nnn {#1} #2
+}
+
+\cs_generate_variant:Nn \spath_translate:nn {nV}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_scale:nnn}
+% Scale a path.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_scale:nnn #1#2#3
+{
+ \tl_map_inline:Nn \g__spath_moveable_attributes
+ {
+ \spath_if_in:nnT {#1} {##1}
+ {
+ \__spath_get:nnN {#1} {##1} \l__spath_tmpa_tl
+
+ \fp_set:Nn \l__spath_tmpa_fp {\tl_head:N \l__spath_tmpa_tl * #2}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \fp_set:Nn \l__spath_tmpb_fp {\tl_head:N \l__spath_tmpa_tl * #3}
+ \tl_clear:N \l__spath_tmpb_tl
+ \tl_put_right:Nx \l__spath_tmpb_tl
+ {
+ {\fp_to_dim:N \l__spath_tmpa_fp}
+ {\fp_to_dim:N \l__spath_tmpb_fp}
+ }
+ \spath_put:nnV {#1} {##1} \l__spath_tmpb_tl
+ }
+ }
+ \tl_map_inline:Nn \g__spath_path_attributes
+ {
+ \spath_if_in:nnT {#1} {##1}
+ {
+ \__spath_get:nnN {#1} {##1} \l__spath_tmpa_tl
+ \tl_clear:N \l__spath_tmpb_tl
+ \bool_until_do:nn {
+ \tl_if_empty_p:N \l__spath_tmpa_tl
+ }
+ {
+ \tl_put_right:Nx \l__spath_tmpb_tl {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+
+ \fp_set:Nn \l__spath_tmpa_fp {\tl_head:N \l__spath_tmpa_tl * #2}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+
+ \fp_set:Nn \l__spath_tmpb_fp {\tl_head:N \l__spath_tmpa_tl * #3}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+
+ \tl_put_right:Nx \l__spath_tmpb_tl
+ {
+ {\fp_to_dim:N \l__spath_tmpa_fp}
+ {\fp_to_dim:N \l__spath_tmpb_fp}
+ }
+ }
+ \spath_put:nnV {#1} {##1} \l__spath_tmpb_tl
+ }
+ }
+}
+\cs_generate_variant:Nn \spath_scale:nnn {nxx}
+% \end{macrocode}
+%
+% This variant allows for passing the coordinates as a single braced group as it strips off the outer braces of the second argument.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_scale:nn #1#2
+{
+ \spath_scale:nnn {#1} #2
+}
+
+\cs_generate_variant:Nn \spath_scale:nn {nV}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_transform:nnnnnnn}
+% Applies an affine (matrix and vector) transformation to path.
+% The matrix is specified in rows first.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_transform:nnnnnnn #1#2#3#4#5#6#7
+{
+ \tl_map_inline:Nn \g__spath_moveable_attributes
+ {
+ \spath_if_in:nnT {#1} {##1}
+ {
+ \__spath_get:nnN {#1} {##1} \l__spath_tmpa_tl
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_head:N \l__spath_tmpb_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_head:N \l__spath_tmpa_tl}
+ \fp_set:Nn \l__spath_tmpa_fp {\l__spath_tmpa_tl * #2 + \l__spath_tmpb_tl * #3 + #6}
+ \fp_set:Nn \l__spath_tmpb_fp {\l__spath_tmpa_tl * #4 + \l__spath_tmpb_tl * #5 + #7}
+ \tl_clear:N \l__spath_tmpb_tl
+ \tl_put_right:Nx \l__spath_tmpb_tl
+ {
+ {\fp_to_dim:N \l__spath_tmpa_fp}
+ {\fp_to_dim:N \l__spath_tmpb_fp}
+ }
+ \spath_put:nnV {#1} {##1} \l__spath_tmpb_tl
+ }
+ }
+ \tl_map_inline:Nn \g__spath_path_attributes
+ {
+ \spath_if_in:nnT {#1} {##1}
+ {
+ \__spath_get:nnN {#1} {##1} \l__spath_tmpa_tl
+ \tl_clear:N \l__spath_tmpb_tl
+ \bool_until_do:nn {
+ \tl_if_empty_p:N \l__spath_tmpa_tl
+ }
+ {
+ \tl_put_right:Nx \l__spath_tmpb_tl {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l_tmpa_tl {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l_tmpb_tl {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+
+ \fp_set:Nn \l__spath_tmpa_fp {\l_tmpa_tl * #2 + \l_tmpb_tl * #3 + #6}
+ \fp_set:Nn \l__spath_tmpb_fp {\l_tmpa_tl * #4 + \l_tmpb_tl * #5 + #7}
+ \tl_put_right:Nx \l__spath_tmpb_tl
+ {
+ {\fp_to_dim:N \l__spath_tmpa_fp}
+ {\fp_to_dim:N \l__spath_tmpb_fp}
+ }
+ }
+ \spath_put:nnV {#1} {##1} \l__spath_tmpb_tl
+ }
+ }
+}
+
+\cs_generate_variant:Nn \spath_transform:nnnnnnn {nxxxxxx}
+% \end{macrocode}
+%
+% This variant allows for passing the coordinates as a single braced group as it strips off the outer braces of the second argument.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_transform:nn #1#2
+{
+ \spath_transform:nnnnnnn {#1} #2
+}
+
+\cs_generate_variant:Nn \spath_transform:nn {nV}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_reverse:n}
+% This reverses a path.
+% As a lot of the data is invariant under reversing, there isn't a lot to do.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_reverse:n #1
+{
+ \spath_if_in:nnF {#1} {reverse path} {
+ \use:c {spath_generate_reverse path:n} {#1}
+ }
+ \spath_swap:nnn {#1} {path} {reverse path}
+ \spath_swap:nnn {#1} {initial point} {final point}
+ \spath_swap:nnn {#1} {initial action} {final action}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_swap:nnn}
+% Swaps two entries, being careful to ensure that their existence (or otherwise) is preserved.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_swap:nnn #1#2#3
+{
+ \__spath_get:nnNF {#1} {#2} \l__spath_tmpa_tl {\tl_clear:N \l__spath_tmpa_tl}
+ \__spath_get:nnNF {#1} {#3} \l__spath_tmpb_tl {\tl_clear:N \l__spath_tmpb_tl}
+ \tl_if_empty:NTF \l__spath_tmpb_tl
+ {\spath_remove:nn {#1} {#2}}
+ {\spath_put:nnV {#1} {#2} \l__spath_tmpb_tl}
+ \tl_if_empty:NTF \l__spath_tmpa_tl
+ {\spath_remove:nn {#1} {#3}}
+ {\spath_put:nnV {#1} {#3} \l__spath_tmpa_tl}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_weld:nn}
+% This welds one path to another, moving the second so that it's initial point coincides with the first's final point.
+% It is called a \emph{weld} because the initial move of the second path is removed.
+% The first path is updated, the second is not modified.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_weld:nn #1#2
+{
+ \spath_clone:nn {#2} {tmp_path}
+ \spath_get:nnN {#1} {final point} \l__spath_tmpa_tl
+
+ \dim_set:Nn \l__spath_tmpa_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \dim_set:Nn \l__spath_tmpb_dim {\tl_head:N \l__spath_tmpa_tl}
+
+ \spath_get:nnN {#2} {initial point} \l__spath_tmpa_tl
+
+ \dim_sub:Nn \l__spath_tmpa_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \dim_sub:Nn \l__spath_tmpb_dim {\tl_head:N \l__spath_tmpa_tl}
+
+ \spath_translate:nxx {tmp_path} {\dim_use:N \l__spath_tmpa_dim} {\dim_use:N \l__spath_tmpb_dim}
+
+ \__spath_get:nnN {#1} {path} \l__spath_tmpa_tl
+ \__spath_get:nnN {tmp_path} {path} \l__spath_tmpb_tl
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_tail:N \l__spath_tmpb_tl}
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_tail:N \l__spath_tmpb_tl}
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_tail:N \l__spath_tmpb_tl}
+ \tl_put_right:NV \l__spath_tmpa_tl \l__spath_tmpb_tl
+
+ \spath_put:nnV {#1} {path} \l__spath_tmpa_tl
+
+ \__spath_get:nnNTF {tmp_path} {final point} \l__spath_tmpa_tl
+ {
+ \spath_put:nnV {#1} {final point} \l__spath_tmpa_tl
+ }
+ {
+ \spath_remove:nn {#1} {final point}
+ }
+
+ \__spath_get:nnNTF {tmp_path} {final action} \l__spath_tmpa_tl
+ {
+ \spath_put:nnV {#1} {final action} \l__spath_tmpa_tl
+ }
+ {
+ \spath_remove:nn {#1} {final action}
+ }
+
+ \__spath_get:nnNT {tmp_path} {min bb} \l__spath_tmpa_tl
+ {
+ \__spath_get:nnNT {#1} {min bb} \l__spath_tmpb_tl
+ {
+ \dim_set:Nn \l__spath_tmpa_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \dim_set:Nn \l__spath_tmpb_dim {\tl_head:N \l__spath_tmpa_tl}
+
+ \dim_set:Nn \l__spath_tmpa_dim {\dim_min:nn {\l__spath_tmpa_dim} {\tl_head:N \l__spath_tmpb_tl}}
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_tail:N \l__spath_tmpb_tl}
+ \dim_set:Nn \l__spath_tmpb_dim {\dim_min:nn {\l__spath_tmpb_dim} {\tl_head:N \l__spath_tmpb_tl}}
+
+ \tl_clear:N \l__spath_tmpb_tl
+ \tl_put_right:Nx \l__spath_tmpb_tl
+ {
+ {\dim_use:N \l__spath_tmpa_dim}
+ {\dim_use:N \l__spath_tmpb_dim}
+ }
+ \spath_put:nnV {#1} {min bb} \l__spath_tmpb_tl
+ }
+ }
+
+ \__spath_get:nnNT {tmp_path} {max bb} \l__spath_tmpa_tl
+ {
+ \__spath_get:nnNT {#1} {max bb} \l__spath_tmpb_tl
+ {
+ \dim_set:Nn \l__spath_tmpa_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \dim_set:Nn \l__spath_tmpb_dim {\tl_head:N \l__spath_tmpa_tl}
+
+ \dim_set:Nn \l__spath_tmpa_dim {\dim_max:nn {\l__spath_tmpa_dim} {\tl_head:N \l__spath_tmpb_tl}}
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_tail:N \l__spath_tmpb_tl}
+ \dim_set:Nn \l__spath_tmpb_dim {\dim_max:nn {\l__spath_tmpb_dim} {\tl_head:N \l__spath_tmpb_tl}}
+
+ \tl_clear:N \l__spath_tmpb_tl
+ \tl_put_right:Nx \l__spath_tmpb_tl
+ {
+ {\dim_use:N \l__spath_tmpa_dim}
+ {\dim_use:N \l__spath_tmpb_dim}
+ }
+ \spath_put:nnV {#1} {max bb} \l__spath_tmpb_tl
+ }
+ }
+
+ \__spath_get:nnNT {tmp_path} {reverse path} \l__spath_tmpa_tl
+ {
+ \__spath_get:nnNT {#1} {reverse path} \l__spath_tmpb_tl
+ {
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_tail:N \l__spath_tmpb_tl}
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_tail:N \l__spath_tmpb_tl}
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_tail:N \l__spath_tmpb_tl}
+ \tl_put_right:NV \l__spath_tmpa_tl \l__spath_tmpb_tl
+
+ \spath_put:nnV {#1} {reverse path} \l__spath_tmpa_tl
+ }
+ }
+
+ \__spath_get:nnNT {tmp_path} {length} \l__spath_tmpa_tl
+ {
+ \__spath_get:nnNT {#1} {length} \l__spath_tmpb_tl
+ {
+ \int_set:Nn \l__spath_tmpa_int {\l__spath_tmpa_tl + \l__spath_tmpb_tl - 1}
+ \spath_put:nnV {#1} {length} \l__spath_tmpa_int
+ }
+ }
+
+ \__spath_get:nnNT {tmp_path} {real length} \l__spath_tmpa_tl
+ {
+ \__spath_get:nnNT {#1} {real length} \l__spath_tmpb_tl
+ {
+ \int_set:Nn \l__spath_tmpa_int {\l__spath_tmpa_tl + \l__spath_tmpb_tl}
+ \spath_put:nnV {#1} {real length} \l__spath_tmpa_int
+ }
+ }
+
+ \__spath_get:nnNT {tmp_path} {number of components} \l__spath_tmpa_tl
+ {
+ \__spath_get:nnNT {#1} {number of components} \l__spath_tmpb_tl
+ {
+ \int_set:Nn \l__spath_tmpa_int {\l__spath_tmpa_tl + \l__spath_tmpb_tl - 1}
+ \spath_put:nnV {#1} {number of components} \l__spath_tmpa_int
+ }
+ }
+
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_prepend_no_move:nn}
+% Prepend the path from the second \Verb+spath+ to the first, removing
+% the adjoining move.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_prepend_no_move:nn #1#2
+{
+ \spath_if_exist:nT {#2}
+ {
+ \__spath_get:nnN {#2} {path} \l__spath_tmpa_tl
+ \__spath_get:nnN {#1} {path} \l__spath_tmpb_tl
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_tail:N \l__spath_tmpb_tl}
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_tail:N \l__spath_tmpb_tl}
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_tail:N \l__spath_tmpb_tl}
+ \tl_put_right:NV \l__spath_tmpa_tl \l__spath_tmpb_tl
+ \spath_put:nnV {#1} {path} \l__spath_tmpa_tl
+
+ \spath_if_in:nnTF {#2} {initial point}
+ {
+ \__spath_get:nnN {#2} {initial point} \l__spath_tmpa_tl
+ \spath_put:nnV {#1} {initial point} \l__spath_tmpa_tl
+ }
+ {
+ \spath_remove:nn {#1} {initial point}
+ }
+
+ \spath_if_in:nnTF {#2} {initial action}
+ {
+ \__spath_get:nnN {#2} {initial action} \l__spath_tmpa_tl
+ \spath_put:nnV {#1} {initial action} \l__spath_tmpa_tl
+ }
+ {
+ \spath_remove:nn {#1} {initial action}
+ }
+
+ \bool_if:nTF
+ {
+ \spath_if_in_p:nn {#1} {length}
+ &&
+ \spath_if_in_p:nn {#2} {length}
+ }
+ {
+ \__spath_get:nnN {#1} {length} \l__spath_tmpa_tl
+ \__spath_get:nnN {#2} {length} \l__spath_tmpb_tl
+ \spath_put:nnx {#1} {length} {\int_eval:n {\l__spath_tmpa_tl +
+ \l__spath_tmpb_tl - 1}}
+ }
+ {
+ \spath_remove:nn {#1} {length}
+ }
+ \bool_if:nTF
+ {
+ \spath_if_in_p:nn {#1} {real length}
+ &&
+ \spath_if_in_p:nn {#2} {real length}
+ }
+ {
+ \__spath_get:nnN {#1} {real length} \l__spath_tmpa_tl
+ \__spath_get:nnN {#2} {real length} \l__spath_tmpb_tl
+ \spath_put:nnx {#1} {real length} {\int_eval:n {\l__spath_tmpa_tl +
+ \l__spath_tmpb_tl }}
+ }
+ {
+ \spath_remove:nn {#1} {real length}
+ }
+ \bool_if:nTF
+ {
+ \spath_if_in_p:nn {#1} {number of components}
+ &&
+ \spath_if_in_p:nn {#2} {number of components}
+ }
+ {
+ \__spath_get:nnN {#1} {number of components} \l__spath_tmpa_tl
+ \__spath_get:nnN {#2} {number of components} \l__spath_tmpb_tl
+ \spath_put:nnx {#1} {number of components} {\int_eval:n {\l__spath_tmpa_tl +
+ \l__spath_tmpb_tl - 1}}
+ }
+ {
+ \spath_remove:nn {#1} {number of components}
+ }
+ \bool_if:nTF
+ {
+ \spath_if_in_p:nn {#1} {min bb}
+ &&
+ \spath_if_in_p:nn {#2} {min bb}
+ }
+ {
+ \__spath_get:nnN {#1} {min bb} \l__spath_tmpa_tl
+ \__spath_get:nnN {#2} {min bb} \l__spath_tmpb_tl
+ \dim_set:Nn \l__spath_tmpa_dim {\dim_min:nn {\tl_item:Nn
+ \l__spath_tmpa_tl {1}} {\tl_item:Nn
+ \l__spath_tmpb_tl {1}}}
+ \dim_set:Nn \l__spath_tmpb_dim {\dim_min:nn {\tl_item:Nn
+ \l__spath_tmpa_tl {2}} {\tl_item:Nn
+ \l__spath_tmpb_tl {2}}}
+ \spath_put:nnx {#1} {min bb} {
+ {\dim_use:N \l__spath_tmpa_dim}
+ {\dim_use:N \l__spath_tmpb_dim}
+ }
+ }
+ {
+ \spath_remove:nn {#1} {min bb}
+ }
+ \bool_if:nTF
+ {
+ \spath_if_in_p:nn {#1} {max bb}
+ &&
+ \spath_if_in_p:nn {#2} {max bb}
+ }
+ {
+ \__spath_get:nnN {#1} {max bb} \l__spath_tmpa_tl
+ \__spath_get:nnN {#2} {max bb} \l__spath_tmpb_tl
+ \dim_set:Nn \l__spath_tmpa_dim {\dim_min:nn {\tl_item:Nn
+ \l__spath_tmpa_tl {1}} {\tl_item:Nn
+ \l__spath_tmpb_tl {1}}}
+ \dim_set:Nn \l__spath_tmpb_dim {\dim_min:nn {\tl_item:Nn
+ \l__spath_tmpa_tl {2}} {\tl_item:Nn
+ \l__spath_tmpb_tl {2}}}
+ \spath_put:nnx {#1} {max bb} {
+ {\dim_use:N \l__spath_tmpa_dim}
+ {\dim_use:N \l__spath_tmpb_dim}
+ }
+ }
+ {
+ \spath_remove:nn {#1} {max bb}
+ }
+ \bool_if:nTF
+ {
+ \spath_if_in_p:nn {#1} {reverse path}
+ &&
+ \spath_if_in_p:nn {#2} {reverse path}
+ }
+ {
+ \__spath_get:nnN {#1} {reverse path} \l__spath_tmpa_tl
+ \__spath_get:nnN {#2} {reverse path} \l__spath_tmpb_tl
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_tail:N \l__spath_tmpb_tl}
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_tail:N \l__spath_tmpb_tl}
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_tail:N \l__spath_tmpb_tl}
+ \tl_put_right:NV \l__spath_tmpa_tl \l__spath_tmpb_tl
+ \spath_put:nnV {#1} {reverse path} \l__spath_tmpb_tl
+ }
+ {
+ \spath_remove:nn {#1} {reverse path}
+ }
+
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_append_no_move:nn}
+% Append the path from the second \Verb+spath+ to the first, removing
+% the adjoining move.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_append_no_move:nn #1#2
+{
+ \spath_if_exist:nT {#2}
+ {
+ \spath_get:nnN {#1} {path} \l__spath_tmpa_tl
+ \spath_get:nnN {#2} {path} \l__spath_tmpb_tl
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_tail:N \l__spath_tmpb_tl}
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_tail:N \l__spath_tmpb_tl}
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_tail:N \l__spath_tmpb_tl}
+ \tl_put_right:NV \l__spath_tmpa_tl \l__spath_tmpb_tl
+ \spath_put:nnV {#1} {path} \l__spath_tmpa_tl
+ \spath_if_in:nnTF {#2} {final point}
+ {
+ \__spath_get:nnN {#2} {final point} \l__spath_tmpa_tl
+ \spath_put:nnV {#1} {final point} \l__spath_tmpa_tl
+ }
+ {
+ \spath_remove:nn {#1} {final point}
+ }
+ \spath_if_in:nnTF {#2} {final action}
+ {
+ \__spath_get:nnN {#2} {final action} \l__spath_tmpa_tl
+ \spath_put:nnV {#1} {final action} \l__spath_tmpa_tl
+ }
+ {
+ \spath_remove:nn {#1} {final action}
+ }
+ \bool_if:nTF
+ {
+ \spath_if_in_p:nn {#1} {length}
+ &&
+ \spath_if_in_p:nn {#2} {length}
+ }
+ {
+ \__spath_get:nnN {#1} {length} \l__spath_tmpa_tl
+ \__spath_get:nnN {#2} {length} \l__spath_tmpb_tl
+ \spath_put:nnx {#1} {length} {\int_eval:n {\l__spath_tmpa_tl +
+ \l__spath_tmpb_tl - 1}}
+ }
+ {
+ \spath_remove:nn {#1} {length}
+ }
+ \bool_if:nTF
+ {
+ \spath_if_in_p:nn {#1} {real length}
+ &&
+ \spath_if_in_p:nn {#2} {real length}
+ }
+ {
+ \__spath_get:nnN {#1} {real length} \l__spath_tmpa_tl
+ \__spath_get:nnN {#2} {real length} \l__spath_tmpb_tl
+ \spath_put:nnx {#1} {real length} {\int_eval:n {\l__spath_tmpa_tl +
+ \l__spath_tmpb_tl }}
+ }
+ {
+ \spath_remove:nn {#1} {real length}
+ }
+ \bool_if:nTF
+ {
+ \spath_if_in_p:nn {#1} {number of components}
+ &&
+ \spath_if_in_p:nn {#2} {number of components}
+ }
+ {
+ \__spath_get:nnN {#1} {number of components} \l__spath_tmpa_tl
+ \__spath_get:nnN {#2} {number of components} \l__spath_tmpb_tl
+ \spath_put:nnx {#1} {number of components} {\int_eval:n {\l__spath_tmpa_tl +
+ \l__spath_tmpb_tl - 1}}
+ }
+ {
+ \spath_remove:nn {#1} {number of components}
+ }
+ \bool_if:nTF
+ {
+ \spath_if_in_p:nn {#1} {min bb}
+ &&
+ \spath_if_in_p:nn {#2} {min bb}
+ }
+ {
+ \__spath_get:nnN {#1} {min bb} \l__spath_tmpa_tl
+ \__spath_get:nnN {#2} {min bb} \l__spath_tmpb_tl
+ \dim_set:Nn \l__spath_tmpa_dim {\dim_min:nn {\tl_item:Nn
+ \l__spath_tmpa_tl {1}} {\tl_item:Nn
+ \l__spath_tmpb_tl {1}}}
+ \dim_set:Nn \l__spath_tmpb_dim {\dim_min:nn {\tl_item:Nn
+ \l__spath_tmpa_tl {2}} {\tl_item:Nn
+ \l__spath_tmpb_tl {2}}}
+ \spath_put:nnx {#1} {min bb} {
+ {\dim_use:N \l__spath_tmpa_dim}
+ {\dim_use:N \l__spath_tmpb_dim}
+ }
+ }
+ {
+ \spath_remove:nn {#1} {min bb}
+ }
+ \bool_if:nTF
+ {
+ \spath_if_in_p:nn {#1} {max bb}
+ &&
+ \spath_if_in_p:nn {#2} {max bb}
+ }
+ {
+ \__spath_get:nnN {#1} {max bb} \l__spath_tmpa_tl
+ \__spath_get:nnN {#2} {max bb} \l__spath_tmpb_tl
+ \dim_set:Nn \l__spath_tmpa_dim {\dim_min:nn {\tl_item:Nn
+ \l__spath_tmpa_tl {1}} {\tl_item:Nn
+ \l__spath_tmpb_tl {1}}}
+ \dim_set:Nn \l__spath_tmpb_dim {\dim_min:nn {\tl_item:Nn
+ \l__spath_tmpa_tl {2}} {\tl_item:Nn
+ \l__spath_tmpb_tl {2}}}
+ \spath_put:nnx {#1} {max bb} {
+ {\dim_use:N \l__spath_tmpa_dim}
+ {\dim_use:N \l__spath_tmpb_dim}
+ }
+ }
+ {
+ \spath_remove:nn {#1} {max bb}
+ }
+ \bool_if:nTF
+ {
+ \spath_if_in_p:nn {#1} {reverse path}
+ &&
+ \spath_if_in_p:nn {#2} {reverse path}
+ }
+ {
+ \__spath_get:nnN {#2} {reverse path} \l__spath_tmpa_tl
+ \__spath_get:nnN {#1} {reverse path} \l__spath_tmpb_tl
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_tail:N \l__spath_tmpb_tl}
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_tail:N \l__spath_tmpb_tl}
+ \tl_set:Nx \l__spath_tmpb_tl {\tl_tail:N \l__spath_tmpb_tl}
+ \tl_put_right:NV \l__spath_tmpa_tl \l__spath_tmpb_tl
+ \spath_put:nnV {#1} {reverse path} \l__spath_tmpb_tl
+ }
+ {
+ \spath_remove:nn {#1} {reverse path}
+ }
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_bake_round:n}
+% Ought to clear the reverse path, if set.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_bake_round:n #1
+{
+ \__spath_get:nnN {#1} {path} \l__spath_tmpa_tl
+ \pgf@@processround\l__spath_tmpa_tl\l__spath_tmpb_tl
+ \spath_put:nnV {#1} {path} \l__spath_tmpb_tl
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_close_path:n}
+% Appends a close path to the end of the path, and to the end of the reverse path.
+% For now, the point is the initial or final point (respectively).
+% To be future proof, it ought to be the point of the adjacent move to.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_close_path:n #1
+{
+ \spath_get:nnN {#1} {initial point} \l__spath_tmpb_tl
+ \__spath_get:nnN {#1} {path} \l__spath_tmpa_tl
+ \tl_put_right:NV \l__spath_tmpa_tl \g__spath_closepath_tl
+ \tl_put_right:NV \l__spath_tmpa_tl \l__spath_tmpb_tl
+ \spath_put:nnV {#1} {path} \l__spath_tmpa_tl
+ \spath_if_in:nnT {#1} {reverse path}
+ {
+ \spath_get:nnN {#1} {final point} \l__spath_tmpb_tl
+ \__spath_get:nnN {#1} {reverse path} \l__spath_tmpa_tl
+ \tl_put_right:NV \l__spath_tmpa_tl \g__spath_closepath_tl
+ \tl_put_right:NV \l__spath_tmpa_tl \l__spath_tmpb_tl
+ \spath_put:nnV {#1} {reverse path} \l__spath_tmpa_tl
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Iteration Functions}
+%
+% \begin{macro}{\spath_map_component:Nn}
+% This iterates through the components of a path, applying the inline function to each.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_map_component:Nn #1#2
+{
+ \int_gincr:N \g__spath_map_int
+ \cs_gset:cpn { __spath_map_ \int_use:N \g__spath_map_int :w } ##1 {#2}
+ \tl_set:NV \l__spath_tmpa_tl #1
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \tl_put_right:NV \l__spath_tmpa_tl \g__spath_moveto_tl
+ \tl_set_eq:NN \l__spath_tmpb_tl \g__spath_moveto_tl
+ \bool_until_do:nn {
+ \tl_if_empty_p:N \l__spath_tmpa_tl
+ }
+ {
+ \tl_set:Nx \l__spath_tmpc_tl {\tl_head:N \l__spath_tmpa_tl}
+ \tl_if_eq:NNT \l__spath_tmpc_tl \g__spath_moveto_tl
+ {
+ \exp_args:NnV \use:c { __spath_map_ \int_use:N \g__spath_map_int :w } \l__spath_tmpb_tl
+\tl_clear:N \l__spath_tmpb_tl
+ }
+ \tl_if_single:NTF \l__spath_tmpc_tl
+ {
+ \tl_put_right:NV \l__spath_tmpb_tl \l__spath_tmpc_tl
+ }
+ {
+ \tl_put_right:Nx \l__spath_tmpb_tl {{\l__spath_tmpc_tl}}
+ }
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_map_segment_inline:Nn}
+% This iterates through the segments of the path, applying the inline function to each.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_map_segment_inline:Nn #1#2
+{
+ \int_gincr:N \g__spath_map_int
+ \cs_gset:cpn { __spath_map_ \int_use:N \g__spath_map_int :w } ##1 ##2 {#2}
+ \spath_map_segment_function:Nc #1 { __spath_map_ \int_use:N \g__spath_map_int :w }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_map_segment_inline:nn}
+% This iterates through the segments of the path of the \Verb+spath+ object, applying the inline function to each.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_map_segment_inline:nn #1#2
+{
+ \int_gincr:N \g__spath_map_int
+ \cs_gset:cpn { __spath_map_ \int_use:N \g__spath_map_int :w } ##1 ##2 {#2}
+ \spath_get:nnN {#1} {path} \l__spath_tmpd_tl
+ \spath_map_segment_function:Nc \l__spath_tmpd_tl { __spath_map_ \int_use:N \g__spath_map_int :w }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\spath_map_segment_function:nN}
+% This iterates through the segments of the path of the \Verb+spath+ object, applying the specified function to each.
+% The specified function should take two \Verb+N+ type arguments.
+% The first is a token representing the type of path segment, the second is the path segment itself.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_map_segment_function:nN #1#2
+{
+ \spath_get:nnN {#1} {path} \l__spath_tmpd_tl
+ \spath_map_segment_function:NN \l__spath_tmpd_tl #2
+}
+% \end{macrocode}
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_map_segment_function:NN #1#2
+{
+ \tl_set_eq:NN \l__spath_tmpa_tl #1
+ \tl_clear:N \l__spath_tmpb_tl
+ \dim_zero:N \l__spath_tmpa_dim
+ \dim_zero:N \l__spath_tmpb_dim
+
+ \bool_until_do:nn {
+ \tl_if_empty_p:N \l__spath_tmpa_tl
+ }
+ {
+ \tl_set:Nx \l__spath_tmpc_tl {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \tl_case:NnF \l__spath_tmpc_tl
+ {
+ \g__spath_lineto_tl
+ {
+ \tl_set_eq:NN \l__spath_tmpb_tl \g__spath_moveto_tl
+ \tl_put_right:Nx \l__spath_tmpb_tl
+ {
+ {\dim_use:N \l__spath_tmpa_dim}
+ {\dim_use:N \l__spath_tmpb_dim}
+ }
+ \tl_put_right:NV \l__spath_tmpb_tl \g__spath_lineto_tl
+
+ \tl_put_right:Nx \l__spath_tmpb_tl {{\tl_head:N \l__spath_tmpa_tl}}
+ \dim_set:Nn \l__spath_tmpa_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+
+ \tl_put_right:Nx \l__spath_tmpb_tl {{\tl_head:N \l__spath_tmpa_tl}}
+ \dim_set:Nn \l__spath_tmpb_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+
+ }
+
+ \g__spath_curvetoa_tl
+ {
+ \tl_set_eq:NN \l__spath_tmpb_tl \g__spath_moveto_tl
+ \tl_put_right:Nx \l__spath_tmpb_tl
+ {
+ {\dim_use:N \l__spath_tmpa_dim}
+ {\dim_use:N \l__spath_tmpb_dim}
+ }
+ \tl_put_right:NV \l__spath_tmpb_tl \g__spath_curvetoa_tl
+
+ \prg_replicate:nn {2} {
+ \tl_put_right:Nx \l__spath_tmpb_tl {{\tl_head:N \l__spath_tmpa_tl}}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \tl_put_right:Nx \l__spath_tmpb_tl {{\tl_head:N \l__spath_tmpa_tl}}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ \tl_put_right:Nx \l__spath_tmpb_tl {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+ }
+
+ \tl_put_right:Nx \l__spath_tmpb_tl {{\tl_head:N \l__spath_tmpa_tl}}
+ \dim_set:Nn \l__spath_tmpa_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+
+ \tl_put_right:Nx \l__spath_tmpb_tl {{\tl_head:N \l__spath_tmpa_tl}}
+ \dim_set:Nn \l__spath_tmpb_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+
+ }
+
+ \g__spath_closepath_tl
+ {
+ \tl_set_eq:NN \l__spath_tmpb_tl \g__spath_moveto_tl
+ \tl_put_right:Nx \l__spath_tmpb_tl
+ {
+ {\dim_use:N \l__spath_tmpa_dim}
+ {\dim_use:N \l__spath_tmpb_dim}
+ }
+ \tl_put_right:NV \l__spath_tmpb_tl \g__spath_lineto_tl
+
+ \tl_put_right:Nx \l__spath_tmpb_tl {{\tl_head:N \l__spath_tmpa_tl}}
+ \dim_set:Nn \l__spath_tmpa_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+
+ \tl_put_right:Nx \l__spath_tmpb_tl {{\tl_head:N \l__spath_tmpa_tl}}
+ \dim_set:Nn \l__spath_tmpb_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+
+ }
+
+ }
+ {
+
+ \tl_set_eq:NN \l__spath_tmpb_tl \l__spath_tmpc_tl
+ \tl_put_right:Nx \l__spath_tmpb_tl {{\tl_head:N \l__spath_tmpa_tl}}
+ \dim_set:Nn \l__spath_tmpa_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+
+ \tl_put_right:Nx \l__spath_tmpb_tl {{\tl_head:N \l__spath_tmpa_tl}}
+ \dim_set:Nn \l__spath_tmpb_dim {\tl_head:N \l__spath_tmpa_tl}
+ \tl_set:Nx \l__spath_tmpa_tl {\tl_tail:N \l__spath_tmpa_tl}
+
+ }
+
+ #2 \l__spath_tmpc_tl \l__spath_tmpb_tl
+ \tl_clear:N \l__spath_tmpb_tl
+
+ }
+}
+\cs_generate_variant:Nn \spath_map_segment_function:NN {Nc}
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Public Commands}
+%
+% The next functions are more ``public'' than the previous lot.
+% That said, they aren't intended for direct use in a normal document.
+%
+% Most are just wrappers around internal functions.
+%
+% \begin{macro}{\MakeSPath}
+% Constructs an \Verb+spath+ object out of the given name and path.
+% \begin{macrocode}
+\NewDocumentCommand \MakeSPath { m m }
+{
+ \spath_clear_new:n {#1}
+ \spath_put:nno {#1} {path} {#2}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\MakeSPathList}
+% This constructs a list of \Verb+spath+ objects from a single path by splitting it into components.
+% \begin{macrocode}
+\NewDocumentCommand \MakeSPathList { m m }
+{
+ \tl_gclear_new:c {l__spath_list_#1}
+ \int_zero:N \l__spath_tmpa_int
+ \spath_map_component:Nn #2 {
+ \spath_clear_new:n {#1 _ \int_use:N \l__spath_tmpa_int}
+ \spath_put:nnn {#1 _ \int_use:N \l__spath_tmpa_int} {path} {##1}
+ \tl_gput_right:cx {l__spath_list_#1} {{#1 _ \int_use:N \l__spath_tmpa_int}}
+ \int_incr:N \l__spath_tmpa_int
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\CloneSPath}
+% \begin{macrocode}
+\NewDocumentCommand \CloneSPath { m m }
+{
+ \spath_clone:nn {#1} {#2}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\SPathInfo}
+% \begin{macrocode}
+\NewDocumentCommand \SPathInfo { m m }
+{
+ \spath_get:nn {#1} {#2}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\SPathPrepare}
+% \begin{macrocode}
+\NewDocumentCommand \SPathPrepare { m }
+{
+ \spath_generate_all:n {#1}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\SPathListPrepare}
+% \begin{macrocode}
+\NewDocumentCommand \SPathListPrepare { m }
+{
+ \tl_map_inline:cn {l__spath_list_#1}
+ {
+ \spath_generate_all:n {##1}
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\SPathInfoInto}
+% \begin{macrocode}
+\NewDocumentCommand \SPathInfoInto { m m m }
+{
+ \tl_clear_new:N #3
+ \spath_get:nnN {#1} {#2} #3
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\SPathShow}
+% \begin{macrocode}
+\NewDocumentCommand \SPathShow { m }
+{
+ \spath_show:n {#1}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\SPathTranslate}
+% \begin{macrocode}
+\NewDocumentCommand \SPathTranslate { m m m }
+{
+ \spath_translate:nnn {#1} {#2} {#3}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\SPathTranslateInto}
+% Clones the path before translating it.
+% \begin{macrocode}
+\NewDocumentCommand \SPathTranslateInto { m m m m }
+{
+ \spath_clone:nn {#1} {#2}
+ \spath_translate:nnn {#2} {#3} {#4}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\SPathScale}
+% \begin{macrocode}
+\NewDocumentCommand \SPathScale { m m m }
+{
+ \spath_scale:nnn {#1} {#2} {#3}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\SPathScaleInto}
+% Clones the path first.
+% \begin{macrocode}
+\NewDocumentCommand \SPathScaleInto { m m m m }
+{
+ \spath_clone:nn {#1} {#2}
+ \spath_scale:nnn {#2} {#3} {#4}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\SPathWeld}
+% \begin{macrocode}
+\NewDocumentCommand \SPathWeld { m m }
+{
+ \spath_weld:nn {#1} {#2}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\SPathWeldInto}
+% \begin{macrocode}
+\NewDocumentCommand \SPathWeldInto { m m m }
+{
+ \spath_clone:nn {#1} {#2}
+ \spath_weld:nn {#2} {#3}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% Interfaces via TikZ keys.
+% \begin{macrocode}
+\tikzset{
+ save~spath/.code={
+ \tikz@addmode{
+ \spath_get_current_path:n {#1}
+ }
+ },
+ restore~spath/.code={
+ \spath_set_current_path:n {#1}
+ }
+}
+% \end{macrocode}
+% \subsection{Miscellaneous Commands}
+%
+% \begin{macro}{\spath_split_curve:nnNN}
+% Splits a Bezier cubic into pieces.
+% \begin{macrocode}
+\cs_new_nopar:Npn \spath_split_curve:nnNN #1#2#3#4
+{
+ \group_begin:
+ \tl_gclear:N \l__spath_smuggle_tl
+ \tl_set_eq:NN \l__spath_tmpa_tl \g__spath_moveto_tl
+ \tl_put_right:Nx \l__spath_tmpa_tl {
+ {\tl_item:nn {#2} {2}}
+ {\tl_item:nn {#2} {3}}
+ }
+ \tl_put_right:NV \l__spath_tmpa_tl \g__spath_curvetoa_tl
+ \tl_put_right:Nx \l__spath_tmpa_tl
+ {
+ {\fp_to_dim:n
+ {
+ (1 - #1) * \tl_item:nn {#2} {2} + (#1) * \tl_item:nn {#2} {5}
+ }}
+ {\fp_to_dim:n
+ {
+ (1 - #1) * \tl_item:nn {#2} {3} + (#1) * \tl_item:nn {#2} {6}
+ }}
+ }
+ \tl_put_right:NV \l__spath_tmpa_tl \g__spath_curvetob_tl
+ \tl_put_right:Nx \l__spath_tmpa_tl
+ {
+ {\fp_to_dim:n
+ {
+ (1 - #1)^2 * \tl_item:nn {#2} {2} + 2 * (1 - #1) * (#1) * \tl_item:nn {#2} {5} + (#1)^2 * \tl_item:nn {#2} {8}
+ }}
+ {\fp_to_dim:n
+ {
+ (1 - #1)^2 * \tl_item:nn {#2} {3} + 2 * (1 - #1) * (#1) * \tl_item:nn {#2} {6} + (#1)^2 * \tl_item:nn {#2} {9}
+ }}
+ }
+ \tl_put_right:NV \l__spath_tmpa_tl \g__spath_curveto_tl
+ \tl_put_right:Nx \l__spath_tmpa_tl
+ {
+ {\fp_to_dim:n
+ {
+ (1 - #1)^3 * \tl_item:nn {#2} {2} + 3 * (1 - #1)^2 * (#1) * \tl_item:nn {#2} {5} + 3 * (1 - #1) * (#1)^2 * \tl_item:nn {#2} {8} + (#1)^3 * \tl_item:nn {#2} {11}
+ }}
+ {\fp_to_dim:n
+ {
+ (1 - #1)^3 * \tl_item:nn {#2} {3} + 3 * (1 - #1)^2 * (#1) * \tl_item:nn {#2} {6} + 3 * (1 - #1) * (#1)^2 * \tl_item:nn {#2} {9} + (#1)^3 * \tl_item:nn {#2} {12}
+ }}
+ }
+ \tl_gset_eq:NN \l__spath_smuggle_tl \l__spath_tmpa_tl
+ \group_end:
+ \tl_set_eq:NN #3 \l__spath_smuggle_tl
+ \group_begin:
+ \tl_set_eq:NN \l__spath_tmpa_tl \g__spath_moveto_tl
+ \tl_put_right:Nx \l__spath_tmpa_tl
+ {
+ {\fp_to_dim:n
+ {
+ (1 - #1)^3 * \tl_item:nn {#2} {2} + 3 * (1 - #1)^2 * (#1) * \tl_item:nn {#2} {5} + 3 * (1 - #1) * (#1)^2 * \tl_item:nn {#2} {8} + (#1)^3 * \tl_item:nn {#2} {11}
+ }}
+ {\fp_to_dim:n
+ {
+ (1 - #1)^3 * \tl_item:nn {#2} {3} + 3 * (1 - #1)^2 * (#1) * \tl_item:nn {#2} {6} + 3 * (1 - #1) * (#1)^2 * \tl_item:nn {#2} {9} + (#1)^3 * \tl_item:nn {#2} {12}
+ }}
+ }
+ \tl_put_right:NV \l__spath_tmpa_tl \g__spath_curvetoa_tl
+ \tl_put_right:Nx \l__spath_tmpa_tl
+ {
+ {\fp_to_dim:n
+ {
+ (1 - #1)^2 * \tl_item:nn {#2} {5} + 2 * (1 - #1) * (#1) * \tl_item:nn {#2} {8} + (#1)^2 * \tl_item:nn {#2} {11}
+ }}
+ {\fp_to_dim:n
+ {
+ (1 - #1)^2 * \tl_item:nn {#2} {6} + 2 * (1 - #1) * (#1) * \tl_item:nn {#2} {9} + (#1)^2 * \tl_item:nn {#2} {12}
+ }}
+ }
+ \tl_put_right:NV \l__spath_tmpa_tl \g__spath_curvetob_tl
+ \tl_put_right:Nx \l__spath_tmpa_tl
+ {
+ {\fp_to_dim:n
+ {
+ (1 - #1) * \tl_item:nn {#2} {8} + (#1) * \tl_item:nn {#2} {11}
+ }}
+ {\fp_to_dim:n
+ {
+ (1 - #1) * \tl_item:nn {#2} {9} + (#1) * \tl_item:nn {#2} {12}
+ }}
+ }
+ \tl_put_right:NV \l__spath_tmpa_tl \g__spath_curveto_tl
+ \tl_put_right:Nx \l__spath_tmpa_tl {
+ {\tl_item:nn {#2} {11}}
+ {\tl_item:nn {#2} {12}}
+ }
+ \tl_gset_eq:NN \l__spath_smuggle_tl \l__spath_tmpa_tl
+ \group_end:
+ \tl_set_eq:NN #4 \l__spath_smuggle_tl
+}
+
+\cs_generate_variant:Nn \spath_split_curve:nnNN {nVNN, VVNN}
+% \end{macrocode}
+% \end{macro}
+% \iffalse
+%</spath3>
+% \fi
+%
+% \iffalse
+%<*calligraphy>
+% \fi
+%
+% \section{The Calligraphy Package}
+%
+% \subsection{Initialisation}
+% \begin{macrocode}
+\RequirePackage{spath3}
+\ExplSyntaxOn
+
+\tl_new:N \l__cal_tmpa_tl
+\tl_new:N \l__cal_tmpb_tl
+\int_new:N \l__cal_tmpa_int
+\int_new:N \l__cal_tmpb_int
+\int_new:N \l__cal_path_component_int
+\int_new:N \l__cal_label_int
+\dim_new:N \l__cal_tmpa_dim
+\dim_new:N \l__cal_tmpb_dim
+\dim_new:N \l__cal_tmpc_dim
+\dim_new:N \l__cal_tmpd_dim
+\dim_new:N \l__cal_tmpe_dim
+\dim_new:N \l__cal_tmpf_dim
+\dim_new:N \l__cal_tmpg_dim
+\dim_new:N \l__cal_tmph_dim
+\bool_new:N \l__cal_annotate_bool
+\bool_new:N \l__cal_taper_start_bool
+\bool_new:N \l__cal_taper_end_bool
+\bool_new:N \l__cal_taperable_bool
+\dim_new:N \l__cal_taper_width_dim
+\dim_new:N \l__cal_line_width_dim
+
+\bool_set_true:N \l__cal_taper_start_bool
+\bool_set_true:N \l__cal_taper_end_bool
+
+\cs_generate_variant:Nn \tl_put_right:Nn {Nv}
+% \end{macrocode}
+%
+% \subsection{TikZ Keys}
+%
+% The public interface to this package is through TikZ keys and styles.
+% \begin{macrocode}
+\tikzset{
+ define~pen/.code={
+ \tikzset{pen~name=#1}
+ \pgf@relevantforpicturesizefalse
+ \tikz@addmode{
+ \pgfsyssoftpath@getcurrentpath\l__cal_tmpa_tl
+ \MakeSPathList{calligraphy pen \pgfkeysvalueof{/tikz/pen~name}}{\l__cal_tmpa_tl}
+ \SPathListPrepare{calligraphy pen \pgfkeysvalueof{/tikz/pen~name}}
+ \pgfusepath{discard}%
+ }
+ },
+ define~pen/.default={default},
+ use~pen/.code={
+ \tikzset{pen~name=#1}
+ \int_gzero:N \l__cal_path_component_int
+ \cs_set_eq:NN \pgfpathmoveto \cal_moveto:n
+ \tikz@addmode{
+ \pgfsyssoftpath@getcurrentpath\l__cal_tmpa_tl
+ \MakeSPathList{calligraphy path}{\l__cal_tmpa_tl}
+ \SPathListPrepare{calligraphy path}
+ \CalligraphyPathCreate{calligraphy path}{\pgfkeysvalueof{/tikz/pen~name}}
+ }
+ },
+ use~pen/.default={default},
+ pen~name/.initial={default},
+ copperplate/.style={pen~name=copperplate},
+ pen~colour/.initial={black},
+ weight/.is~choice,
+ weight/heavy/.style={
+ line~width=\pgfkeysvalueof{/tikz/heavy~line~width},
+ taper~width=\pgfkeysvalueof{/tikz/light~line~width},
+ },
+ weight/light/.style={
+ line~width=\pgfkeysvalueof{/tikz/light~line~width},
+ taper~width=0pt,
+ },
+ heavy/.style={
+ weight=heavy
+ },
+ light/.style={
+ weight=light
+ },
+ heavy~line~width/.initial=2pt,
+ light~line~width/.initial=1pt,
+ taper/.is~choice,
+ taper/.default=both,
+ taper/none/.style={
+ taper~start=false,
+ taper~end=false,
+ },
+ taper/both/.style={
+ taper~start=true,
+ taper~end=true,
+ },
+ taper/start/.style={
+ taper~start=true,
+ taper~end=false,
+ },
+ taper/end/.style={
+ taper~start=false,
+ taper~end=true,
+ },
+ taper~start/.code={
+ \tl_if_eq:nnTF {#1} {true}
+ {
+ \bool_set_true:N \l__cal_taper_start_bool
+ }
+ {
+ \bool_set_false:N \l__cal_taper_start_bool
+ }
+ },
+ taper~start/.default={true},
+ taper~end/.code={
+ \tl_if_eq:nnTF {#1} {true}
+ {
+ \bool_set_true:N \l__cal_taper_end_bool
+ }
+ {
+ \bool_set_false:N \l__cal_taper_end_bool
+ }
+ },
+ taper~end/.default={true},
+ taper~width/.code={\dim_set:Nn \l__cal_taper_width_dim {#1}},
+ nib~style/.code~2~args={
+ \tl_clear_new:c {l__cal_nib_style_#1}
+ \tl_set:cn {l__cal_nib_style_#1} {#2}
+ },
+ stroke~style/.code~2~args={
+ \tl_clear_new:c {l__cal_stroke_style_#1}
+ \tl_set:cn {l__cal_stroke_style_#1} {#2}
+ },
+ this~stroke~style/.code={
+ \tl_clear_new:c {l__cal_stroke_inline_style_ \int_use:N \l__cal_path_component_int}
+ \tl_set:cn {l__cal_stroke_inline_style_ \int_use:N \l__cal_path_component_int} {#1}
+ },
+ annotate/.style={
+ annotate~if,
+ annotate~reset,
+ annotation~style/.update~value={#1},
+ },
+ annotate~if/.default={true},
+ annotate~if/.code={
+ \tl_if_eq:nnTF {#1} {true}
+ {
+ \bool_set_true:N \l__cal_annotate_bool
+ }
+ {
+ \bool_set_false:N \l__cal_annotate_bool
+ }
+ },
+ annotate~reset/.code={
+ \int_gzero:N \l__cal_label_int
+ },
+ annotation~style/.initial={draw,->},
+ annotation~shift/.initial={(0,1ex)},
+ every~annotation~node/.initial={anchor=south~west},
+ annotation~node~style/.code~2~args={
+ \tl_set:cn {l__cal_annotation_style_ #1 _tl}{#2}
+ },
+ tl~use:N/.code={
+ \exp_args:NV \pgfkeysalso #1
+ },
+ tl~use:c/.code={
+ \tl_if_exist:cT {#1}
+ {
+ \exp_args:Nv \pgfkeysalso {#1}
+ }
+ },
+ /handlers/.update~style/.code={
+ \tl_if_eq:nnF {#1} {\pgfkeysnovalue}
+ {
+ \pgfkeys{\pgfkeyscurrentpath/.code=\pgfkeysalso{#1}}
+ }
+ },
+ /handlers/.update~value/.code={
+ \tl_if_eq:nnF {#1} {\pgfkeysnovalue}
+ {
+ \pgfkeyssetvalue{\pgfkeyscurrentpath}{#1}
+ }
+ }
+}
+% \end{macrocode}
+%
+% Some wrappers around the TikZ keys.
+% \begin{macrocode}
+\NewDocumentCommand \pen { O{} }
+{
+ \path[define~ pen,every~ calligraphy~ pen/.try,#1]
+}
+
+\NewDocumentCommand \definepen { O{} }
+{
+ \tikz \path[define~ pen,every~ calligraphy~ pen/.try,#1]
+}
+
+\NewDocumentCommand \calligraphy { O{} }
+{
+ \path[use~ pen,every~ calligraphy/.try,#1]
+}
+% \end{macrocode}
+%
+% \subsection{The Path Creation}
+%
+% \begin{macro}{\CalligraphyPathCreate}
+% This is the main command for creating the calligraphic paths.
+% \begin{macrocode}
+\NewDocumentCommand \CalligraphyPathCreate { m m }
+{
+ \int_zero:N \l__cal_tmpa_int
+ \tl_map_inline:cn {l__spath_list_#1}
+ {
+ \int_incr:N \l__cal_tmpa_int
+ \int_zero:N \l__cal_tmpb_int
+ \tl_map_inline:cn {l__spath_list_calligraphy pen #2}
+ {
+ \int_incr:N \l__cal_tmpb_int
+ \group_begin:
+ \pgfsys@beginscope
+
+ \cal_apply_style:c {l__cal_stroke_style_ \int_use:N \l__cal_tmpa_int}
+ \cal_apply_style:c {l__cal_stroke_inline_style_ \int_use:N \l__cal_tmpa_int}
+ \cal_apply_style:c {l__cal_nib_style_ \int_use:N \l__cal_tmpb_int}
+
+ \spath_clone:nn {##1} {calligraphy temp path}
+
+ \__spath_get:nnN {####1} {initial point} \l__cal_tmpa_tl
+ \spath_translate:nV {calligraphy temp path} \l__cal_tmpa_tl
+
+ \__spath_get:nnN {####1} {length} \l__cal_tmpa_tl
+
+ \int_compare:nTF {\l__cal_tmpa_tl = 1}
+ {
+ \cal_at_least_three:n {calligraphy temp path}
+
+ \spath_protocol_path:n {calligraphy temp path}
+
+ \__spath_get:nnN {calligraphy temp path} {path} \l__cal_tmpa_tl
+
+ \tikz@options
+ \dim_set:Nn \l__cal_line_width_dim {\pgflinewidth}
+ \cal_maybe_taper:N \l__cal_tmpa_tl
+ }
+ {
+
+ \spath_weld:nn {calligraphy temp path} {####1}
+ \spath_reverse:n {##1}
+ \spath_reverse:n {####1}
+ \spath_weld:nn {calligraphy temp path} {##1}
+ \spath_weld:nn {calligraphy temp path} {####1}
+ \spath_reverse:n {##1}
+ \spath_reverse:n {####1}
+
+ \tl_clear:N \l__cal_tmpa_tl
+ \tl_set:Nn \l__cal_tmpa_tl {fill=\pgfkeysvalueof{/tikz/pen~colour},draw=none}
+ \tl_if_exist:cT {l__cal_stroke_style_ \int_use:N \l__cal_tmpa_int}
+ {
+ \tl_put_right:Nv \l__cal_tmpa_tl {l__cal_stroke_style_ \int_use:N \l__cal_tmpa_int}
+ }
+ \tl_if_exist:cT {l__cal_stroke_inline_style_ \int_use:N \l__cal_tmpa_int}
+ {
+ \tl_put_right:Nn \l__cal_tmpa_tl {,}
+ \tl_put_right:Nv \l__cal_tmpa_tl {l__cal_stroke_inline_style_ \int_use:N \l__cal_tmpa_int}
+ }
+ \tl_if_exist:cT {l__cal_nib_style_ \int_use:N \l__cal_tmpb_int}
+ {
+ \tl_put_right:Nn \l__cal_tmpa_tl {,}
+ \tl_put_right:Nv \l__cal_tmpa_tl {l__cal_nib_style_ \int_use:N \l__cal_tmpb_int}
+ }
+ \spath_tikz_path:Vn \l__cal_tmpa_tl {calligraphy temp path}
+
+ }
+ \pgfsys@endscope
+ \group_end:
+ }
+ \bool_if:NT \l__cal_annotate_bool
+ {
+ \spath_clone:nn {##1} {calligraphy temp path}
+ \tl_set_eq:Nc \l_tmpa_tl {l__spath_list_calligraphy pen #2}
+ \tl_reverse:N \l_tmpa_tl
+ \tl_set:Nx \l_tmpa_tl {\tl_head:N \l_tmpa_tl}
+ \spath_generate_finalpoint:V \l_tmpa_tl
+ \spath_get:VnN \l_tmpa_tl {final point} \l_tmpa_tl
+ \spath_translate:nV {calligraphy temp path} \l_tmpa_tl
+ \tikz@scan@one@point\pgfutil@firstofone\pgfkeysvalueof{/tikz/annotation~shift}
+ \spath_translate:nnn {calligraphy temp path} {\pgf@x} {\pgf@y}
+
+ \pgfkeysgetvalue{/tikz/annotation~style}{\l_tmpa_tl}
+ \spath_tikz_path:Vn \l_tmpa_tl {calligraphy temp path}
+ \spath_get:nnN {calligraphy temp path} {final point} \l_tmpa_tl
+ \exp_last_unbraced:NV \pgfqpoint \l_tmpa_tl
+ \begin{scope}[reset~ cm]
+ \node[every~annotation~node/.try,tl~use:c = {l__cal_annotation_style_ \int_use:N \l__cal_tmpa_int _tl}] at (\pgf@x,\pgf@y) {\int_use:N \l__cal_tmpa_int};
+ \end{scope}
+ }
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\cal_moveto:n}
+% When creating the path, we need to keep track of the number of components so that we can apply styles accordingly.
+% \begin{macrocode}
+\cs_new_eq:NN \cal_orig_moveto:n \pgfpathmoveto
+\cs_new_nopar:Npn \cal_moveto:n #1
+{
+ \int_gincr:N \l__cal_path_component_int
+ \cal_orig_moveto:n {#1}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\cal_apply_style:N}
+% Interface for applying \Verb+\tikzset+ to a token list.
+% \begin{macrocode}
+\cs_new_nopar:Npn \cal_apply_style:N #1
+{
+ \tl_if_exist:NT #1 {
+ \exp_args:NV \tikzset #1
+ }
+}
+\cs_generate_variant:Nn \cal_apply_style:N {c}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\cal_at_least_three:n}
+% A tapered path has to have at least three components.
+% This figures out if it is necessary and sets up the splitting.
+% \begin{macrocode}
+\cs_new_nopar:Npn \cal_at_least_three:n #1
+{
+ \spath_get:nnN {#1} {real length} \l__cal_tmpa_tl
+ \tl_clear:N \l__cal_tmpb_tl
+ \int_compare:nTF {\l__cal_tmpa_tl = 1}
+ {
+ \spath_get:nnN {#1} {path} \l__cal_tmpa_tl
+ \spath_map_segment_inline:Nn \l__cal_tmpa_tl
+ {
+ \tl_case:NnF ##1 {
+ \g__spath_lineto_tl {
+ \cal_split_line_in_three:NN \l__cal_tmpb_tl ##2
+ }
+ \g__spath_curvetoa_tl {
+ \cal_split_curve_in_three:NN \l__cal_tmpb_tl ##2
+ }
+ }
+ {
+ \tl_put_right:NV \l__cal_tmpb_tl ##2
+ }
+ }
+ \spath_put:nnV {#1} {path} \l__cal_tmpb_tl
+ }
+ {
+ \int_compare:nT {\l__cal_tmpa_tl = 2}
+ {
+ \spath_get:nnN {#1} {path} \l__cal_tmpa_tl
+ \spath_map_segment_inline:Nn \l__cal_tmpa_tl
+ {
+ \tl_case:NnF ##1 {
+ \g__spath_lineto_tl {
+ \cal_split_line_in_two:NN \l__cal_tmpb_tl ##2
+ }
+ \g__spath_curvetoa_tl {
+ \cal_split_curve_in_two:NN \l__cal_tmpb_tl ##2
+ }
+ }
+ {
+ \tl_put_right:NV \l__cal_tmpb_tl ##2
+ }
+ }
+ \spath_put:nnV {#1} {path} \l__cal_tmpb_tl
+ }
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\cal_split_line_in_two:NN}
+% Splits a line in two, adding the splits to the first token list.
+% \begin{macrocode}
+\cs_new_nopar:Npn \cal_split_line_in_two:NN #1#2
+{
+ \tl_set_eq:NN \l__cal_tmpc_tl #2
+
+ \tl_set:Nx \l__cal_tmpc_tl {\tl_tail:N \l__cal_tmpc_tl}
+
+ \dim_set:Nn \l__cal_tmpa_dim {\tl_head:N \l__cal_tmpc_tl}
+ \tl_set:Nx \l__cal_tmpc_tl {\tl_tail:N \l__cal_tmpc_tl}
+
+ \dim_set:Nn \l__cal_tmpb_dim {\tl_head:N \l__cal_tmpc_tl}
+ \tl_set:Nx \l__cal_tmpc_tl {\tl_tail:N \l__cal_tmpc_tl}
+
+ \tl_set:Nx \l__cal_tmpc_tl {\tl_tail:N \l__cal_tmpc_tl}
+
+ \dim_set:Nn \l__cal_tmpc_dim {\tl_head:N \l__cal_tmpc_tl}
+ \tl_set:Nx \l__cal_tmpc_tl {\tl_tail:N \l__cal_tmpc_tl}
+ \dim_set:Nn \l__cal_tmpd_dim {\tl_head:N \l__cal_tmpc_tl}
+ \tl_set:Nx \l__cal_tmpc_tl {\tl_tail:N \l__cal_tmpc_tl}
+
+ \tl_put_right:NV #1 \g__spath_lineto_tl
+
+ \tl_put_right:Nx #1 {
+ {\dim_eval:n {(\l__cal_tmpa_dim + \l__cal_tmpc_dim)/2}}
+ {\dim_eval:n {(\l__cal_tmpb_dim + \l__cal_tmpd_dim)/2}}
+ }
+
+ \tl_put_right:NV #1 \g__spath_lineto_tl
+ \tl_put_right:Nx #1 {
+ {\dim_use:N \l__cal_tmpc_dim}
+ {\dim_use:N \l__cal_tmpd_dim}
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\cal_split_line_in_three:NN}
+% Splits a line in three, adding the splits to the first token list.
+% \begin{macrocode}
+\cs_new_nopar:Npn \cal_split_line_in_three:NN #1#2
+{
+ \tl_set_eq:NN \l__cal_tmpc_tl #2
+
+ \tl_set:Nx \l__cal_tmpc_tl {\tl_tail:N \l__cal_tmpc_tl}
+
+ \dim_set:Nn \l__cal_tmpa_dim {\tl_head:N \l__cal_tmpc_tl}
+ \tl_set:Nx \l__cal_tmpc_tl {\tl_tail:N \l__cal_tmpc_tl}
+
+ \dim_set:Nn \l__cal_tmpb_dim {\tl_head:N \l__cal_tmpc_tl}
+ \tl_set:Nx \l__cal_tmpc_tl {\tl_tail:N \l__cal_tmpc_tl}
+
+ \tl_set:Nx \l__cal_tmpc_tl {\tl_tail:N \l__cal_tmpc_tl}
+
+ \dim_set:Nn \l__cal_tmpc_dim {\tl_head:N \l__cal_tmpc_tl}
+ \tl_set:Nx \l__cal_tmpc_tl {\tl_tail:N \l__cal_tmpc_tl}
+ \dim_set:Nn \l__cal_tmpd_dim {\tl_head:N \l__cal_tmpc_tl}
+ \tl_set:Nx \l__cal_tmpc_tl {\tl_tail:N \l__cal_tmpc_tl}
+
+ \tl_put_right:NV #1 \g__spath_lineto_tl
+
+ \tl_put_right:Nx #1 {
+ {\dim_eval:n {(2\l__cal_tmpa_dim + \l__cal_tmpc_dim)/3}}
+ {\dim_eval:n {(2\l__cal_tmpb_dim + \l__cal_tmpd_dim)/3}}
+ }
+
+ \tl_put_right:NV #1 \g__spath_lineto_tl
+
+ \tl_put_right:Nx #1 {
+ {\dim_eval:n {(\l__cal_tmpa_dim + 2\l__cal_tmpc_dim)/3}}
+ {\dim_eval:n {(\l__cal_tmpb_dim + 2\l__cal_tmpd_dim)/3}}
+ }
+
+ \tl_put_right:NV #1 \g__spath_lineto_tl
+ \tl_put_right:Nx #1 {
+ {\dim_use:N \l__cal_tmpc_dim}
+ {\dim_use:N \l__cal_tmpd_dim}
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\cal_split_curve_in_two:NN}
+% Splits a curve in two, adding the splits to the first token list.
+% \begin{macrocode}
+\cs_new_nopar:Npn \cal_split_curve_in_two:NN #1#2
+{
+ \spath_split_curve:nVNN {.5} #2 \l_tmpa_tl \l_tmpb_tl
+ \tl_set:Nx \l_tmpa_tl {\tl_tail:N \l_tmpa_tl}
+ \tl_set:Nx \l_tmpa_tl {\tl_tail:N \l_tmpa_tl}
+ \tl_set:Nx \l_tmpa_tl {\tl_tail:N \l_tmpa_tl}
+ \tl_set:Nx \l_tmpb_tl {\tl_tail:N \l_tmpb_tl}
+ \tl_set:Nx \l_tmpb_tl {\tl_tail:N \l_tmpb_tl}
+ \tl_set:Nx \l_tmpb_tl {\tl_tail:N \l_tmpb_tl}
+ \tl_put_right:NV #1 \l_tmpa_tl
+ \tl_put_right:NV #1 \l_tmpb_tl
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\cal_split_curve_in_three:NN}
+% Splits a curve in three, adding the splits to the first token list.
+% \begin{macrocode}
+\cs_new_nopar:Npn \cal_split_curve_in_three:NN #1#2
+{
+ \spath_split_curve:nVNN {1/3} #2 \l_tmpa_tl \l_tmpb_tl
+
+ \tl_set:Nx \l_tmpa_tl {\tl_tail:N \l_tmpa_tl}
+ \tl_set:Nx \l_tmpa_tl {\tl_tail:N \l_tmpa_tl}
+ \tl_set:Nx \l_tmpa_tl {\tl_tail:N \l_tmpa_tl}
+ \tl_put_right:NV #1 \l_tmpa_tl
+
+ \spath_split_curve:nVNN {.5} \l_tmpb_tl \l_tmpa_tl \l_tmpb_tl
+ \tl_set:Nx \l_tmpa_tl {\tl_tail:N \l_tmpa_tl}
+ \tl_set:Nx \l_tmpa_tl {\tl_tail:N \l_tmpa_tl}
+ \tl_set:Nx \l_tmpa_tl {\tl_tail:N \l_tmpa_tl}
+ \tl_set:Nx \l_tmpb_tl {\tl_tail:N \l_tmpb_tl}
+ \tl_set:Nx \l_tmpb_tl {\tl_tail:N \l_tmpb_tl}
+ \tl_set:Nx \l_tmpb_tl {\tl_tail:N \l_tmpb_tl}
+ \tl_put_right:NV #1 \l_tmpa_tl
+ \tl_put_right:NV #1 \l_tmpb_tl
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\cal_maybe_taper:N}
+% Possibly tapers the path, depending on the booleans.
+% \begin{macrocode}
+\cs_new_nopar:Npn \cal_maybe_taper:N #1
+{
+ \tl_set_eq:NN \l__cal_tmpa_tl #1
+
+ \bool_if:NT \l__cal_taper_start_bool
+ {
+
+ \dim_set:Nn \l__cal_tmpa_dim {\tl_item:Nn \l__cal_tmpa_tl {2}}
+ \dim_set:Nn \l__cal_tmpb_dim {\tl_item:Nn \l__cal_tmpa_tl {3}}
+ \tl_set:Nx \l__cal_tmpb_tl {\tl_item:Nn \l__cal_tmpa_tl {4}}
+
+ \tl_case:NnF \l__cal_tmpb_tl
+ {
+ \g__spath_lineto_tl
+ {
+
+ \bool_set_true:N \l__cal_taperable_bool
+ \dim_set:Nn \l__cal_tmpg_dim {\tl_item:Nn \l__cal_tmpa_tl {5}}
+ \dim_set:Nn \l__cal_tmph_dim {\tl_item:Nn \l__cal_tmpa_tl {6}}
+ \dim_set:Nn \l__cal_tmpc_dim {(2\l__cal_tmpa_dim + \l__cal_tmpg_dim)/3}
+ \dim_set:Nn \l__cal_tmpd_dim {(2\l__cal_tmpb_dim + \l__cal_tmph_dim)/3}
+ \dim_set:Nn \l__cal_tmpe_dim {(\l__cal_tmpa_dim + 2\l__cal_tmpg_dim)/3}
+ \dim_set:Nn \l__cal_tmpf_dim {(\l__cal_tmpb_dim + 2\l__cal_tmph_dim)/3}
+ \prg_replicate:nn {4}
+ {
+ \tl_set:Nx \l__cal_tmpa_tl {\tl_tail:N \l__cal_tmpa_tl}
+ }
+ \tl_put_left:NV \l__cal_tmpa_tl \g__spath_moveto_tl
+ }
+ \g__spath_curvetoa_tl
+ {
+ \bool_set_true:N \l__cal_taperable_bool
+ \dim_set:Nn \l__cal_tmpc_dim {\tl_item:Nn \l__cal_tmpa_tl {5}}
+ \dim_set:Nn \l__cal_tmpd_dim {\tl_item:Nn \l__cal_tmpa_tl {6}}
+ \dim_set:Nn \l__cal_tmpe_dim {\tl_item:Nn \l__cal_tmpa_tl {8}}
+ \dim_set:Nn \l__cal_tmpf_dim {\tl_item:Nn \l__cal_tmpa_tl {9}}
+ \dim_set:Nn \l__cal_tmpg_dim {\tl_item:Nn \l__cal_tmpa_tl {11}}
+ \dim_set:Nn \l__cal_tmph_dim {\tl_item:Nn \l__cal_tmpa_tl {12}}
+ \prg_replicate:nn {10}
+ {
+ \tl_set:Nx \l__cal_tmpa_tl {\tl_tail:N \l__cal_tmpa_tl}
+ }
+ \tl_put_left:NV \l__cal_tmpa_tl \g__spath_moveto_tl
+ }
+ }
+ {
+ \bool_set_false:N \l__cal_taperable_bool
+ }
+
+ \bool_if:NT \l__cal_taperable_bool
+ {
+ \__cal_taper_aux:
+ }
+
+ }
+
+ \bool_if:NT \l__cal_taper_end_bool
+ {
+
+ \dim_set:Nn \l__cal_tmpa_dim {\tl_item:Nn \l__cal_tmpa_tl {-2}}
+ \dim_set:Nn \l__cal_tmpb_dim {\tl_item:Nn \l__cal_tmpa_tl {-1}}
+ \tl_set:Nx \l__cal_tmpb_tl {\tl_item:Nn \l__cal_tmpa_tl {-3}}
+
+ \tl_case:NnF \l__cal_tmpb_tl
+ {
+ \g__spath_lineto_tl
+ {
+
+ \bool_set_true:N \l__cal_taperable_bool
+ \dim_set:Nn \l__cal_tmpg_dim {\tl_item:Nn \l__cal_tmpa_tl {-5}}
+ \dim_set:Nn \l__cal_tmph_dim {\tl_item:Nn \l__cal_tmpa_tl {-4}}
+ \dim_set:Nn \l__cal_tmpc_dim {(2\l__cal_tmpa_dim + \l__cal_tmpg_dim)/3}
+ \dim_set:Nn \l__cal_tmpd_dim {(2\l__cal_tmpb_dim + \l__cal_tmph_dim)/3}
+ \dim_set:Nn \l__cal_tmpe_dim {(\l__cal_tmpa_dim + 2\l__cal_tmpg_dim)/3}
+ \dim_set:Nn \l__cal_tmpf_dim {(\l__cal_tmpb_dim + 2\l__cal_tmph_dim)/3}
+ \tl_reverse:N \l__cal_tmpa_tl
+ \prg_replicate:nn {3}
+ {
+ \tl_set:Nx \l__cal_tmpa_tl {\tl_tail:N \l__cal_tmpa_tl}
+ }
+ \tl_reverse:N \l__cal_tmpa_tl
+ }
+ \g__spath_curveto_tl
+ {
+ \bool_set_true:N \l__cal_taperable_bool
+ \dim_set:Nn \l__cal_tmpc_dim {\tl_item:Nn \l__cal_tmpa_tl {-5}}
+ \dim_set:Nn \l__cal_tmpd_dim {\tl_item:Nn \l__cal_tmpa_tl {-4}}
+ \dim_set:Nn \l__cal_tmpe_dim {\tl_item:Nn \l__cal_tmpa_tl {-8}}
+ \dim_set:Nn \l__cal_tmpf_dim {\tl_item:Nn \l__cal_tmpa_tl {-7}}
+ \dim_set:Nn \l__cal_tmpg_dim {\tl_item:Nn \l__cal_tmpa_tl {-11}}
+ \dim_set:Nn \l__cal_tmph_dim {\tl_item:Nn \l__cal_tmpa_tl {-10}}
+ \tl_reverse:N \l__cal_tmpa_tl
+ \prg_replicate:nn {9}
+ {
+ \tl_set:Nx \l__cal_tmpa_tl {\tl_tail:N \l__cal_tmpa_tl}
+ }
+ \tl_reverse:N \l__cal_tmpa_tl
+ }
+ }
+ {
+ \bool_set_false:N \l__cal_taperable_bool
+ }
+
+ \bool_if:NT \l__cal_taperable_bool
+ {
+ \__cal_taper_aux:
+ }
+
+ }
+
+ \pgfsyssoftpath@setcurrentpath\l__cal_tmpa_tl
+ \pgfsetstrokecolor{\pgfkeysvalueof{/tikz/pen~colour}}
+ \pgfusepath{stroke}
+
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\__cal_taper_aux:}
+% Auxiliary macro to avoid unnecessary code duplication.
+% \begin{macrocode}
+\cs_new_nopar:Npn \__cal_taper_aux:
+{
+ \tl_clear:N \l__cal_tmpb_tl
+ \tl_put_right:NV \l__cal_tmpb_tl \g__spath_moveto_tl
+
+ \fp_set:Nn \l__cal_tmpa_fp
+ {
+ \l__cal_tmpd_dim - \l__cal_tmpb_dim
+ }
+ \fp_set:Nn \l__cal_tmpb_fp
+ {
+ \l__cal_tmpa_dim - \l__cal_tmpc_dim
+ }
+ \fp_set:Nn \l__cal_tmpe_fp
+ {
+ (\l__cal_tmpa_fp^2 + \l__cal_tmpb_fp^2)^.5
+ }
+
+ \fp_set:Nn \l__cal_tmpa_fp {.5*\l__cal_taper_width_dim * \l__cal_tmpa_fp / \l__cal_tmpe_fp}
+ \fp_set:Nn \l__cal_tmpb_fp {.5*\l__cal_taper_width_dim * \l__cal_tmpb_fp / \l__cal_tmpe_fp}
+
+ \fp_set:Nn \l__cal_tmpc_fp
+ {
+ \l__cal_tmph_dim - \l__cal_tmpf_dim
+ }
+ \fp_set:Nn \l__cal_tmpd_fp
+ {
+ \l__cal_tmpe_dim - \l__cal_tmpg_dim
+ }
+ \fp_set:Nn \l__cal_tmpe_fp
+ {
+ (\l__cal_tmpc_fp^2 + \l__cal_tmpd_fp^2)^.5
+ }
+
+ \fp_set:Nn \l__cal_tmpc_fp {.5*\l__cal_line_width_dim * \l__cal_tmpc_fp / \l__cal_tmpe_fp}
+ \fp_set:Nn \l__cal_tmpd_fp {.5*\l__cal_line_width_dim * \l__cal_tmpd_fp / \l__cal_tmpe_fp}
+
+ \tl_put_right:Nx \l__cal_tmpb_tl
+ {
+ {\dim_eval:n { \fp_to_dim:N \l__cal_tmpa_fp + \l__cal_tmpa_dim}}
+ {\dim_eval:n { \fp_to_dim:N \l__cal_tmpb_fp + \l__cal_tmpb_dim}}
+ }
+
+ \tl_put_right:NV \l__cal_tmpb_tl \g__spath_curvetoa_tl
+
+ \tl_put_right:Nx \l__cal_tmpb_tl
+ {
+ {\dim_eval:n { \fp_to_dim:N \l__cal_tmpa_fp + \l__cal_tmpc_dim}}
+ {\dim_eval:n { \fp_to_dim:N \l__cal_tmpb_fp + \l__cal_tmpd_dim}}
+ }
+
+ \tl_put_right:NV \l__cal_tmpb_tl \g__spath_curvetob_tl
+
+ \tl_put_right:Nx \l__cal_tmpb_tl
+ {
+ {\dim_eval:n { \fp_to_dim:N \l__cal_tmpc_fp + \l__cal_tmpe_dim}}
+ {\dim_eval:n { \fp_to_dim:N \l__cal_tmpd_fp + \l__cal_tmpf_dim}}
+ }
+
+ \tl_put_right:NV \l__cal_tmpb_tl \g__spath_curveto_tl
+
+ \tl_put_right:Nx \l__cal_tmpb_tl
+ {
+ {\dim_eval:n { \fp_to_dim:N \l__cal_tmpc_fp + \l__cal_tmpg_dim}}
+ {\dim_eval:n { \fp_to_dim:N \l__cal_tmpd_fp + \l__cal_tmph_dim}}
+ }
+
+ \tl_put_right:NV \l__cal_tmpb_tl \g__spath_curvetoa_tl
+
+ \tl_put_right:Nx \l__cal_tmpb_tl
+ {
+ {\dim_eval:n { \fp_to_dim:N \l__cal_tmpc_fp + \l__cal_tmpg_dim - \fp_to_dim:n{ 1.32 * \l__cal_tmpd_fp}}}
+ {\dim_eval:n { \fp_to_dim:N \l__cal_tmpd_fp + \l__cal_tmph_dim + \fp_to_dim:n {1.32* \l__cal_tmpc_fp}}}
+ }
+
+ \tl_put_right:NV \l__cal_tmpb_tl \g__spath_curvetob_tl
+
+ \tl_put_right:Nx \l__cal_tmpb_tl
+ {
+ {\dim_eval:n { -\fp_to_dim:N \l__cal_tmpc_fp + \l__cal_tmpg_dim - \fp_to_dim:n {1.32 * \l__cal_tmpd_fp}}}
+ {\dim_eval:n { -\fp_to_dim:N \l__cal_tmpd_fp + \l__cal_tmph_dim + \fp_to_dim:n {1.32 * \l__cal_tmpc_fp}}}
+ }
+
+ \tl_put_right:NV \l__cal_tmpb_tl \g__spath_curveto_tl
+
+ \tl_put_right:Nx \l__cal_tmpb_tl
+ {
+ {\dim_eval:n { -\fp_to_dim:N \l__cal_tmpc_fp + \l__cal_tmpg_dim}}
+ {\dim_eval:n { -\fp_to_dim:N \l__cal_tmpd_fp + \l__cal_tmph_dim}}
+ }
+
+ \tl_put_right:NV \l__cal_tmpb_tl \g__spath_curvetoa_tl
+
+ \tl_put_right:Nx \l__cal_tmpb_tl
+ {
+ {\dim_eval:n { -\fp_to_dim:N \l__cal_tmpc_fp + \l__cal_tmpe_dim}}
+ {\dim_eval:n { -\fp_to_dim:N \l__cal_tmpd_fp + \l__cal_tmpf_dim}}
+ }
+
+ \tl_put_right:NV \l__cal_tmpb_tl \g__spath_curvetob_tl
+
+ \tl_put_right:Nx \l__cal_tmpb_tl
+ {
+ {\dim_eval:n { -\fp_to_dim:N \l__cal_tmpa_fp + \l__cal_tmpc_dim}}
+ {\dim_eval:n { -\fp_to_dim:N \l__cal_tmpb_fp + \l__cal_tmpd_dim}}
+ }
+
+ \tl_put_right:NV \l__cal_tmpb_tl \g__spath_curveto_tl
+
+ \tl_put_right:Nx \l__cal_tmpb_tl
+ {
+ {\dim_eval:n { -\fp_to_dim:N \l__cal_tmpa_fp + \l__cal_tmpa_dim}}
+ {\dim_eval:n { -\fp_to_dim:N \l__cal_tmpb_fp + \l__cal_tmpb_dim}}
+ }
+
+ \tl_put_right:NV \l__cal_tmpb_tl \g__spath_curvetoa_tl
+
+ \tl_put_right:Nx \l__cal_tmpb_tl
+ {
+ {\dim_eval:n { -\fp_to_dim:N \l__cal_tmpa_fp + \l__cal_tmpa_dim + \fp_to_dim:n{ 1.32 * \l__cal_tmpb_fp}}}
+ {\dim_eval:n { -\fp_to_dim:N \l__cal_tmpb_fp + \l__cal_tmpb_dim - \fp_to_dim:n {1.32* \l__cal_tmpa_fp}}}
+ }
+
+ \tl_put_right:NV \l__cal_tmpb_tl \g__spath_curvetob_tl
+
+ \tl_put_right:Nx \l__cal_tmpb_tl
+ {
+ {\dim_eval:n { \fp_to_dim:N \l__cal_tmpa_fp + \l__cal_tmpa_dim + \fp_to_dim:n {1.32 * \l__cal_tmpb_fp}}}
+ {\dim_eval:n { \fp_to_dim:N \l__cal_tmpb_fp + \l__cal_tmpb_dim - \fp_to_dim:n {1.32 * \l__cal_tmpa_fp}}}
+ }
+
+ \tl_put_right:NV \l__cal_tmpb_tl \g__spath_curveto_tl
+
+ \tl_put_right:Nx \l__cal_tmpb_tl
+ {
+ {\dim_eval:n { \fp_to_dim:N \l__cal_tmpa_fp + \l__cal_tmpa_dim}}
+ {\dim_eval:n { \fp_to_dim:N \l__cal_tmpb_fp + \l__cal_tmpb_dim}}
+ }
+
+ \pgfsyssoftpath@setcurrentpath\l__cal_tmpb_tl
+ \pgfsetfillcolor{\pgfkeysvalueof{/tikz/pen~colour}}
+ \pgfusepath{fill}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% Defines a copperplate pen.
+% \begin{macrocode}
+\tl_set:Nn \l__cal_tmpa_tl {\pgfsyssoftpath@movetotoken{0pt}{0pt}}
+\MakeSPathList{calligraphy pen copperplate}{\l__cal_tmpa_tl}
+\SPathListPrepare{calligraphy pen copperplate}
+% \end{macrocode}
+%
+% \begin{macrocode}
+\ExplSyntaxOff
+% \end{macrocode}
+%
+% \subsection{Decorations}
+%
+% If a decoration library is loaded we define some decorations that use the calligraphy library, specifically the copperplate pen with its tapering.
+%
+% First, a brace decoration.
+% \begin{macrocode}
+\expandafter\ifx\csname pgfdeclaredecoration\endcsname\relax
+\else
+\pgfdeclaredecoration{calligraphic brace}{brace}
+{
+ \state{brace}[width=+\pgfdecoratedremainingdistance,next state=final]
+ {
+ \pgfsyssoftpath@setcurrentpath{\pgfutil@empty}
+ \pgfpathmoveto{\pgfpointorigin}
+ \pgfpathcurveto
+ {\pgfqpoint{.15\pgfdecorationsegmentamplitude}{.3\pgfdecorationsegmentamplitude}}
+ {\pgfqpoint{.5\pgfdecorationsegmentamplitude}{.5\pgfdecorationsegmentamplitude}}
+ {\pgfqpoint{\pgfdecorationsegmentamplitude}{.5\pgfdecorationsegmentamplitude}}
+ {
+ \pgftransformxshift{+\pgfdecorationsegmentaspect\pgfdecoratedremainingdistance}
+ \pgfpathlineto{\pgfqpoint{-\pgfdecorationsegmentamplitude}{.5\pgfdecorationsegmentamplitude}}
+ \pgfpathcurveto
+ {\pgfqpoint{-.5\pgfdecorationsegmentamplitude}{.5\pgfdecorationsegmentamplitude}}
+ {\pgfqpoint{-.15\pgfdecorationsegmentamplitude}{.7\pgfdecorationsegmentamplitude}}
+ {\pgfqpoint{0\pgfdecorationsegmentamplitude}{1\pgfdecorationsegmentamplitude}}
+ \pgfpathmoveto{\pgfqpoint{0\pgfdecorationsegmentamplitude}{1\pgfdecorationsegmentamplitude}}
+ \pgfpathcurveto
+ {\pgfqpoint{.15\pgfdecorationsegmentamplitude}{.7\pgfdecorationsegmentamplitude}}
+ {\pgfqpoint{.5\pgfdecorationsegmentamplitude}{.5\pgfdecorationsegmentamplitude}}
+ {\pgfqpoint{\pgfdecorationsegmentamplitude}{.5\pgfdecorationsegmentamplitude}}
+ }
+ {
+ \pgftransformxshift{+\pgfdecoratedremainingdistance}
+ \pgfpathlineto{\pgfqpoint{-\pgfdecorationsegmentamplitude}{.5\pgfdecorationsegmentamplitude}}
+ \pgfpathcurveto
+ {\pgfqpoint{-.5\pgfdecorationsegmentamplitude}{.5\pgfdecorationsegmentamplitude}}
+ {\pgfqpoint{-.15\pgfdecorationsegmentamplitude}{.3\pgfdecorationsegmentamplitude}}
+ {\pgfqpoint{0pt}{0pt}}
+ }
+ \tikzset{
+ taper width=.5\pgflinewidth,
+ taper
+ }%
+ \pgfsyssoftpath@getcurrentpath\cal@tmp@path
+ \MakeSPathList{calligraphy path}{\cal@tmp@path}%
+ \SPathListPrepare{calligraphy path}%
+ \CalligraphyPathCreate{calligraphy path}{copperplate}%
+ }
+ \state{final}{}
+}
+% \end{macrocode}
+%
+% The second is a straightened parenthesis (so that when very large it doesn't bow out too far).
+% \begin{macrocode}
+\pgfdeclaredecoration{calligraphic straight parenthesis}{brace}
+{
+ \state{brace}[width=+\pgfdecoratedremainingdistance,next state=final]
+ {
+ \pgfsyssoftpath@setcurrentpath{\pgfutil@empty}
+ \pgfpathmoveto{\pgfpointorigin}
+ \pgfpathcurveto
+ {\pgfqpoint{.76604\pgfdecorationsegmentamplitude}{.64279\pgfdecorationsegmentamplitude}}
+ {\pgfqpoint{2.3333\pgfdecorationsegmentamplitude}{\pgfdecorationsegmentamplitude}}
+ {\pgfqpoint{3.3333\pgfdecorationsegmentamplitude}{\pgfdecorationsegmentamplitude}}
+ {
+ \pgftransformxshift{+\pgfdecoratedremainingdistance}
+ \pgfpathlineto{\pgfqpoint{-3.3333\pgfdecorationsegmentamplitude}{\pgfdecorationsegmentamplitude}}
+ \pgfpathcurveto
+ {\pgfqpoint{-2.3333\pgfdecorationsegmentamplitude}{\pgfdecorationsegmentamplitude}}
+ {\pgfqpoint{-.76604\pgfdecorationsegmentamplitude}{.64279\pgfdecorationsegmentamplitude}}
+ {\pgfqpoint{0pt}{0pt}}
+ }
+ \tikzset{
+ taper width=.5\pgflinewidth,
+ taper
+ }%
+ \pgfsyssoftpath@getcurrentpath\cal@tmp@path
+ \MakeSPathList{calligraphy path}{\cal@tmp@path}%
+ \SPathListPrepare{calligraphy path}%
+ \CalligraphyPathCreate{calligraphy path}{copperplate}%
+ }
+ \state{final}{}%
+}
+% \end{macrocode}
+%
+% The third is a curved parenthesis.
+% \begin{macrocode}
+\pgfdeclaredecoration{calligraphic curved parenthesis}{brace}
+{
+ \state{brace}[width=+\pgfdecoratedremainingdistance,next state=final]
+ {
+ \pgfsyssoftpath@setcurrentpath{\pgfutil@empty}
+ \pgfpathmoveto{\pgfpointorigin}
+ \pgf@xa=\pgfdecoratedremainingdistance\relax
+ \advance\pgf@xa by -1.5890\pgfdecorationsegmentamplitude\relax
+ \edef\cgrphy@xa{\the\pgf@xa}
+ \pgfpathcurveto
+ {\pgfqpoint{1.5890\pgfdecorationsegmentamplitude}{1.3333\pgfdecorationsegmentamplitude}}
+ {\pgfqpoint{\cgrphy@xa}{1.3333\pgfdecorationsegmentamplitude}}
+ {\pgfqpoint{\pgfdecoratedremainingdistance}{0pt}}
+ \tikzset{
+ taper width=.5\pgflinewidth,
+ taper
+ }%
+ \pgfsyssoftpath@getcurrentpath\cal@tmp@path
+ \MakeSPathList{calligraphy path}{\cal@tmp@path}%
+ \SPathListPrepare{calligraphy path}%
+ \CalligraphyPathCreate{calligraphy path}{copperplate}%
+ }
+ \state{final}{}%
+}
+% \end{macrocode}
+% End the conditional for if pgfdecoration module is loaded
+% \begin{macrocode}
+\fi
+% \end{macrocode}
+% \iffalse
+%</calligraphy>
+% \fi
+%
+%
+% \iffalse
+%<*knots>
+% \fi
+%%
+% \section{Drawing Knots}
+%
+%
+% \subsection{Initialisation}
+%
+% We load the \Verb+spath3+ library and the \Verb+intersections+ TikZ library.
+% Then we get going.
+% \begin{macrocode}
+\RequirePackage{spath3}
+\usetikzlibrary{intersections}
+
+\ExplSyntaxOn
+
+\tl_new:N \l__knot_tmpa_tl
+\tl_new:N \l__knot_tmpb_tl
+\tl_new:N \l__knot_tmpc_tl
+\tl_new:N \l__knot_tmpd_tl
+\tl_new:N \l__knot_tmpe_tl
+\tl_new:N \l__knot_tmpf_tl
+\tl_new:N \l__knot_tmpg_tl
+\tl_new:N \l__knot_redraws_tl
+\tl_new:N \l__knot_clip_width_tl
+\tl_new:N \l__knot_name_tl
+\tl_new:N \l__knot_node_tl
+\tl_new:N \l__knot_aux_tl
+\tl_new:N \l__knot_auxa_tl
+
+\int_new:N \l__knot_tmpa_int
+\int_new:N \l__knot_strands_int
+\int_new:N \l__knot_intersections_int
+\int_new:N \l__knot_filaments_int
+\int_new:N \l__knot_component_start_int
+
+\dim_new:N \l__knot_tmpa_dim
+\dim_new:N \l__knot_tmpb_dim
+\dim_new:N \l__knot_tmpc_dim
+\dim_new:N \l__knot_tolerance_dim
+\dim_new:N \l__knot_clip_bg_radius_dim
+\dim_new:N \l__knot_clip_draw_radius_dim
+
+\bool_new:N \l__knot_draft_bool
+\bool_new:N \l__knot_ignore_ends_bool
+\bool_new:N \l__knot_self_intersections_bool
+\bool_new:N \l__knot_splits_bool
+\bool_new:N \l__knot_super_draft_bool
+
+\bool_new:N \l__knot_prepend_prev_bool
+\bool_new:N \l__knot_append_next_bool
+\bool_new:N \l__knot_skip_bool
+\bool_new:N \l__knot_save_bool
+
+\seq_new:N \l__knot_nodes_seq
+
+\bool_set_true:N \l__knot_ignore_ends_bool
+% \end{macrocode}
+%
+% Configuration is via TikZ keys and styles.
+% \begin{macrocode}
+\tikzset{
+ knot/.code={
+ \tl_if_eq:nnTF {#1} {none}
+ {
+ \tikz@addmode{\tikz@mode@doublefalse}
+ }
+ {
+ \tikz@addmode{\tikz@mode@doubletrue}
+ \tl_if_eq:nnTF {\pgfkeysnovalue} {#1}
+ {
+ \tikz@addoption{\pgfsetinnerstrokecolor{.}}
+ }
+ {
+ \pgfsetinnerstrokecolor{#1}
+ }
+ \tikz@addoption{
+ \pgfsetstrokecolor{knotbg}
+ }
+ \tl_set:Nn \tikz@double@setup{
+ \pgfsetinnerlinewidth{\pgflinewidth}
+ \pgfsetlinewidth{\dim_eval:n {\tl_use:N \l__knot_gap_tl \pgflinewidth}}
+ }
+ }
+ },
+ knot~ gap/.store~ in=\l__knot_gap_tl,
+ knot~ gap=3,
+ knot~ diagram/.is~family,
+ knot~ diagram/.unknown/.code={
+ \tl_set_eq:NN \l__knot_tmpa_tl \pgfkeyscurrentname
+ \pgfkeysalso{
+ /tikz/\l__knot_tmpa_tl=#1
+ }
+ },
+ background~ colour/.code={%
+ \colorlet{knotbg}{#1}%
+ },
+ background~ color/.code={%
+ \colorlet{knotbg}{#1}%
+ },
+ background~ colour=white,
+ knot~ diagram,
+ name/.store~ in=\l__knot_name_tl,
+ name={knot},
+ save~ intersections/.is~ choice,
+ save~ intersections/.default=true,
+ save~ intersections/true/.code={
+ \bool_set_true:N \l__knot_save_bool
+ },
+ save~ intersections/false/.code={
+ \bool_set_false:N \l__knot_save_bool
+ },
+ every~ strand/.style={draw},
+ ignore~ endpoint~ intersections/.code={
+ \tl_if_eq:nnTF {#1} {true}
+ {
+ \bool_set_true:N \l__knot_ignore_ends_bool
+ }
+ {
+ \bool_set_false:N \l__knot_ignore_ends_bool
+ }
+ },
+ ignore~ endpoint~ intersections/.default=true,
+ consider~ self~ intersections/.is~choice,
+ consider~ self~ intersections/true/.code={
+ \bool_set_true:N \l__knot_self_intersections_bool
+ \bool_set_true:N \l__knot_splits_bool
+ },
+ consider~ self~ intersections/false/.code={
+ \bool_set_false:N \l__knot_self_intersections_bool
+ \bool_set_false:N \l__knot_splits_bool
+ },
+ consider~ self~ intersections/no~ splits/.code={
+ \bool_set_true:N \l__knot_self_intersections_bool
+ \bool_set_false:N \l__knot_splits_bool
+ },
+ consider~ self~ intersections/.default={true},
+ clip~ radius/.code={
+ \dim_set:Nn \l__knot_clip_bg_radius_dim {#1}
+ \dim_set:Nn \l__knot_clip_draw_radius_dim {#1+2pt}
+ },
+ clip~ draw~ radius/.code={
+ \dim_set:Nn \l__knot_clip_draw_radius_dim {#1}
+ },
+ clip~ background~ radius/.code={
+ \dim_set:Nn \l__knot_clip_bg_radius_dim {#1}
+ },
+ clip~ radius=10pt,
+ end~ tolerance/.code={
+ \dim_set:Nn \l__knot_tolerance_dim {#1}
+ },
+ end~ tolerance=14pt,
+ clip/.style={
+ clip
+ },
+ background~ clip/.style={
+ clip
+ },
+ clip~ width/.code={
+ \tl_set:Nn \l__knot_clip_width_tl {#1}
+ },
+ clip~ width=3,
+ flip~ crossing/.code={%
+ \tl_clear_new:c {l__knot_crossing_#1}
+ \tl_set:cn {l__knot_crossing_#1} {x}
+ },
+ ignore~ crossing/.code={%
+ \tl_clear_new:c {l__knot_ignore_crossing_#1}
+ \tl_set:cn {l__knot_ignore_crossing_#1} {x}
+ },
+ draft~ mode/.is~ choice,
+ draft~ mode/off/.code={%
+ \bool_set_false:N \l__knot_draft_bool
+ \bool_set_false:N \l__knot_super_draft_bool
+ },
+ draft~ mode/crossings/.code={%
+ \bool_set_true:N \l__knot_draft_bool
+ \bool_set_false:N \l__knot_super_draft_bool
+ },
+ draft~ mode/strands/.code={%
+ \bool_set_true:N \l__knot_draft_bool
+ \bool_set_true:N \l__knot_super_draft_bool
+ },
+ draft/.is~ family,
+ draft,
+ crossing~ label/.style={
+ overlay,
+ fill=white,
+ fill~ opacity=.5,
+ text~ opacity=1,
+ text=blue,
+ pin~ edge={blue,<-}
+ },
+ strand~ label/.style={
+ overlay,
+ circle,
+ draw=purple,
+ fill=white,
+ fill~ opacity=.5,
+ text~ opacity=1,
+ text=purple,
+ inner~ sep=0pt
+ },
+}
+% \end{macrocode}
+%
+% Wrapper around \Verb+\tikzset+ for applying keys from a token list, checking for if the given token list exists.
+% \begin{macrocode}
+\cs_new_nopar:Npn \knot_apply_style:N #1
+{
+ \tl_if_exist:NT #1 {
+ \exp_args:NV \tikzset #1
+ }
+}
+\cs_generate_variant:Nn \knot_apply_style:N {c}
+% \end{macrocode}
+%
+%\begin{macro}{\flipcrossings}
+% The user can specify a comma separated list of crossings to flip.
+% \begin{macrocode}
+\NewDocumentCommand \flipcrossings {m}
+{
+ \tikzset{knot~ diagram/flip~ crossing/.list={#1}}%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\strand}
+% This is how the user specifies a strand of the knot.
+% \begin{macrocode}
+\NewDocumentCommand \strand { O{} }
+{
+ \int_incr:N \l__knot_strands_int
+ \tl_clear_new:c {l__knot_options_strand \int_use:N \l__knot_strands_int}
+ \tl_set:cn {l__knot_options_strand \int_use:N \l__knot_strands_int} {#1}
+ \path[#1,save~ spath=knot strand \int_use:N \l__knot_strands_int]
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{knot}
+% This is the wrapper environment that calls the knot generation code.
+% \begin{macrocode}
+\NewDocumentEnvironment{knot} { O{} }
+{
+ \knot_initialise:n {#1}
+}
+{
+ \knot_render:
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\knot_initialise:n}
+% Set up some stuff before loading in the strands.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \knot_initialise:n #1
+{
+ \tikzset{knot~ diagram/.cd,every~ knot~ diagram/.try,#1}
+ \int_zero:N \l__knot_strands_int
+ \tl_clear:N \l__knot_redraws_tl
+ \seq_gclear:N \l__knot_nodes_seq
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\knot_render:}
+% This is the code that starts the work of rendering the knot.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \knot_render:
+{
+% \end{macrocode}
+% Start a scope and reset the transformation (since all transformations have already been taken into account when defining the strands).
+% \begin{macrocode}
+ \pgfscope
+ \pgftransformreset
+% \end{macrocode}
+% Loop through the strands drawing each one for the first time.
+% \begin{macrocode}
+ \int_step_function:nnnN {1} {1} {\l__knot_strands_int} \knot_draw_strand:n
+% \end{macrocode}
+% In super draft mode we don't do anything else.
+% \begin{macrocode}
+ \bool_if:NF \l__knot_super_draft_bool
+ {
+% \end{macrocode}
+% In draft mode we draw labels at the ends of the strands; this also handles splitting curves to avoid self-intersections of Bezier curves if that's requested.
+% \begin{macrocode}
+ \int_step_function:nnnN {1} {1} {\l__knot_strands_int} \knot_draw_labels:n
+% \end{macrocode}
+% If we're considering self intersections we need to split the strands into filaments.
+% \begin{macrocode}
+ \bool_if:NTF \l__knot_self_intersections_bool
+ {
+ \knot_split_strands:
+ \int_set_eq:NN \l__knot_tmpa_int \l__knot_filaments_int
+ \tl_set:Nn \l__knot_prefix_tl {filament}
+ }
+ {
+ \int_set_eq:NN \l__knot_tmpa_int \l__knot_strands_int
+ \tl_set:Nn \l__knot_prefix_tl {strand}
+ }
+% \end{macrocode}
+% Initialise the intersection count.
+% \begin{macrocode}
+ \int_gzero:N \l__knot_intersections_int
+% \end{macrocode}
+% If in draft mode we label the intersections, otherwise we just stick a coordinate at each one.
+% \begin{macrocode}
+ \bool_if:NTF \l__knot_draft_bool
+ {
+ \tl_set:Nn \l__knot_node_tl {
+ \exp_not:N \node[coordinate,
+ pin={[node~ contents={\int_use:N \l__knot_intersections_int},knot~ diagram/draft/crossing~ label, knot~ diagram/draft/crossing~ \int_use:N \l__knot_intersections_int \c_space_tl label/.try]
+ }]
+ }
+ }
+ {
+ \tl_set:Nn \l__knot_node_tl {\exp_not:N \node[coordinate]}
+ }
+% \end{macrocode}
+% This double loop steps through the pieces (strands or filaments) and computes the intersections and does stuff with those.
+% \begin{macrocode}
+ \int_step_variable:nnnNn {1} {1} {\l__knot_tmpa_int - 1} \l__knot_tmpa_tl
+ {
+ \int_step_variable:nnnNn {\tl_use:N \l__knot_tmpa_tl + 1} {1} {\l__knot_tmpa_int} \l__knot_tmpb_tl
+ {
+ \knot_intersections:VV \l__knot_tmpa_tl \l__knot_tmpb_tl
+ }
+ }
+% \end{macrocode}
+% If any redraws were requested, do them here.
+% \begin{macrocode}
+ \tl_use:N \l__knot_redraws_tl
+% \end{macrocode}
+% Draw the crossing nodes
+% \begin{macrocode}
+ \seq_use:Nn \l__knot_nodes_seq {}
+ }
+% \end{macrocode}
+% Close the scope
+% \begin{macrocode}
+ \endpgfscope
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\knot_draw_strand:n}
+% This renders a strand using the options originally specified.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \knot_draw_strand:n #1
+{
+ \pgfscope
+ \group_begin:
+ \tl_set:Nn \l_tmpa_tl {knot~ diagram/every~ strand/.try,}
+ \tl_put_right:Nv \l_tmpa_tl {l__knot_options_strand #1}
+ \tl_put_right:Nn \l_tmpa_tl {,knot~ diagram/only~ when~ rendering/.try,only~ when~ rendering/.try}
+ \spath_bake_round:n {knot strand #1}
+ \spath_tikz_path:Vn \l_tmpa_tl {knot strand #1}
+ \group_end:
+ \endpgfscope
+}
+\cs_generate_variant:Nn \tl_put_right:Nn {Nv}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\knot_draw_labels:n}
+% Draw a label at each end of each strand, if in draft mode.
+% Also, if requested, split potentially self intersecting Bezier curves.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \knot_draw_labels:n #1
+{
+ \bool_if:NT \l__knot_draft_bool
+ {
+ \spath_get:nnN {knot strand #1} {final point} \l__knot_tmpb_tl
+ \dim_set:Nn \l__knot_tmpa_dim {\tl_item:Nn \l__knot_tmpb_tl {1}}
+ \dim_set:Nn \l__knot_tmpb_dim {\tl_item:Nn \l__knot_tmpb_tl {2}}
+ \node[knot~ diagram/draft/strand~label] at (\l__knot_tmpa_dim,\l__knot_tmpb_dim) {#1};
+ \spath_get:nnN {knot strand #1} {initial point} \l__knot_tmpb_tl
+ \dim_set:Nn \l__knot_tmpa_dim {\tl_item:Nn \l__knot_tmpb_tl {1}}
+ \dim_set:Nn \l__knot_tmpb_dim {\tl_item:Nn \l__knot_tmpb_tl {2}}
+ \node[knot~ diagram/draft/strand~label] at (\l__knot_tmpa_dim,\l__knot_tmpb_dim) {#1};
+ }
+ \bool_if:nT {
+ \l__knot_self_intersections_bool
+ &&
+ \l__knot_splits_bool
+ }
+ {
+ \tl_clear:N \l__knot_tmpa_tl
+ \spath_map_segment_function:nN {knot strand #1} \knot_split_self_intersects:NN
+ \spath_put:nnV {knot strand #1} {path} \l__knot_tmpa_tl
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\knot_split_self_intersects:NN}
+% This is the macro that does the split.
+% Figuring out whether a Bezier cubic self intersects is apparently a difficult problem so we don't bother.
+% We compute a point such that if there is an intersection then it lies on either side of the point.
+% I don't recall where the formula came from!
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \knot_split_self_intersects:NN #1#2
+{
+ \tl_case:NnF #1
+ {
+ \g__spath_curvetoa_tl
+ {
+ \fp_set:Nn \l_tmpa_fp
+ {
+ (\tl_item:Nn #2 {3} - 3 * \tl_item:Nn #2 {6} + 3 * \tl_item:Nn #2 {9} - \tl_item:Nn #2 {12})
+ *
+ (3 * \tl_item:Nn #2 {8} - 3 * \tl_item:Nn #2 {11})
+ -
+ (\tl_item:Nn #2 {2} - 3 * \tl_item:Nn #2 {5} + 3 * \tl_item:Nn #2 {8} - \tl_item:Nn #2 {11})
+ *
+ (3 * \tl_item:Nn #2 {9} - 3 * \tl_item:Nn #2 {12})
+ }
+ \fp_set:Nn \l_tmpb_fp
+ {
+ (\tl_item:Nn #2 {2} - 3 * \tl_item:Nn #2 {5} + 3 * \tl_item:Nn #2 {8} - \tl_item:Nn #2 {11})
+ *
+ (3 * \tl_item:Nn #2 {6} - 6 * \tl_item:Nn #2 {9} + 3 * \tl_item:Nn #2 {12})
+ -
+ (\tl_item:Nn #2 {3} - 3 * \tl_item:Nn #2 {6} + 3 * \tl_item:Nn #2 {9} - \tl_item:Nn #2 {12})
+ *
+ (3 * \tl_item:Nn #2 {5} - 6 * \tl_item:Nn #2 {8} + 3 * \tl_item:Nn #2 {11})
+ }
+ \fp_compare:nTF
+ {
+ \l_tmpb_fp != 0
+ }
+ {
+ \fp_set:Nn \l_tmpa_fp {.5 * \l_tmpa_fp / \l_tmpb_fp}
+ \fp_compare:nTF
+ {
+ 0 < \l_tmpa_fp && \l_tmpa_fp < 1
+ }
+ {
+ \spath_split_curve:VVNN \l_tmpa_fp #2 \l_tmpa_tl \l_tmpb_tl
+ \tl_set:Nx \l_tmpa_tl {\tl_tail:N \l_tmpa_tl}
+ \tl_set:Nx \l_tmpa_tl {\tl_tail:N \l_tmpa_tl}
+ \tl_set:Nx \l_tmpa_tl {\tl_tail:N \l_tmpa_tl}
+ \tl_set:Nx \l_tmpb_tl {\tl_tail:N \l_tmpb_tl}
+ \tl_set:Nx \l_tmpb_tl {\tl_tail:N \l_tmpb_tl}
+ \tl_set:Nx \l_tmpb_tl {\tl_tail:N \l_tmpb_tl}
+ \tl_put_right:NV \l__knot_tmpa_tl \l_tmpa_tl
+ \tl_put_right:NV \l__knot_tmpa_tl \l_tmpb_tl
+ }
+ {
+ \tl_set_eq:NN \l_tmpa_tl #2
+ \tl_set:Nx \l_tmpa_tl {\tl_tail:N \l_tmpa_tl}
+ \tl_set:Nx \l_tmpa_tl {\tl_tail:N \l_tmpa_tl}
+ \tl_set:Nx \l_tmpa_tl {\tl_tail:N \l_tmpa_tl}
+ \tl_put_right:NV \l__knot_tmpa_tl \l_tmpa_tl
+ }
+ }
+ {
+ \tl_set_eq:NN \l_tmpa_tl #2
+ \tl_set:Nx \l_tmpa_tl {\tl_tail:N \l_tmpa_tl}
+ \tl_set:Nx \l_tmpa_tl {\tl_tail:N \l_tmpa_tl}
+ \tl_set:Nx \l_tmpa_tl {\tl_tail:N \l_tmpa_tl}
+ \tl_put_right:NV \l__knot_tmpa_tl \l_tmpa_tl
+ }
+ }
+ \g__spath_lineto_tl
+ {
+ \tl_set_eq:NN \l_tmpa_tl #2
+ \tl_set:Nx \l_tmpa_tl {\tl_tail:N \l_tmpa_tl}
+ \tl_set:Nx \l_tmpa_tl {\tl_tail:N \l_tmpa_tl}
+ \tl_set:Nx \l_tmpa_tl {\tl_tail:N \l_tmpa_tl}
+ \tl_put_right:NV \l__knot_tmpa_tl \l_tmpa_tl
+ }
+ }
+ {
+ \tl_put_right:NV \l__knot_tmpa_tl #2
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\knot_intersections:nn}
+% This computes the intersections of two pieces and steps through them.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \knot_intersections:nn #1#2
+{
+ \group_begin:
+ \tl_set_eq:NN \l__knot_tmpa_tl \l__knot_prefix_tl
+ \tl_put_right:Nn \l__knot_tmpa_tl {#1}
+ \tl_set_eq:NN \l__knot_tmpb_tl \l__knot_prefix_tl
+ \tl_put_right:Nn \l__knot_tmpb_tl {#2}
+ \spath_get:nnN {knot \tl_use:N \l__knot_tmpa_tl} {path} \l__knot_tmpc_tl
+ \spath_get:nnN {knot \tl_use:N \l__knot_tmpb_tl} {path} \l__knot_tmpd_tl
+
+ \bool_if:nTF {
+ \l__knot_save_bool
+ &&
+ \tl_if_exist_p:c {knot~ intersections~ \tl_use:N \l__knot_name_tl - \tl_use:N \l__knot_tmpa_tl - \tl_use:N \l__knot_tmpb_tl}
+ }
+ {
+ \tl_use:c {knot~ intersections~ \tl_use:N \l__knot_name_tl - \tl_use:N \l__knot_tmpa_tl - \tl_use:N \l__knot_tmpb_tl}
+ }
+ {
+\pgfintersectionofpaths{\pgfsetpath\l__knot_tmpc_tl}{\pgfsetpath\l__knot_tmpd_tl}
+
+ }
+ \int_compare:nT {\pgfintersectionsolutions > 0}
+ {
+ \int_step_function:nnnN {1} {1} {\pgfintersectionsolutions} \knot_do_intersection:n
+ }
+ \knot_save_intersections:VV \l__knot_tmpa_tl \l__knot_tmpb_tl
+ \group_end:
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\knot_save_intersections:nn}
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \knot_save_intersections:nn #1#2
+{
+ \bool_if:NT \l__knot_save_bool
+ {
+ \tl_clear:N \l__knot_aux_tl
+ \tl_put_right:Nn \l__knot_aux_tl
+ {
+ \def\pgfintersectionsolutions
+ }
+ \tl_put_right:Nx \l__knot_aux_tl
+ {
+ {\int_eval:n {\pgfintersectionsolutions}}
+ }
+ \int_compare:nT {\pgfintersectionsolutions > 0}
+ {
+ \int_step_inline:nnnn {1} {1} {\pgfintersectionsolutions}
+ {
+ \pgfpointintersectionsolution{##1}
+ \dim_set:Nn \l__knot_tmpa_dim {\pgf@x}
+ \dim_set:Nn \l__knot_tmpb_dim {\pgf@y}
+ \tl_put_right:Nn \l__knot_aux_tl
+ {
+ \expandafter\def\csname pgfpoint@intersect@solution@##1\endcsname
+ }
+ \tl_put_right:Nx \l__knot_aux_tl
+ {
+ {\exp_not:N \pgf@x=\dim_use:N \l__knot_tmpa_dim\exp_not:N\relax\exp_not:N \pgf@y =\dim_use:N \l__knot_tmpb_dim\relax}
+ }
+ }
+ \tl_set:Nn \l__knot_auxa_tl {\expandafter \gdef \csname knot~ intersections~}
+ \tl_put_right:Nx \l__knot_auxa_tl {\tl_use:N \l__knot_name_tl - #1 - #2}
+ \tl_put_right:Nn \l__knot_auxa_tl {\endcsname}
+ \tl_put_right:Nx \l__knot_auxa_tl {{\tl_to_str:N \l__knot_aux_tl}}
+ \protected@write\@auxout{}{\tl_to_str:N \l__knot_auxa_tl}
+ }
+ }
+}
+\cs_generate_variant:Nn \knot_save_intersections:nn {VV}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\knot_do_intersection:n}
+% This handles a specific intersection.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \knot_do_intersection:n #1
+{
+% \end{macrocode}
+% Get the intersection coordinates.
+% \begin{macrocode}
+ \pgfpointintersectionsolution{#1}
+ \dim_set:Nn \l__knot_tmpa_dim {\pgf@x}
+ \dim_set:Nn \l__knot_tmpb_dim {\pgf@y}
+% \end{macrocode}
+% If we're dealing with filaments, we can get false positives from the end points.
+% \begin{macrocode}
+ \bool_set_false:N \l__knot_skip_bool
+ \bool_if:NT \l__knot_self_intersections_bool
+ {
+% \end{macrocode}
+% If one filament preceded the other, test for the intersection being at the relevant end point.
+% \begin{macrocode}
+ \tl_set:Nn \l_tmpa_tl {knot previous}
+ \tl_put_right:NV \l_tmpa_tl \l__knot_tmpa_tl
+ \tl_set:Nv \l_tmpa_tl \l_tmpa_tl
+ \tl_if_eq:NNT \l_tmpa_tl \l__knot_tmpb_tl
+ {
+ \knot_test_endpoint:VnT \l__knot_tmpb_tl {final point}
+ {
+ \bool_set_true:N \l__knot_skip_bool
+ }
+ }
+
+ \tl_set:Nn \l_tmpa_tl {knot previous}
+ \tl_put_right:NV \l_tmpa_tl \l__knot_tmpb_tl
+ \tl_set:Nv \l_tmpa_tl \l_tmpa_tl
+ \tl_if_eq:NNT \l_tmpa_tl \l__knot_tmpa_tl
+ {
+ \knot_test_endpoint:VnT \l__knot_tmpa_tl {final point}
+ {
+ \bool_set_true:N \l__knot_skip_bool
+ }
+ }
+ }
+% \end{macrocode}
+% The user can also say that end points of filaments (or strands) should simply be ignored anyway.
+% \begin{macrocode}
+ \bool_if:NT \l__knot_ignore_ends_bool
+ {
+ \knot_test_endpoint:VnT \l__knot_tmpa_tl {initial point}
+ {
+ \bool_set_true:N \l__knot_skip_bool
+ }
+ \knot_test_endpoint:VnT \l__knot_tmpa_tl {final point}
+ {
+ \bool_set_true:N \l__knot_skip_bool
+ }
+ \knot_test_endpoint:VnT \l__knot_tmpb_tl {initial point}
+ {
+ \bool_set_true:N \l__knot_skip_bool
+ }
+ \knot_test_endpoint:VnT \l__knot_tmpb_tl {final point}
+ {
+ \bool_set_true:N \l__knot_skip_bool
+ }
+ }
+% \end{macrocode}
+% Assuming that we passed all the above tests, we render the crossing.
+% \begin{macrocode}
+ \bool_if:NF \l__knot_skip_bool
+ {
+
+ \int_gincr:N \l__knot_intersections_int
+% \end{macrocode}
+% This is the intersection test.
+% If the intersection finder finds too many, it might be useful to ignore some.
+% \begin{macrocode}
+ \bool_if:nF
+ {
+ \tl_if_exist_p:c {l__knot_ignore_crossing_ \int_use:N
+ \l__knot_intersections_int}
+ &&
+ ! \tl_if_empty_p:c {l__knot_ignore_crossing_ \int_use:N
+ \l__knot_intersections_int}
+ }
+ {
+% \end{macrocode}
+% This is the flip test.
+% We only render one of the paths.
+% The ``flip'' swaps which one we render.
+% \begin{macrocode}
+ \bool_if:nTF
+ {
+ \tl_if_exist_p:c {l__knot_crossing_ \int_use:N
+ \l__knot_intersections_int}
+ &&
+ ! \tl_if_empty_p:c {l__knot_crossing_ \int_use:N
+ \l__knot_intersections_int}
+ }
+ {
+ \tl_set_eq:NN \l__knot_tmpg_tl \l__knot_tmpb_tl
+ }
+ {
+ \tl_set_eq:NN \l__knot_tmpg_tl \l__knot_tmpa_tl
+ }
+% \end{macrocode}
+% Now we know which one we're rendering, we test to see if we should also render its predecessor or successor to ensure that we render a path through the entire crossing region.
+% \begin{macrocode}
+ \bool_if:NT \l__knot_self_intersections_bool
+ {
+ \knot_test_endpoint:VnT \l__knot_tmpg_tl {initial point}
+ {
+ \bool_set_true:N \l__knot_prepend_prev_bool
+ }
+ {
+ \bool_set_false:N \l__knot_prepend_prev_bool
+ }
+ \knot_test_endpoint:VnT \l__knot_tmpg_tl {final point}
+ {
+ \bool_set_true:N \l__knot_append_next_bool
+ }
+ {
+ \bool_set_false:N \l__knot_append_next_bool
+ }
+% \end{macrocode}
+% If either of those tests succeeded, do the appending or prepending.
+% \begin{macrocode}
+ \bool_if:nT
+ {
+ \l__knot_prepend_prev_bool || \l__knot_append_next_bool
+ }
+ {
+ \spath_clone:nn {knot \tl_use:N \l__knot_tmpg_tl}
+ {knot \tl_use:N \l__knot_prefix_tl -1}
+
+ \tl_set_eq:cc {l__knot_options_ \tl_use:N \l__knot_prefix_tl -1} {l__knot_options_ \tl_use:N \l__knot_tmpg_tl}
+
+ \bool_if:nT
+ {
+ \l__knot_prepend_prev_bool
+ &&
+ \tl_if_exist_p:c {knot previous \tl_use:N \l__knot_tmpg_tl}
+ &&
+ !\tl_if_empty_p:c {knot previous \tl_use:N \l__knot_tmpg_tl}
+ }
+ {
+ \spath_prepend_no_move:nn {knot \tl_use:N \l__knot_prefix_tl -1} {knot \tl_use:c {knot previous \tl_use:N \l__knot_tmpg_tl}}
+% \end{macrocode}
+% If we split potentially self intersecting curves, we test to see if we should prepend yet another segment.
+% \begin{macrocode}
+ \bool_if:nT
+ {
+ \l__knot_splits_bool
+ &&
+ \tl_if_exist_p:c {knot previous \tl_use:N \l__knot_tmpg_tl}
+ &&
+ !\tl_if_empty_p:c {knot previous \tl_use:N \l__knot_tmpg_tl}
+ }
+ {
+ \knot_test_endpoint:vnT {knot previous \tl_use:N \l__knot_tmpg_tl} {initial point}
+ {
+ \spath_get:nnN {knot \tl_use:N \l__knot_prefix_tl -1} {path} \l_tmpa_tl
+ \spath_prepend_no_move:nn {knot \tl_use:N \l__knot_prefix_tl -1} {knot \tl_use:c {knot previous \tl_use:c {knot previous \tl_use:N \l__knot_tmpg_tl}}}
+ \spath_get:nnN {knot \tl_use:N \l__knot_prefix_tl -1} {path} \l_tmpa_tl
+
+ }
+ }
+ }
+% \end{macrocode}
+% Now the same for appending.
+% \begin{macrocode}
+ \bool_if:nT
+ {
+ \l__knot_append_next_bool
+ &&
+ \tl_if_exist_p:c {knot next \tl_use:N \l__knot_tmpg_tl}
+ &&
+ !\tl_if_empty_p:c {knot previous \tl_use:N \l__knot_tmpg_tl}
+ }
+ {
+ \spath_append_no_move:nn {knot \tl_use:N \l__knot_prefix_tl -1} {knot \tl_use:c {knot next \tl_use:N \l__knot_tmpg_tl}}
+ \bool_if:nT
+ {
+ \l__knot_splits_bool
+ &&
+ \tl_if_exist_p:c {knot previous \tl_use:N
+ \l__knot_tmpg_tl}
+ &&
+ !\tl_if_empty_p:c {knot previous \tl_use:N \l__knot_tmpg_tl}
+ }
+ {
+ \knot_test_endpoint:vnT {knot previous \tl_use:N \l__knot_tmpg_tl} {final point}
+ {
+ \spath_append_no_move:nn {knot \tl_use:N \l__knot_prefix_tl -1} {knot \tl_use:c {knot next \tl_use:c {knot next \tl_use:N \l__knot_tmpg_tl}}}
+
+ }
+ }
+ }
+
+ \tl_set:Nn \l__knot_tmpg_tl {\tl_use:N \l__knot_prefix_tl -1}
+ }
+ }
+% \end{macrocode}
+% Now we render the crossing.
+% \begin{macrocode}
+ \pgfscope
+ \group_begin:
+ \tikzset{knot~ diagram/every~ intersection/.try, every~ intersection/.try, knot~ diagram/intersection~ \int_use:N \l__knot_intersections_int/.try}
+ \knot_draw_crossing:nVV {\tl_use:N \l__knot_tmpg_tl} \l__knot_tmpa_dim \l__knot_tmpb_dim
+ \group_end:
+ \endpgfscope
+% \end{macrocode}
+% This ends the boolean as to whether to consider the intersection at all
+% \begin{macrocode}
+ }
+% \end{macrocode}
+% And stick a coordinate possibly with a label at the crossing.
+% \begin{macrocode}
+ \seq_gpush:Nx \l__knot_nodes_seq { \l__knot_node_tl (\l__knot_name_tl \c_space_tl \int_use:N \l__knot_intersections_int) at (\dim_use:N \l__knot_tmpa_dim, \dim_use:N \l__knot_tmpb_dim) {};}
+
+ }
+}
+
+\cs_generate_variant:Nn \knot_intersections:nn {VV}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\knot_test_endpoint:N}
+% Test whether the point is near the intersection point.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \knot_test_endpoint:N #1 {p,T,F,TF}
+{
+ \dim_compare:nTF
+ {
+ \dim_abs:n { \l__knot_tmpa_dim - \tl_item:Nn #1 {1}}
+ +
+ \dim_abs:n { \l__knot_tmpb_dim - \tl_item:Nn #1 {2}}
+ <
+ \l__knot_tolerance_dim
+ }
+ {
+ \prg_return_true:
+ }
+ {
+ \prg_return_false:
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\knot_test_endpoint:nn}
+% Wrapper around the above.
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn \knot_test_endpoint:nn #1#2 {T,F,TF}
+{
+ \spath_get:nnN {knot #1} {#2} \l__knot_tmpd_tl
+ \knot_test_endpoint:NTF \l__knot_tmpd_tl
+ {
+ \prg_return_true:
+ }
+ {
+ \prg_return_false:
+ }
+}
+
+\cs_generate_variant:Nn \knot_test_endpoint:nnT {VnT,vnT}
+\cs_generate_variant:Nn \knot_test_endpoint:nnF {VnF,vnF}
+\cs_generate_variant:Nn \knot_test_endpoint:nnTF {VnTF,vnTF}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\knot_draw_crossing:nnn}
+% This is the code that actually renders a crossing.
+% \begin{macrocode}
+\cs_new_nopar:Npn \knot_draw_crossing:nnn #1#2#3
+{
+ \group_begin:
+ \pgfscope
+ \path[knot~ diagram/background~ clip] (#2, #3) circle[radius=\l__knot_clip_bg_radius_dim];
+
+ \tl_set:Nn \l_tmpa_tl {knot~ diagram/every~ strand/.try,}
+ \tl_if_exist:cT {l__knot_options_ #1}
+ {
+ \tl_put_right:Nv \l_tmpa_tl {l__knot_options_ #1}
+ }
+ \tl_put_right:Nn \l_tmpa_tl {,knotbg,line~ width= \tl_use:N \l__knot_clip_width_tl * \pgflinewidth}
+ \spath_tikz_path:Vn \l_tmpa_tl {knot #1}
+
+ \endpgfscope
+
+ \pgfscope
+ \path[knot~ diagram/clip] (#2, #3) circle[radius=\l__knot_clip_draw_radius_dim];
+
+ \tl_set:Nn \l_tmpa_tl {knot~ diagram/every~ strand/.try,}
+ \tl_if_exist:cT {l__knot_options_ #1}
+ {
+ \tl_put_right:Nv \l_tmpa_tl {l__knot_options_ #1}
+ }
+ \tl_put_right:Nn \l_tmpa_tl {,knot~ diagram/only~ when~ rendering/.try,only~ when~ rendering/.try}
+ \spath_tikz_path:Vn \l_tmpa_tl {knot #1}
+
+ \endpgfscope
+ \group_end:
+}
+
+\cs_generate_variant:Nn \knot_draw_crossing:nnn {nVV}
+
+\cs_new_nopar:Npn \knot_draw_crossing:nn #1#2
+{
+ \tikz@scan@one@point\pgfutil@firstofone #2 \relax
+ \knot_draw_crossing:nVV {#1} \pgf@x \pgf@y
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\knot_split_strands:}
+% This, and the following macros, are for splitting strands into filaments.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \knot_split_strands:
+{
+ \int_gzero:N \l__knot_filaments_int
+ \int_step_function:nnnN {1} {1} {\l__knot_strands_int} \knot_split_strand:n
+ \int_step_function:nnnN {1} {1} {\l__knot_filaments_int} \knot_compute_nexts:n
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\knot_compute_nexts:n}
+% Each filament needs to know its predecessor and successor.
+% We work out the predecessors as we go along, this fills in the successors.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \knot_compute_nexts:n #1
+{
+ \tl_clear_new:c {knot next \tl_use:c {knot previous filament #1}}
+ \tl_set:cn {knot next \tl_use:c {knot previous filament #1}} {filament #1}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\knot_split_strand:n}
+% Sets up the split for a single strand.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \knot_split_strand:n #1
+{
+ \int_set_eq:NN \l__knot_component_start_int \l__knot_filaments_int
+ \int_incr:N \l__knot_component_start_int
+ \tl_set_eq:Nc \l__knot_tmpa_tl {l__knot_options_strand #1}
+ \spath_map_segment_function:nN {knot strand #1} \knot_save_filament:NN
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\knot_save_filament:NN}
+% Saves a filament as a new \Verb+spath+ object.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \knot_save_filament:NN #1#2
+{
+ \tl_case:NnF #1
+ {
+ \g__spath_moveto_tl
+ {
+ \int_compare:nT {\l__knot_component_start_int < \l__knot_filaments_int}
+ {
+ \int_set_eq:NN \l__knot_component_start_int \l__knot_filaments_int
+ }
+ }
+ \g__spath_lineto_tl
+ {
+ \int_gincr:N \l__knot_filaments_int
+ \spath_clear_new:n {knot filament \int_use:N \l__knot_filaments_int}
+ \spath_put:nnV {knot filament \int_use:N \l__knot_filaments_int} {path} #2
+ \tl_set_eq:cN {l__knot_options_filament \int_use:N \l__knot_filaments_int} \l__knot_tmpa_tl
+
+ \tl_clear_new:c {knot previous filament \int_use:N \l__knot_filaments_int}
+ \int_compare:nF {\l__knot_component_start_int == \l__knot_filaments_int}
+ {
+ \tl_set:cx {knot previous filament \int_use:N \l__knot_filaments_int} {filament \int_eval:n {\l__knot_filaments_int - 1}}
+ }
+ }
+ \g__spath_curvetoa_tl
+ {
+ \int_gincr:N \l__knot_filaments_int
+ \spath_clear_new:n {knot filament \int_use:N \l__knot_filaments_int}
+ \spath_put:nnV {knot filament \int_use:N \l__knot_filaments_int} {path} #2
+ \tl_set_eq:cN {l__knot_options_filament \int_use:N \l__knot_filaments_int} \l__knot_tmpa_tl
+
+ \tl_clear_new:c {knot previous filament \int_use:N \l__knot_filaments_int}
+ \int_compare:nF {\l__knot_component_start_int == \l__knot_filaments_int}
+ {
+ \tl_set:cx {knot previous filament \int_use:N \l__knot_filaments_int} {filament \int_eval:n {\l__knot_filaments_int - 1}}
+ }
+ }
+ \g__spath_closepath_tl
+ {
+ \tl_show:N #2
+ \int_gincr:N \l__knot_filaments_int
+ \spath_clear_new:n {knot filament \int_use:N \l__knot_filaments_int}
+ \tl_clear:N \l_tmpa_tl
+ \tl_put_right:Nx {\tl_item:Nn #2 {1}\tl_item:Nn #2 {2}\tl_item:Nn #2 {3}}
+ \tl_put_right:NV \l_tmpa_tl \g__spath_lineto_tl
+ \tl_put_right:Nx {\tl_item:Nn #2 {5}\tl_item:Nn #2 {6}}
+ \tl_show:N \l_tmpa_tl
+ \spath_put:nnV {knot filament \int_use:N \l__knot_filaments_int} {path} \l_tmpa_tl
+ \tl_set_eq:cN {l__knot_options_filament \int_use:N \l__knot_filaments_int} \l__knot_tmpa_tl
+ \tl_clear_new:c {knot previous filament \int_use:N \l__knot_filaments_int}
+ \int_compare:nF {\l__knot_component_start_int == \l__knot_filaments_int}
+ {
+ \tl_set:cx {knot previous filament \int_use:N \l__knot_filaments_int} {filament \int_eval:n {\l__knot_filaments_int - 1}}
+ }
+ \tl_set:cx {knot previous filament \int_use:N \l__knot_component_start_int} {filament \int_use:N \l__knot_filaments_int}
+ }
+ }
+ {
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\redraw}
+% The user can redraw segments of the strands at specific locations.
+% \begin{macrocode}
+\NewDocumentCommand \redraw { m m }
+{
+% \tikz@scan@one@point\pgfutil@firstofone #2 \relax
+ \tl_put_right:Nn \l__knot_redraws_tl {\knot_draw_crossing:nn}
+ \tl_put_right:Nx \l__knot_redraws_tl {
+ {strand #1} {#2}% {\dim_use:N \pgf@x} {\dim_use:N \pgf@y}
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+\ExplSyntaxOff
+% \end{macrocode}
+%
+% \begin{macro}{\pgf@sh@@knotanchor}
+% Add the extra anchors for the knot crossing nodes.
+% \begin{macrocode}
+\def\pgf@sh@@knotanchor#1#2{%
+ \anchor{#2 north west}{%
+ \csname pgf@anchor@knot #1@north west\endcsname%
+ \pgf@x=#2\pgf@x%
+ \pgf@y=#2\pgf@y%
+ }%
+ \anchor{#2 north east}{%
+ \csname pgf@anchor@knot #1@north east\endcsname%
+ \pgf@x=#2\pgf@x%
+ \pgf@y=#2\pgf@y%
+ }%
+ \anchor{#2 south west}{%
+ \csname pgf@anchor@knot #1@south west\endcsname%
+ \pgf@x=#2\pgf@x%
+ \pgf@y=#2\pgf@y%
+ }%
+ \anchor{#2 south east}{%
+ \csname pgf@anchor@knot #1@south east\endcsname%
+ \pgf@x=#2\pgf@x%
+ \pgf@y=#2\pgf@y%
+ }%
+ \anchor{#2 north}{%
+ \csname pgf@anchor@knot #1@north\endcsname%
+ \pgf@x=#2\pgf@x%
+ \pgf@y=#2\pgf@y%
+ }%
+ \anchor{#2 east}{%
+ \csname pgf@anchor@knot #1@east\endcsname%
+ \pgf@x=#2\pgf@x%
+ \pgf@y=#2\pgf@y%
+ }%
+ \anchor{#2 west}{%
+ \csname pgf@anchor@knot #1@west\endcsname%
+ \pgf@x=#2\pgf@x%
+ \pgf@y=#2\pgf@y%
+ }%
+ \anchor{#2 south}{%
+ \csname pgf@anchor@knot #1@south\endcsname%
+ \pgf@x=#2\pgf@x%
+ \pgf@y=#2\pgf@y%
+ }%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{knot crossing}
+% \begin{macrocode}
+\pgfdeclareshape{knot crossing}
+{
+ \inheritsavedanchors[from=circle] % this is nearly a circle
+ \inheritanchorborder[from=circle]
+ \inheritanchor[from=circle]{north}
+ \inheritanchor[from=circle]{north west}
+ \inheritanchor[from=circle]{north east}
+ \inheritanchor[from=circle]{center}
+ \inheritanchor[from=circle]{west}
+ \inheritanchor[from=circle]{east}
+ \inheritanchor[from=circle]{mid}
+ \inheritanchor[from=circle]{mid west}
+ \inheritanchor[from=circle]{mid east}
+ \inheritanchor[from=circle]{base}
+ \inheritanchor[from=circle]{base west}
+ \inheritanchor[from=circle]{base east}
+ \inheritanchor[from=circle]{south}
+ \inheritanchor[from=circle]{south west}
+ \inheritanchor[from=circle]{south east}
+ \inheritanchorborder[from=circle]
+ \pgf@sh@@knotanchor{crossing}{2}
+ \pgf@sh@@knotanchor{crossing}{3}
+ \pgf@sh@@knotanchor{crossing}{4}
+ \pgf@sh@@knotanchor{crossing}{8}
+ \pgf@sh@@knotanchor{crossing}{16}
+ \pgf@sh@@knotanchor{crossing}{32}
+ \backgroundpath{
+ \pgfutil@tempdima=\radius%
+ \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/outer xsep}}%
+ \pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/outer ysep}}%
+ \ifdim\pgf@xb<\pgf@yb%
+ \advance\pgfutil@tempdima by-\pgf@yb%
+ \else%
+ \advance\pgfutil@tempdima by-\pgf@xb%
+ \fi%
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{knot over cross}
+% \begin{macrocode}
+\pgfdeclareshape{knot over cross}
+{
+ \inheritsavedanchors[from=rectangle] % this is nearly a circle
+ \inheritanchorborder[from=rectangle]
+ \inheritanchor[from=rectangle]{north}
+ \inheritanchor[from=rectangle]{north west}
+ \inheritanchor[from=rectangle]{north east}
+ \inheritanchor[from=rectangle]{center}
+ \inheritanchor[from=rectangle]{west}
+ \inheritanchor[from=rectangle]{east}
+ \inheritanchor[from=rectangle]{mid}
+ \inheritanchor[from=rectangle]{mid west}
+ \inheritanchor[from=rectangle]{mid east}
+ \inheritanchor[from=rectangle]{base}
+ \inheritanchor[from=rectangle]{base west}
+ \inheritanchor[from=rectangle]{base east}
+ \inheritanchor[from=rectangle]{south}
+ \inheritanchor[from=rectangle]{south west}
+ \inheritanchor[from=rectangle]{south east}
+ \inheritanchorborder[from=rectangle]
+ \backgroundpath{
+ \southwest \pgf@xa=\pgf@x \pgf@ya=\pgf@y
+ \northeast \pgf@xb=\pgf@x \pgf@yb=\pgf@y
+ \pgfpathmoveto{\pgfqpoint{\pgf@xa}{\pgf@ya}}
+ \pgfpathlineto{\pgfqpoint{\pgf@xb}{\pgf@yb}}
+ }
+ \foregroundpath{
+% store lower right in xa/ya and upper right in xb/yb
+ \southwest \pgf@xa=\pgf@x \pgf@ya=\pgf@y
+ \northeast \pgf@xb=\pgf@x \pgf@yb=\pgf@y
+ \pgfpathmoveto{\pgfqpoint{\pgf@xa}{\pgf@yb}}
+ \pgfpathlineto{\pgfqpoint{\pgf@xb}{\pgf@ya}}
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{knot under cross}
+% \begin{macrocode}
+\pgfdeclareshape{knot under cross}
+{
+ \inheritsavedanchors[from=rectangle] % this is nearly a circle
+ \inheritanchorborder[from=rectangle]
+ \inheritanchor[from=rectangle]{north}
+ \inheritanchor[from=rectangle]{north west}
+ \inheritanchor[from=rectangle]{north east}
+ \inheritanchor[from=rectangle]{center}
+ \inheritanchor[from=rectangle]{west}
+ \inheritanchor[from=rectangle]{east}
+ \inheritanchor[from=rectangle]{mid}
+ \inheritanchor[from=rectangle]{mid west}
+ \inheritanchor[from=rectangle]{mid east}
+ \inheritanchor[from=rectangle]{base}
+ \inheritanchor[from=rectangle]{base west}
+ \inheritanchor[from=rectangle]{base east}
+ \inheritanchor[from=rectangle]{south}
+ \inheritanchor[from=rectangle]{south west}
+ \inheritanchor[from=rectangle]{south east}
+ \inheritanchorborder[from=rectangle]
+ \backgroundpath{
+ \southwest \pgf@xa=\pgf@x \pgf@ya=\pgf@y
+ \northeast \pgf@xb=\pgf@x \pgf@yb=\pgf@y
+ \pgfpathmoveto{\pgfqpoint{\pgf@xa}{\pgf@yb}}
+ \pgfpathlineto{\pgfqpoint{\pgf@xb}{\pgf@ya}}
+ }
+ \foregroundpath{
+% store lower right in xa/ya and upper right in xb/yb
+ \southwest \pgf@xa=\pgf@x \pgf@ya=\pgf@y
+ \northeast \pgf@xb=\pgf@x \pgf@yb=\pgf@y
+ \pgfpathmoveto{\pgfqpoint{\pgf@xa}{\pgf@ya}}
+ \pgfpathlineto{\pgfqpoint{\pgf@xb}{\pgf@yb}}
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{knot vert}
+% \begin{macrocode}
+\pgfdeclareshape{knot vert}
+{
+ \inheritsavedanchors[from=rectangle] % this is nearly a circle
+ \inheritanchorborder[from=rectangle]
+ \inheritanchor[from=rectangle]{north}
+ \inheritanchor[from=rectangle]{north west}
+ \inheritanchor[from=rectangle]{north east}
+ \inheritanchor[from=rectangle]{center}
+ \inheritanchor[from=rectangle]{west}
+ \inheritanchor[from=rectangle]{east}
+ \inheritanchor[from=rectangle]{mid}
+ \inheritanchor[from=rectangle]{mid west}
+ \inheritanchor[from=rectangle]{mid east}
+ \inheritanchor[from=rectangle]{base}
+ \inheritanchor[from=rectangle]{base west}
+ \inheritanchor[from=rectangle]{base east}
+ \inheritanchor[from=rectangle]{south}
+ \inheritanchor[from=rectangle]{south west}
+ \inheritanchor[from=rectangle]{south east}
+ \inheritanchorborder[from=rectangle]
+ \backgroundpath{
+% store lower right in xa/ya and upper right in xb/yb
+ \southwest \pgf@xa=\pgf@x \pgf@ya=\pgf@y
+ \northeast \pgf@xb=\pgf@x \pgf@yb=\pgf@y
+ \pgfpathmoveto{\pgfqpoint{\pgf@xa}{\pgf@ya}}
+ \pgfpathlineto{\pgfqpoint{\pgf@xa}{\pgf@yb}}
+ \pgfpathmoveto{\pgfqpoint{\pgf@xb}{\pgf@yb}}
+ \pgfpathlineto{\pgfqpoint{\pgf@xb}{\pgf@ya}}
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{knot horiz}
+% \begin{macrocode}
+\pgfdeclareshape{knot horiz}
+{
+ \inheritsavedanchors[from=rectangle] % this is nearly a circle
+ \inheritanchorborder[from=rectangle]
+ \inheritanchor[from=rectangle]{north}
+ \inheritanchor[from=rectangle]{north west}
+ \inheritanchor[from=rectangle]{north east}
+ \inheritanchor[from=rectangle]{center}
+ \inheritanchor[from=rectangle]{west}
+ \inheritanchor[from=rectangle]{east}
+ \inheritanchor[from=rectangle]{mid}
+ \inheritanchor[from=rectangle]{mid west}
+ \inheritanchor[from=rectangle]{mid east}
+ \inheritanchor[from=rectangle]{base}
+ \inheritanchor[from=rectangle]{base west}
+ \inheritanchor[from=rectangle]{base east}
+ \inheritanchor[from=rectangle]{south}
+ \inheritanchor[from=rectangle]{south west}
+ \inheritanchor[from=rectangle]{south east}
+ \inheritanchorborder[from=rectangle]
+ \foregroundpath{
+% store lower right in xa/ya and upper right in xb/yb
+ \southwest \pgf@xa=\pgf@x \pgf@ya=\pgf@y
+ \northeast \pgf@xb=\pgf@x \pgf@yb=\pgf@y
+ \pgfpathmoveto{\pgfqpoint{\pgf@xa}{\pgf@ya}}
+ \pgfpathlineto{\pgfqpoint{\pgf@xb}{\pgf@ya}}
+ \pgfpathmoveto{\pgfqpoint{\pgf@xa}{\pgf@yb}}
+ \pgfpathlineto{\pgfqpoint{\pgf@xb}{\pgf@yb}}
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \iffalse
+%</knots>
+% \fi
+%\Finale
+\endinput
diff --git a/graphics/pgf/contrib/spath3/spath3_code.pdf b/graphics/pgf/contrib/spath3/spath3_code.pdf
new file mode 100644
index 0000000000..be1f2941bd
--- /dev/null
+++ b/graphics/pgf/contrib/spath3/spath3_code.pdf
Binary files differ