% \iffalse meta-comment
%<*internal>
\iffalse
%</internal>
%<*readme>
----------------------------------------------------------------
spath3 --- LaTeX3 functions for manipulating PGF soft paths
E-mail: Andrew Stacey <[email protected]>
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 l3docstrip.tex
\keepsilent
\askforoverwritefalse
\preamble
----------------------------------------------------------------
spath3 --- Functions for manipulating PGF soft paths
E-mail: Andrew Stacey <[email protected]>
Released under the LaTeX Project Public License v1.3c or later
See http://www.latex-project.org/lppl.txt
----------------------------------------------------------------

\endpreamble
\postamble

Copyright (C) 2011-2024 by Andrew Stacey <[email protected]>

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_code.dtx
                                calligraphy_doc.tex
                                knots_doc.tex
                                spath3.tex
and the derived files            spath3.ins,
                                spath3_code.pdf,
                                spath3.sty,
                                tikzlibrarycalligraphy.code.tex
                                tikzlibraryknots.code.tex
                                tikzlibraryspath3.code.tex
                                calligraphy.pdf
                                knots.pdf
                                spath3.pdf
                                README.txt

\endpostamble
\usedir{tex/latex/spath3}
\generate{
 \file{spath3.sty}{\from{\jobname.dtx}{spath3}}
}
\generate{
 \file{tikzlibrarycalligraphy.code.tex}{\from{\jobname.dtx}{calligraphy}}
}
\generate{
 \file{tikzlibraryknots.code.tex}{\from{\jobname.dtx}{knots}}
}
\generate{
 \file{tikzlibraryspath3.code.tex}{\from{\jobname.dtx}{tikzspath3}}
}
%</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=[LaTeX]TeX,
 basicstyle=\small\ttfamily,
 keepspaces=true,
 columns=fullflexible
}

\usepackage{fancyvrb}

\newenvironment{example}
 {\VerbatimEnvironment
  \begin{VerbatimOut}[gobble=2]{example.out}}
 {\end{VerbatimOut}
  \begin{center}
%   \setlength{\parindent}{0pt}
  \fbox{\begin{minipage}{.9\linewidth}
    \lstinputlisting[]{example.out}
  \end{minipage}}
  \fbox{\begin{minipage}{.9\linewidth}
    \centering
    \input{example.out}
  \end{minipage}}
\end{center}
}
\EnableCrossrefs
\CodelineIndex
\RecordChanges
\begin{document}
 \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
% \CheckSum{10278}
%
% \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}
% \changes{1.4}{2020/12/18}{A fair amount of code reimplementation and reorganisation, together with defining TikZ keys to make functions available for use.}
% \changes{2.0}{2021/01/19}{Refactored the code to remove the OO approach and make it functional, introduced the spath3 TikZ library to provide a user interface.}
% \changes{2.2}{2021/02/05}{Bugfixes and improvements, mainly with regard to the intersection and splitting routines.}
% \changes{2.4}{2021/02/21}{Rejigged how the routines for using paths were implemented, added some more routines for joining paths.}
% \changes{2.6}{2021/11/23}{Modified core routines to cope with a "true rectangle" path; added routines for splitting at a parametrised point.}
% \changes{2.7}{2022/08/24}{Bug fixes}
% \changes{2.8}{2024/05/31}{Routine to disable arrow shortening, bug fixes}
%
% \DoNotIndex{\newcommand,\newenvironment}
%
% \pdfstringdefDisableCommands{%
%  \def\\{}%
%  \def\url#1{<#1>}%
% }
%
% \providecommand*{\url}{\texttt}
% \GetFileInfo{spath3.sty}
% \title{The \textsf{spath3} package: code}
% \author{Andrew Stacey \\ \url{[email protected]}}
% \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 was originally not really intended for use by end users but as a foundation on which other packages can be built.
% However, over the years I've found myself using it at ever higher levels and so a set of interfaces has been designed using TikZ keys.
%
% It also provides the engine that drives a few other packages, such as the  \Verb+calligraphy+, \Verb+knot+, and \Verb+tilings+ (formerly, \Verb+penrose+) packages.
% The first two of these are subpackages of this one.
% The \Verb+calligraphy+ package simulates a calligraphic pen stroking a path.
% The \Verb+knots+ package can be used to draw knot (and similar) diagrams.
%
% For usage, see the documentation of the following packages (\Verb+texdoc <package>+):
%
% \begin{itemize}
% \item \Verb+calligraphy+
% \item \Verb+knots+
% \item \Verb+tilings+
% \item \Verb+spath3+ (\emph{this} document is the code, there's another which focusses on usage)
% \end{itemize}
%
% \section{Technical Details}
%
% 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.
%
% In the original implementation, I wrapped this token list in a \Verb+prop+ to store useful information along with the path.
% Over time, this additional structure has proved a little unwieldy and I've pared it back to working primarily with the original soft path as a token list.
%
% A frequent use of this package is to break a path into pieces and do something with each of those pieces.
% To that end, there are various words that I use to describe the levels of the structure of a path.
%
% At the top level is the path itself.
% At the bottom level are the triples of the form \Verb+\macro{dim}{dim}+, as described above.
% In between these are the \emph{segments} and \emph{components}.
%
% A \emph{segment} is a minimal drawing piece.
% Thus it might be a straight line or a B\'ezier curve.
% When a path is broken into segments then each segment is a complete path so it isn't simply a selection of triples from the original path.
%
% A \emph{component} is a minimal connected section of the path.
% So every component starts with a move command and continues until the next move command.
% For ease of implementation (and to enable a copperplate pen in the calligraphy package!), an isolated move is considered as a component.
% Thus the following path consists of three components:
%
% \begin{Verbatim}
% \path (0,0) -- (1,0) (2,0) (3,0) to[out=0,in=90] (4,0);
% \end{Verbatim}
%
% \StopEventually{}
%
% \section{Implementation}
%
% \iffalse
%<*spath3>
% \fi
% \subsection{Initialisation}
%
%    \begin{macrocode}
%<@@=spath>
%    \end{macrocode}
%
% Load the \LaTeX3 foundation and register us as a \LaTeX3\ package.
%    \begin{macrocode}
\NeedsTeXFormat{LaTeX2e}
\RequirePackage{expl3}
\RequirePackage{pgf}
\ProvidesExplPackage {spath3} {2024/05/31} {2.8} {Functions for
manipulating PGF soft paths}
\RequirePackage{xparse}
%    \end{macrocode}
%
% Utilities copied from \url{https://github.com/loopspace/LaTeX3-Utilities} for adding something in braces to a token list.
% I find I use this quite a lot in my packages.
%    \begin{macrocode}
\cs_new_protected:Nn \@@_tl_put_right_braced:Nn
{
 \tl_put_right:Nn #1 { { #2 } }
}
\cs_generate_variant:Nn \@@_tl_put_right_braced:Nn { NV, cV, cv, Nx, cx }

\cs_new_protected:Nn \@@_tl_gput_right_braced:Nn
{
 \tl_gput_right:Nn #1 { { #2 } }
}
\cs_generate_variant:Nn \@@_tl_gput_right_braced:Nn { NV, cV, cv, Nx, cx }
\cs_new_protected:Nn \@@_tl_put_left_braced:Nn
{
 \tl_put_left:Nn #1 { { #2 } }
}
\cs_generate_variant:Nn \@@_tl_put_left_braced:Nn { NV, cV, cv, Nx, cx }

\cs_new_protected:Nn \@@_tl_gput_left_braced:Nn
{
 \tl_gput_left:Nn #1 { { #2 } }
}
\cs_generate_variant:Nn \@@_tl_gput_left_braced:Nn { NV, cV, cv, Nx, cx }
%    \end{macrocode}
%
%
% I had to think a bit about how to get \TeX\ to work the way I wanted.
% I'm really defining \emph{functions} but \TeX\ doesn't really have that concept, even with all the amazing \LaTeX3 stuff.
% The main issue I had was with scoping and return values.
% By default, \TeX\ functions aren't scoped -- they work on the same level as the calling functions.
% To protect the internals from being overwritten, each core function works inside a group.
% But then I have to work to get the answer out of it.
% So each of my core functions finishes by storing its return value in an appropriate \Verb+output+ variable.
% The core functions are then wrapped in a more user friendly interface that will take that output and assign it to a variable.
% This also means that I can deal with local and global versions without duplicating code.
%
%    \begin{macrocode}
\tl_new:N \g_@@_output_tl
\int_new:N \g_@@_output_int
\seq_new:N \g_@@_output_seq
\bool_new:N \g_@@_output_bool
%    \end{macrocode}
%
% To avoid creating vast numbers of variables, we provide ourselves with a few that we reuse frequently.
% For that reason, most of them don't have very exciting names.
%
% These are general purpose variables.
%    \begin{macrocode}
\tl_new:N \l_@@_tmpa_tl
\tl_new:N \l_@@_tmpb_tl
\tl_new:N \l_@@_tmpc_tl
\tl_new:N \l_@@_tmpd_tl
\tl_new:N \l_@@_tmpe_tl
\tl_new:N \l_@@_tmpf_tl
\tl_new:N \l_@@_tmpg_tl
\tl_new:N \l_@@_tmph_tl
\tl_new:N \l_@@_tmpi_tl

\seq_new:N \l_@@_tmpa_seq
\seq_new:N \l_@@_tmpb_seq
\seq_new:N \l_@@_tmpc_seq

\dim_new:N \l_@@_tmpa_dim
\dim_new:N \l_@@_tmpb_dim

\fp_new:N \l_@@_tmpa_fp
\fp_new:N \l_@@_tmpb_fp
\fp_new:N \l_@@_tmpc_fp
\fp_new:N \l_@@_tmpd_fp
\fp_new:N \l_@@_tmpe_fp
\fp_new:N \l_@@_tmpf_fp

\int_new:N \l_@@_tmpa_int
\int_new:N \l_@@_tmpb_int

\bool_new:N \l_@@_tmpa_bool
%    \end{macrocode}
%
% Whenever I need more than two \Verb+dim+ variables it is because I need to remember the position of a move.
%
%    \begin{macrocode}
\dim_new:N \l_@@_move_x_dim
\dim_new:N \l_@@_move_y_dim
%    \end{macrocode}
%
% Closed paths often need special handling.
% When it's needed, this will say whether the path is closed or not.
%
%    \begin{macrocode}
\bool_new:N \l_@@_closed_bool
%    \end{macrocode}
%
% True rectangles are rare, but need special handling.
% They are specified by two tokens, the first specifies the lower left corner which can be handled pretty much as other tokens but the second specifies the width and height meaning that it transforms differently.
% So when encountering on, the coordinates of the lower left corner are useful to remember.
%
%    \begin{macrocode}
\dim_new:N \l_@@_rectx_dim
\dim_new:N \l_@@_recty_dim

\bool_new:N \l_@@_rect_bool
%    \end{macrocode}
%
% When restoring a path we need to know whether to update the stored moveto.
%
%    \begin{macrocode}
\bool_new:N \l_spath_movetorelevant_bool
%    \end{macrocode}
%
% When manipulating soft paths, we might need to separate the shortening due to an arrow from when the path is rendered.
%
%    \begin{macrocode}
\bool_new:N \l_spath_arrow_shortening_bool
\bool_set_true:N \l_spath_arrow_shortening_bool
%    \end{macrocode}
%
% The intersection routine can't happen inside a group so we need two token lists to hold the paths that we'll intersect.
%
%    \begin{macrocode}
\tl_new:N \l_@@_intersecta_tl
\tl_new:N \l_@@_intersectb_tl
%    \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.
% These are global constants so that they can be used in other packages.
%    \begin{macrocode}
\tl_const:Nn \c_spath_moveto_tl {\pgfsyssoftpath@movetotoken}
\tl_const:Nn \c_spath_lineto_tl {\pgfsyssoftpath@linetotoken}
\tl_const:Nn \c_spath_curveto_tl {\pgfsyssoftpath@curvetotoken}
\tl_const:Nn \c_spath_curvetoa_tl {\pgfsyssoftpath@curvetosupportatoken}
\tl_const:Nn \c_spath_curvetob_tl {\pgfsyssoftpath@curvetosupportbtoken}
\tl_const:Nn \c_spath_closepath_tl {\pgfsyssoftpath@closepathtoken}
\tl_const:Nn \c_spath_rectcorner_tl {\pgfsyssoftpath@rectcornertoken}
\tl_const:Nn \c_spath_rectsize_tl {\pgfsyssoftpath@rectsizetoken}
%    \end{macrocode}
%
% We will want to be able to use anonymous spaths internally, so we create a global counter that we can use to refer to them.
%    \begin{macrocode}
\int_new:N \g_@@_anon_int
\int_gzero:N \g_@@_anon_int
%    \end{macrocode}
%
% \begin{macro}[internal]{
% \spath_anonymous:N,
% \spath_ganonymous:N
% }
% Set the token list to the next anonymous name.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \spath_anonymous:N #1
{
 \tl_set:Nx #1 {anonymous_\int_use:N \g_@@_anon_int}
 \int_gincr:N \g_@@_anon_int
}
\cs_new_protected_nopar:Npn \spath_ganonymous:N #1
{
 \tl_gset:Nx #1 {anonymous_\int_use:N \g_@@_anon_int}
 \int_gincr:N \g_@@_anon_int
}
\cs_generate_variant:Nn \spath_anonymous:N {c}
\cs_generate_variant:Nn \spath_ganonymous:N {c}
%    \end{macrocode}
% \end{macro}
%
% And some error messages
%    \begin{macrocode}
\msg_new:nnn { spath3 } { unknown path construction }
{ The~ path~ construction~ element~ #1~ is~ not~ currently~ supported.}
%    \end{macrocode}
%
%
% \subsection{Functional Implementation}
%
% In the functional approach, we start with a token list containing a soft path and do something to it (either calculate some information or manipulate it in some fashion).
% We then store that information, or the manipulated path, in an appropriate macro.
% The macro to store it in is the first argument.
% These functions occur in two versions, the one with the \texttt{g} makes the assignment global.
%
% \begin{macro}[internal]{
% \spath_segments_to_seq:Nn,
% \spath_segments_gto_seq:Nn
%}
% Splits a soft path into \emph{segments}, storing the result in a sequence.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_segments_to_seq:n #1
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpa_tl {#1}
 \tl_clear:N \l_@@_tmpb_tl
 \seq_clear:N \l_@@_tmpa_seq
 \dim_zero:N \l_@@_tmpa_dim
 \dim_zero:N \l_@@_tmpb_dim

 \bool_until_do:nn {
   \tl_if_empty_p:N \l_@@_tmpa_tl
 }
 {
   \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl}
   \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
   \token_case_meaning:NnF \l_@@_tmpc_tl
   {
     \c_spath_moveto_tl
     {
       \tl_set_eq:NN \l_@@_tmpb_tl \c_spath_moveto_tl
       \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}}
       \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl}
       \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

       \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}}
       \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl}
       \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

       \tl_set:Nx \l_@@_tmpd_tl {\tl_head:N \l_@@_tmpa_tl}
       \tl_if_eq:NNF \l_@@_tmpd_tl \c_spath_moveto_tl
       {
         \tl_clear:N \l_@@_tmpb_tl
       }

     }

     \c_spath_lineto_tl
     {
       \tl_set_eq:NN \l_@@_tmpb_tl \c_spath_moveto_tl
       \tl_put_right:Nx \l_@@_tmpb_tl
       {
         {\dim_use:N \l_@@_tmpa_dim}
         {\dim_use:N \l_@@_tmpb_dim}
       }
       \tl_put_right:NV \l_@@_tmpb_tl \c_spath_lineto_tl

       \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}}
       \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl}
       \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

       \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}}
       \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl}
       \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

     }

     \c_spath_curvetoa_tl
     {
       \tl_set_eq:NN \l_@@_tmpb_tl \c_spath_moveto_tl
       \tl_put_right:Nx \l_@@_tmpb_tl
       {
         {\dim_use:N \l_@@_tmpa_dim}
         {\dim_use:N \l_@@_tmpb_dim}
       }
       \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curvetoa_tl

       \prg_replicate:nn {2} {
         \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}}
         \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
         \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}}
         \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
         \tl_put_right:Nx \l_@@_tmpb_tl {\tl_head:N \l_@@_tmpa_tl}
         \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
       }

       \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}}
       \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl}
       \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

       \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}}
       \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl}
       \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

     }

     \c_spath_rectcorner_tl
     {
       \tl_set_eq:NN \l_@@_tmpb_tl \c_spath_rectcorner_tl

       \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}}
       \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
       \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}}
       \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
       \tl_put_right:Nx \l_@@_tmpb_tl {\tl_head:N \l_@@_tmpa_tl}
       \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

       \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}}
       \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl}
       \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

       \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}}
       \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl}
       \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

     }

     \c_spath_closepath_tl
     {
       \tl_set_eq:NN \l_@@_tmpb_tl \c_spath_moveto_tl
       \tl_put_right:Nx \l_@@_tmpb_tl
       {
         {\dim_use:N \l_@@_tmpa_dim}
         {\dim_use:N \l_@@_tmpb_dim}
       }
       \tl_put_right:NV \l_@@_tmpb_tl \c_spath_lineto_tl

       \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}}
       \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl}
       \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

       \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}}
       \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl}
       \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

     }

   }
   {

     \tl_set_eq:NN \l_@@_tmpb_tl \l_@@_tmpc_tl
     \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}}
     \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl}
     \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

     \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}}
     \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl}
     \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

   }

   \tl_if_empty:NF \l_@@_tmpb_tl
   {
     \seq_put_right:NV \l_@@_tmpa_seq \l_@@_tmpb_tl
   }
   \tl_clear:N \l_@@_tmpb_tl
 }

 \seq_gclear:N \g_@@_output_seq
 \seq_gset_eq:NN \g_@@_output_seq \l_@@_tmpa_seq
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_segments_to_seq:Nn #1#2
{
 \@@_segments_to_seq:n {#2}
 \seq_clear_new:N #1
 \seq_set_eq:NN #1 \g_@@_output_seq
 \seq_gclear:N \g_@@_output_seq
}
\cs_generate_variant:Nn \spath_segments_to_seq:Nn {NV, cn, cV, Nv, cv}
\cs_new_protected_nopar:Npn \spath_segments_gto_seq:Nn #1#2
{
 \@@_segments_to_seq:n {#2}
 \seq_clear_new:N #1
 \seq_gset_eq:NN #1 \g_@@_output_seq
 \seq_gclear:N \g_@@_output_seq
}
\cs_generate_variant:Nn \spath_segments_gto_seq:Nn {NV, cn, cV, Nv, cv}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_components_to_seq:Nn,
% \spath_components_gto_seq:Nn,
% \spath_components_to_clist:Nn,
% \spath_components_gto_clist:Nn,
% }
% Splits a soft path into \emph{components}, storing the result in a sequence or a clist.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_components_to_seq:n #1
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpa_tl {#1}
 \seq_clear:N \l_@@_tmpa_seq
 \tl_set:Nx \l_@@_tmpb_tl {\tl_head:N \l_@@_tmpa_tl}
 \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

 \tl_put_right:NV \l_@@_tmpa_tl \c_spath_moveto_tl

 \bool_do_until:nn {
   \tl_if_empty_p:N \l_@@_tmpa_tl
 }
 {
   \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl}
   \tl_if_eq:NNT \l_@@_tmpc_tl \c_spath_moveto_tl
   {
     \seq_put_right:NV \l_@@_tmpa_seq \l_@@_tmpb_tl
     \tl_clear:N \l_@@_tmpb_tl
   }
   \tl_if_eq:NNT \l_@@_tmpc_tl \c_spath_rectcorner_tl
   {
     \seq_put_right:NV \l_@@_tmpa_seq \l_@@_tmpb_tl
     \tl_clear:N \l_@@_tmpb_tl
   }
   \tl_if_single:NTF \l_@@_tmpc_tl
   {
     \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpc_tl
   }
   {
     \tl_put_right:Nx \l_@@_tmpb_tl {{\l_@@_tmpc_tl}}
   }
   \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
 }

 \seq_gclear:N \g_@@_output_seq
 \seq_gset_eq:NN \g_@@_output_seq \l_@@_tmpa_seq
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_components_to_seq:Nn #1#2
{
 \@@_components_to_seq:n {#2}
 \seq_clear_new:N #1
 \seq_set_eq:NN #1 \g_@@_output_seq
 \seq_gclear:N \g_@@_output_seq
}
\cs_generate_variant:Nn \spath_components_to_seq:Nn {NV, cn, cV, cv, Nv}
\cs_new_protected_nopar:Npn \spath_components_gto_seq:Nn #1#2
{
 \@@_components_to_seq:n {#2}
 \seq_clear_new:N #1
 \seq_gset_eq:NN #1 \g_@@_output_seq
 \seq_gclear:N \g_@@_output_seq
}
\cs_generate_variant:Nn \spath_components_gto_seq:Nn {NV, cn, cV, cv, Nv}
\cs_new_protected_nopar:Npn \spath_components_to_clist:Nn #1#2
{
 \@@_components_to_seq:n {#2}
 \clist_clear_new:N #1
 \clist_set_from_seq:NN #1 \g_@@_output_seq
 \seq_gclear:N \g_@@_output_seq
}
\cs_generate_variant:Nn \spath_components_to_clist:Nn {NV, cn, cV, cv, Nv}
\cs_new_protected_nopar:Npn \spath_components_gto_clist:Nn #1#2
{
 \@@_components_to_seq:n {#2}
 \clist_clear_new:N #1
 \clist_gset_from_seq:NN #1 \g_@@_output_seq
 \seq_gclear:N \g_@@_output_seq
}
\cs_generate_variant:Nn \spath_components_gto_clist:Nn {NV, cn, cV, cv, Nv}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[internal]{\spath_length:n}
% Counts the number of triples in the path.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \spath_length:n #1
{
 \int_eval:n {\tl_count:n {#1} / 3}
}
\cs_generate_variant:Nn \spath_length:n {V}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_reallength:Nn,
% \spath_greallength:Nn
% }
% The real length of a path is the number of triples that actually draw something (that is, the number of lines, curves, rectangles, and closepaths).
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_reallength:n #1
{
 \group_begin:
 \int_set:Nn \l_@@_tmpa_int {0}
 \tl_map_inline:nn {#1} {
   \tl_set:Nn \l_@@_tmpa_tl {##1}
   \token_case_meaning:NnT \l_@@_tmpa_tl
   {
     \c_spath_lineto_tl {}
     \c_spath_curveto_tl {}
     \c_spath_closepath_tl {}
     \c_spath_rectsize_tl {}
   }
   {
     \int_incr:N \l_@@_tmpa_int
   }
 }
 \int_gzero:N \g_@@_output_int
 \int_gset_eq:NN \g_@@_output_int \l_@@_tmpa_int
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_reallength:Nn #1#2
{
 \@@_reallength:n {#2}
 \int_set_eq:NN #1 \g_@@_output_int
 \int_gzero:N \g_@@_output_int
}
\cs_generate_variant:Nn \spath_reallength:Nn {NV, cn, cV, Nv, cv}
\cs_new_protected_nopar:Npn \spath_greallength:Nn #1#2
{
 \@@_reallength:n {#2}
 \int_gset_eq:NN #1 \g_@@_output_int
 \int_gzero:N \g_@@_output_int
}
\cs_generate_variant:Nn \spath_greallength:Nn {NV, cn, cV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_numberofcomponents:Nn,
% \spath_gnumberofcomponents:Nn
% }
% 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_protected_nopar:Npn \@@_numberofcomponents:n #1
{
 \group_begin:
 \int_set:Nn \l_@@_tmpa_int {0}
 \tl_map_inline:nn {#1} {
   \tl_set:Nn \l_@@_tmpa_tl {##1}
   \token_case_meaning:Nn \l_@@_tmpa_tl
   {
     \c_spath_moveto_tl
     {
       \int_incr:N \l_@@_tmpa_int
     }
     \c_spath_rectcorner_tl
     {
       \int_incr:N \l_@@_tmpa_int
     }
   }
 }
 \int_gzero:N \g_@@_output_int
 \int_gset_eq:NN \g_@@_output_int \l_@@_tmpa_int
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_numberofcomponents:Nn #1#2
{
 \@@_numberofcomponents:n {#2}
 \int_set_eq:NN #1 \g_@@_output_int
 \int_gzero:N \g_@@_output_int
}
\cs_generate_variant:Nn \spath_numberofcomponents:Nn {NV, cn, cV, Nv}
\cs_new_protected_nopar:Npn \spath_gnumberofcomponents:Nn #1#2
{
 \@@_numberofcomponents:n {#2}
 \int_gset_eq:NN #1 \g_@@_output_int
 \int_gzero:N \g_@@_output_int
}
\cs_generate_variant:Nn \spath_gnumberofcomponents:Nn {NV, cn, cV, Nv}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_initialpoint:Nn,
% \spath_ginitialpoint:Nn
% }
% The starting point of the path.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_initialpoint:n #1
{
 \group_begin:
 \tl_clear:N \l_@@_tmpa_tl
 \tl_set:Nx \l_@@_tmpa_tl
 {
   { \tl_item:nn {#1} {2} }
   { \tl_item:nn {#1} {3} }
 }
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_initialpoint:Nn #1#2
{
 \@@_initialpoint:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_initialpoint:Nn {NV, cn, cV, Nv}
\cs_new_protected_nopar:Npn \spath_ginitialpoint:Nn #1#2
{
 \@@_initialpoint:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_ginitialpoint:Nn {NV, cn, cV, Nv}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_finalpoint:Nn,
% \spath_gfinalpoint:Nn,
% }
% The final point of the path.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_finalpoint:n #1
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpa_tl {#1}
 \tl_reverse:N \l_@@_tmpa_tl
 \tl_clear:N \l_@@_tmpb_tl
 \tl_set:Nx \l_@@_tmpc_tl {\tl_item:Nn \l_@@_tmpa_tl {3}}
 \tl_if_eq:NNTF \l_@@_tmpc_tl \c_spath_rectsize_tl
 {
   \tl_set:Nx \l_@@_tmpb_tl
   {
     {
       \dim_eval:n
       {
         \tl_item:Nn \l_@@_tmpa_tl {2}
         +
         \tl_item:Nn \l_@@_tmpa_tl {5}
       }
     }
     {
       \dim_eval:n
       {
         \tl_item:Nn \l_@@_tmpa_tl {1}
         +
         \tl_item:Nn \l_@@_tmpa_tl {4}
       }
     }
   }
 }
 {
   \tl_set:Nx \l_@@_tmpb_tl
   {
     { \tl_item:Nn \l_@@_tmpa_tl {2} }
     { \tl_item:Nn \l_@@_tmpa_tl {1} }
   }
 }
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_finalpoint:Nn #1#2
{
 \@@_finalpoint:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_finalpoint:Nn {NV, cn, cV, Nv}
\cs_new_protected_nopar:Npn \spath_gfinalpoint:Nn #1#2
{
 \@@_finalpoint:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gfinalpoint:Nn {NV, cn, cV, Nv}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_finalmovepoint:Nn,
% \spath_gfinalmovepoint:Nn
% }
% Get the last move on the path.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_finalmovepoint:n #1
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpc_tl { {0pt} {0pt} }
 \tl_set:Nn \l_@@_tmpa_tl {#1}
 \bool_do_until:nn
 {
   \tl_if_empty_p:N \l_@@_tmpa_tl
 }
 {
   \tl_set:Nx \l_@@_tmpb_tl {\tl_head:N \l_@@_tmpa_tl}
   \token_case_meaning:Nn \l_@@_tmpb_tl
   {
     \c_spath_moveto_tl
     {
       \tl_set:Nx \l_@@_tmpc_tl
       {
         { \tl_item:Nn \l_@@_tmpa_tl {2} }
         { \tl_item:Nn \l_@@_tmpa_tl {3} }
       }
     }

     \c_spath_rectcorner_tl
     {
       \tl_set:Nx \l_@@_tmpc_tl
       {
         { \tl_item:Nn \l_@@_tmpa_tl {2} }
         { \tl_item:Nn \l_@@_tmpa_tl {3} }
       }
     }

   }
   \prg_replicate:nn {3}
   {
     \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
   }
 }
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpc_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_finalmovepoint:Nn #1#2
{
 \@@_finalmovepoint:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_finalmovepoint:Nn {NV, cn, cV}
\cs_new_protected_nopar:Npn \spath_gfinalmovepoint:Nn #1#2
{
 \@@_finalmovepoint:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gfinalmovepoint:Nn {NV, cn, cV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_initialtangent:Nn,
% \spath_ginitialtangent:Nn
% }
% The starting tangent of the path.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_initialtangent:n #1
{
 \group_begin:
 \tl_set:Nx \l_@@_tmpb_tl {\tl_item:nn {#1} {4}}
 \tl_if_eq:NNTF \l_@@_tmpb_tl \c_spath_curvetoa_tl
 {
   \fp_set:Nn \l_@@_tmpa_fp {3}
 }
 {
   \fp_set:Nn \l_@@_tmpa_fp {1}
 }
 \tl_clear:N \l_@@_tmpa_tl
 \tl_set:Nx \l_@@_tmpa_tl
 {
   {
     \fp_to_dim:n {
       \l_@@_tmpa_fp
       *
       (
       \tl_item:nn {#1} {5}
       -
       \tl_item:nn {#1} {2}
       )
     }
   }
   {
     \fp_to_dim:n {
       \l_@@_tmpa_fp
       *
       (
       \tl_item:nn {#1} {6}
       -
       \tl_item:nn {#1} {3}
       )
     }
   }
 }
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_initialtangent:Nn #1#2
{
 \@@_initialtangent:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_initialtangent:Nn {NV, cn, cV, Nv}
\cs_new_protected_nopar:Npn \spath_ginitialtangent:Nn #1#2
{
 \@@_initialtangent:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_ginitialtangent:Nn {NV, cn, cV, Nv}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_finaltangent:Nn,
% \spath_gfinaltangent:Nn,
% }
% The final tangent of the path.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_finaltangent:n #1
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpa_tl {#1}
 \tl_reverse:N \l_@@_tmpa_tl
 \tl_set:Nx \l_@@_tmpb_tl {\tl_item:nn {#1} {6}}
 \tl_if_eq:NNTF \l_@@_tmpb_tl \c_spath_curveto_tl
 {
   \fp_set:Nn \l_@@_tmpa_fp {3}
 }
 {
   \fp_set:Nn \l_@@_tmpa_fp {1}
 }
 \tl_clear:N \l_@@_tmpb_tl
 \tl_set:Nx \l_@@_tmpb_tl
 {
   {
     \fp_to_dim:n {
       \l_@@_tmpa_fp
       *
       (
       \tl_item:Nn \l_@@_tmpa_tl {2}
       -
       \tl_item:Nn \l_@@_tmpa_tl {5}
       )
     }
   }
   {
     \fp_to_dim:n {
       \l_@@_tmpa_fp
       *
       (
       \tl_item:Nn \l_@@_tmpa_tl {1}
       -
       \tl_item:Nn \l_@@_tmpa_tl {4}
       )
     }
   }
 }
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_finaltangent:Nn #1#2
{
 \@@_finaltangent:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_finaltangent:Nn {NV, cn, cV, Nv}
\cs_new_protected_nopar:Npn \spath_gfinaltangent:Nn #1#2
{
 \@@_finaltangent:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gfinaltangent:Nn {NV, cn, cV, Nv}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[internal]{
% \spath_finalmovetangent:Nn,
% \spath_gfinalmovetangent:Nn
% }
% Get the last move on the path.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_finalmovetangent:n #1
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpc_tl { {0pt} {0pt} }
 \tl_set:Nn \l_@@_tmpa_tl {#1}
 \bool_do_until:nn
 {
   \tl_if_empty_p:N \l_@@_tmpa_tl
 }
 {
   \tl_set:Nx \l_@@_tmpb_tl {\tl_head:N \l_@@_tmpa_tl}
   \token_case_meaning:Nn \l_@@_tmpb_tl
   {
     \c_spath_moveto_tl
     {
       \tl_set:Nx \l_@@_tmpd_tl { \tl_item:Nn \l_@@_tmpa_tl {4} }
       \tl_if_eq:NNTF \l_@@_tmpd_tl \c_spath_curveto_tl
       {
         \fp_set:Nn \l_@@_tmpa_fp {3}
       }
       {
         \fp_set:Nn \l_@@_tmpa_fp {1}
       }
       \tl_set:Nx \l_@@_tmpc_tl
       {
         {
           \fp_to_dim:n
           {
             \l_@@_tmpa_fp
             *
             (
             \tl_item:Nn \l_@@_tmpa_tl {5}
             -
             \tl_item:Nn \l_@@_tmpa_tl {2}
             )
           }
         }
         {
           \fp_to_dim:n
           {
             \l_@@_tmpa_fp
             *
             (
             \tl_item:Nn \l_@@_tmpa_tl {6}
             -
             \tl_item:Nn \l_@@_tmpa_tl {3}
             )
           }
         }
       }
     }
   }
   \prg_replicate:nn {3}
   {
     \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
   }
 }
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpc_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_finalmovetangent:Nn #1#2
{
 \@@_finalmovetangent:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_finalmovetangent:Nn {NV, cn, cV}
\cs_new_protected_nopar:Npn \spath_gfinalmovetangent:Nn #1#2
{
 \@@_finalmovetangent:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gfinalmovetangent:Nn {NV, cn, cV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_reverse:Nn,
% \spath_greverse:Nn
% }
% This computes the reverse of the path.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_reverse:n #1
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpa_tl {#1}

 \tl_clear:N \l_@@_tmpb_tl
 \tl_clear:N \l_@@_tmpd_tl

 \bool_set_false:N \l_@@_rect_bool

 \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl}
 \bool_while_do:nn {
   \tl_if_eq_p:NN \l_@@_tmpc_tl \c_spath_rectcorner_tl
 }
 {
   \tl_put_left:Nx \l_@@_tmpd_tl
   {
     \tl_item:Nn \l_@@_tmpa_tl {1}
     {\tl_item:Nn \l_@@_tmpa_tl {2}}
     {\tl_item:Nn \l_@@_tmpa_tl {3}}
     \tl_item:Nn \l_@@_tmpa_tl {4}
     {\tl_item:Nn \l_@@_tmpa_tl {5}}
     {\tl_item:Nn \l_@@_tmpa_tl {6}}
   }
   \prg_replicate:nn {6}
   {
     \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
   }
   \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl}
   \bool_set_true:N \l_@@_rect_bool
 }

 \tl_if_empty:NF \l_@@_tmpa_tl
 {
   \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
   \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl}
   \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
   \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl}
   \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

   \tl_put_left:Nx \l_@@_tmpd_tl
   {
     {\dim_use:N \l_@@_tmpa_dim}
     {\dim_use:N \l_@@_tmpb_dim}
   }

   \bool_set_false:N \l_@@_closed_bool

   \bool_until_do:nn {
     \tl_if_empty_p:N \l_@@_tmpa_tl
   }
   {
     \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl}
     \bool_set_false:N \l_@@_rect_bool

     \token_case_meaning:NnTF \l_@@_tmpc_tl
     {
       \c_spath_moveto_tl {

         \bool_if:NT \l_@@_closed_bool
         {
           \tl_put_right:NV \l_@@_tmpd_tl \c_spath_closepath_tl
           \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpd_tl}
           \tl_put_right:Nx \l_@@_tmpd_tl
           {
             { \tl_head:N \l_@@_tmpd_tl }
             { \tl_head:N \l_@@_tmpe_tl }
           }
         }
         \bool_set_false:N \l_@@_closed_bool
         \tl_put_left:NV \l_@@_tmpd_tl \c_spath_moveto_tl
         \tl_put_left:NV \l_@@_tmpb_tl \l_@@_tmpd_tl
         \tl_clear:N \l_@@_tmpd_tl
       }
       \c_spath_rectcorner_tl {

         \bool_if:NT \l_@@_closed_bool
         {
           \tl_put_right:NV \l_@@_tmpd_tl \c_spath_closepath_tl
           \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpd_tl}
           \tl_put_right:Nx \l_@@_tmpd_tl
           {
             { \tl_head:N \l_@@_tmpd_tl }
             { \tl_head:N \l_@@_tmpe_tl }
           }
         }
         \bool_set_false:N \l_@@_closed_bool
         \tl_put_left:NV \l_@@_tmpd_tl \c_spath_moveto_tl
         \tl_put_left:NV \l_@@_tmpb_tl \l_@@_tmpd_tl
         \tl_clear:N \l_@@_tmpd_tl

         \bool_while_do:nn {
           \tl_if_eq_p:NN \l_@@_tmpc_tl \c_spath_rectcorner_tl
         }
         {
           \tl_put_left:Nx \l_@@_tmpb_tl
           {
             \tl_item:Nn \l_@@_tmpa_tl {1}
             {\tl_item:Nn \l_@@_tmpa_tl {2}}
             {\tl_item:Nn \l_@@_tmpa_tl {3}}
             \tl_item:Nn \l_@@_tmpa_tl {4}
             {\tl_item:Nn \l_@@_tmpa_tl {5}}
             {\tl_item:Nn \l_@@_tmpa_tl {6}}
           }
           \prg_replicate:nn {6}
           {
             \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
           }
           \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl}
         }
         \bool_set_true:N \l_@@_rect_bool

       }
       \c_spath_lineto_tl {
         \tl_put_left:NV \l_@@_tmpd_tl \c_spath_lineto_tl
       }
       \c_spath_curveto_tl {
         \tl_put_left:NV \l_@@_tmpd_tl \c_spath_curvetoa_tl
       }
       \c_spath_curvetoa_tl {
         \tl_put_left:NV \l_@@_tmpd_tl \c_spath_curveto_tl
       }
       \c_spath_curvetob_tl {
         \tl_put_left:NV \l_@@_tmpd_tl \c_spath_curvetob_tl
       }
     }
     {
       \tl_if_empty:NF \l_@@_tmpa_tl
       {
       \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

       \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl}
       \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
       \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl}
       \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

       \tl_put_left:Nx \l_@@_tmpd_tl
       {
         {\dim_use:N \l_@@_tmpa_dim}
         {\dim_use:N \l_@@_tmpb_dim}
       }
       }
     }
     {
       \tl_if_eq:NNTF \l_@@_tmpc_tl \c_spath_closepath_tl
       {
         \bool_set_true:N \l_@@_closed_bool
       }
       {
         \msg_warning:nnx
         { spath3 }
         { unknown path construction }
         { \l_@@_tmpc_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}

     }
   }

   \bool_if:NT \l_@@_closed_bool
   {
     \tl_put_right:NV \l_@@_tmpd_tl \c_spath_closepath_tl
     \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpd_tl}
     \tl_put_right:Nx \l_@@_tmpd_tl
     {
       { \tl_head:N \l_@@_tmpd_tl }
       { \tl_head:N \l_@@_tmpe_tl }
     }
   }

   \bool_set_false:N \l_@@_closed_bool
   \bool_if:NF \l_@@_rect_bool
   {
     \tl_put_left:NV \l_@@_tmpd_tl \c_spath_moveto_tl
   }
 }

 \tl_put_left:NV \l_@@_tmpb_tl \l_@@_tmpd_tl
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_reverse:Nn #1#2
{
 \@@_reverse:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_reverse:Nn {NV, cn, cV, Nv}
\cs_new_protected_nopar:Npn \spath_reverse:N #1
{
 \spath_reverse:NV #1#1
}
\cs_generate_variant:Nn \spath_reverse:N {c}
\cs_new_protected_nopar:Npn \spath_greverse:Nn #1#2
{
 \@@_reverse:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_greverse:Nn {NV, cn, cV, Nv}
\cs_new_protected_nopar:Npn \spath_greverse:N #1
{
 \spath_greverse:NV #1#1
}
\cs_generate_variant:Nn \spath_greverse:N {c}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_initialaction:Nn,
% \spath_ginitialaction:Nn,
% }
% This is the first thing that the path does (after the initial move).
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_initialaction:n #1
{
 \group_begin:
 \tl_clear:N \l_@@_tmpa_tl
 \tl_set:Nx \l_@@_tmpb_tl {\tl_head:n {#1}}
 \tl_if_eq:NNTF \l_@@_tmpb_tl \c_spath_rectcorner_tl
 {
   \tl_set_eq:NN \l_@@_tmpa_tl \c_spath_rectcorner_tl
 }
 {
   \int_compare:nT
   {
     \tl_count:n {#1} > 3
   }
   {
     \tl_set:Nx \l_@@_tmpa_tl
     {
       \tl_item:Nn {#1} {4}
     }
   }
 }
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_initialaction:Nn #1#2
{
 \@@_initialaction:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_initialaction:Nn {NV}
\cs_new_protected_nopar:Npn \spath_ginitialaction:Nn #1#2
{
 \@@_initialaction:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_ginitialaction:Nn {NV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_finalaction:Nn,
% \spath_gfinalaction:Nn
% }
% This is the last thing that the path does.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_finalaction:n #1
{
 \group_begin:
 \tl_clear:N \l_@@_tmpb_tl
 \int_compare:nT
 {
   \tl_count:n {#1} > 3
 }
 {
   \tl_set:Nn \l_@@_tmpa_tl {#1}
   \tl_reverse:N \l_@@_tmpa_tl
   \tl_set:Nx \l_@@_tmpb_tl
   {
     \tl_item:Nn \l_@@_tmpa_tl {3}
   }
   \tl_if_eq:NNT \l_@@_tmpb_tl \c_spath_curvetoa_tl
   {
     \tl_set_eq:NN \l_@@_tmpb_tl \c_spath_curveto_tl
   }
   \tl_if_eq:NNT \l_@@_tmpb_tl \c_spath_rectsize_tl
   {
     \tl_set_eq:NN \l_@@_tmpb_tl \c_spath_rectcorner_tl
   }
 }
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_finalaction:Nn #1#2
{
 \@@_finalaction:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_finalaction:Nn {NV}
\cs_new_protected_nopar:Npn \spath_gfinalaction:Nn #1#2
{
 \@@_finalaction:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gfinalaction:Nn {NV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_minbb:Nn,
% \spath_gminbb:Nn
% }
% This computes the minimum (bottom left) of the bounding box of the path.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_minbb:n #1
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpa_tl {#1}

 \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl}
 \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

 \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl}
 \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

 \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl}
 \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

 \tl_if_eq:NNT \l_@@_tmpc_tl \c_spath_rectcorner_tl
 {
   \dim_set_eq:NN \l_@@_rectx_dim \l_@@_tmpa_dim
   \dim_set_eq:NN \l_@@_recty_dim \l_@@_tmpb_dim
 }
 \bool_until_do:nn {
   \tl_if_empty_p:N \l_@@_tmpa_tl
 }
 {
   \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl}
   \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

   \tl_if_eq:NNTF \l_@@_tmpc_tl \c_spath_rectsize_tl
   {
     \dim_set:Nn \l_@@_tmpa_dim
     {\dim_min:nn
       {\tl_head:N \l_@@_tmpa_tl + \l_@@_rectx_dim} {\l_@@_tmpa_dim}
     }
     \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

     \dim_set:Nn \l_@@_tmpb_dim
     {\dim_min:nn
       {\tl_head:N \l_@@_tmpa_tl + \l_@@_recty_dim} {\l_@@_tmpb_dim}
     }
     \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
   }
   {
     \dim_set:Nn \l_@@_tmpa_dim
     {\dim_min:nn {\tl_head:N \l_@@_tmpa_tl} {\l_@@_tmpa_dim}}
     \tl_if_eq:NNT \l_@@_tmpc_tl \c_spath_rectcorner_tl
     {
       \dim_set:Nn \l_@@_rectx_dim {\tl_head:N \l_@@_tmpa_tl}
     }
     \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

     \dim_set:Nn \l_@@_tmpb_dim
     {\dim_min:nn {\tl_head:N \l_@@_tmpa_tl} {\l_@@_tmpb_dim}}
     \tl_if_eq:NNT \l_@@_tmpc_tl \c_spath_rectcorner_tl
     {
       \dim_set:Nn \l_@@_recty_dim {\tl_head:N \l_@@_tmpa_tl}
     }
     \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
   }

 }
 \tl_clear:N \l_@@_tmpb_tl
 \tl_put_right:Nx \l_@@_tmpb_tl
 {
   {\dim_use:N \l_@@_tmpa_dim}
   {\dim_use:N \l_@@_tmpb_dim}
 }
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_minbb:Nn #1#2
{
 \@@_minbb:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_minbb:Nn {NV, cn, cV}
\cs_new_protected_nopar:Npn \spath_gminbb:Nn #1#2
{
 \@@_minbb:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gminbb:Nn {NV, cn, cV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_maxbb:Nn,
% \spath_gmaxbb:Nn,
% }
% This computes the maximum (top right) of the bounding box of the path.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_maxbb:n #1
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpa_tl {#1}

 \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl}
 \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

 \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl}
 \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

 \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl}
 \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

 \tl_if_eq:NNT \l_@@_tmpc_tl \c_spath_rectcorner_tl
 {
   \dim_set_eq:NN \l_@@_rectx_dim \l_@@_tmpa_dim
   \dim_set_eq:NN \l_@@_recty_dim \l_@@_tmpb_dim
 }
 \bool_until_do:nn {
   \tl_if_empty_p:N \l_@@_tmpa_tl
 }
 {
   \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl}
   \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

   \tl_if_eq:NNTF \l_@@_tmpc_tl \c_spath_rectsize_tl
   {
     \dim_set:Nn \l_@@_tmpa_dim
     {\dim_max:nn
       {\tl_head:N \l_@@_tmpa_tl + \l_@@_rectx_dim} {\l_@@_tmpa_dim}
     }
     \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

     \dim_set:Nn \l_@@_tmpb_dim
     {\dim_max:nn
       {\tl_head:N \l_@@_tmpa_tl + \l_@@_recty_dim} {\l_@@_tmpb_dim}
     }
     \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
   }
   {
     \dim_set:Nn \l_@@_tmpa_dim
     {\dim_max:nn {\tl_head:N \l_@@_tmpa_tl} {\l_@@_tmpa_dim}}
     \tl_if_eq:NNT \l_@@_tmpc_tl \c_spath_rectcorner_tl
     {
       \dim_set:Nn \l_@@_rectx_dim {\tl_head:N \l_@@_tmpa_tl}
     }
     \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

     \dim_set:Nn \l_@@_tmpb_dim
     {\dim_max:nn {\tl_head:N \l_@@_tmpa_tl} {\l_@@_tmpb_dim}}
     \tl_if_eq:NNT \l_@@_tmpc_tl \c_spath_rectcorner_tl
     {
       \dim_set:Nn \l_@@_recty_dim {\tl_head:N \l_@@_tmpa_tl}
     }
     \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
   }

 }
 \tl_clear:N \l_@@_tmpb_tl
 \tl_set:Nx \l_@@_tmpb_tl
 {
   {\dim_use:N \l_@@_tmpa_dim}
   {\dim_use:N \l_@@_tmpb_dim}
 }
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_maxbb:Nn #1#2
{
 \@@_maxbb:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_maxbb:Nn {NV, cn, cV}
\cs_new_protected_nopar:Npn \spath_gmaxbb:Nn #1#2
{
 \@@_maxbb:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gmaxbb:Nn {NV, cn, cV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_save_to_aux:Nn,
% \spath_save_to_aux:N
% }
% This saves a soft path to the auxfile.
% The first argument is the macro that will be assigned to the soft path when the aux file is read back in.
%    \begin{macrocode}
\int_set:Nn \l_@@_tmpa_int {\char_value_catcode:n {`@}}
\char_set_catcode_letter:N @
\cs_new_protected_nopar:Npn \spath_save_to_aux:Nn #1#2 {
 \tl_if_empty:nF {#2}
 {
   \tl_clear:N \l_@@_tmpa_tl
   \tl_put_right:Nn \l_@@_tmpa_tl {
     \ExplSyntaxOn
     \tl_gclear_new:N #1
     \tl_gset:Nn #1 {#2}
     \ExplSyntaxOff
   }
   \protected@write\@auxout{}{
     \tl_to_str:N \l_@@_tmpa_tl
   }
 }
}
\char_set_catcode:nn {`@} {\l_@@_tmpa_int}
\cs_generate_variant:Nn \spath_save_to_aux:Nn {cn, cV, NV}
\cs_new_protected_nopar:Npn \spath_save_to_aux:N #1
{
 \tl_if_exist:NT #1
 {
   \spath_save_to_aux:NV #1#1
 }
}
\cs_generate_variant:Nn \spath_save_to_aux:N {c}
%    \end{macrocode}
% \end{macro}
%
%
% \subsection{Path Manipulation}
%
% These functions all manipulate a soft path.
% They come with a variety of different argument specifications.
% As a general rule, the first argument is the macro in which to store the modified path, the second is the path to manipulate, and the rest are the information about what to do.
% There is always a variant in which the path is specified by a macro and restored back in that same macro.
%
%
% \begin{macro}[internal]{
% \spath_translate:Nnnn,
% \spath_translate:Nnn,
% \spath_gtranslate:Nnnn,
% \spath_gtranslate:Nnn
% }
% Translates a path by an amount.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_translate:nnn #1#2#3
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpa_tl {#1}
 \tl_clear:N \l_@@_tmpb_tl
 \bool_until_do:nn {
   \tl_if_empty_p:N \l_@@_tmpa_tl
 }
 {
   \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl}

   \tl_put_right:Nx \l_@@_tmpb_tl {\tl_head:N \l_@@_tmpa_tl}
   \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

   \tl_if_eq:NNTF \l_@@_tmpc_tl \c_spath_rectsize_tl
   {
     \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl}
   }
   {
     \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl + #2}
   }
   \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

   \tl_if_eq:NNTF \l_@@_tmpc_tl \c_spath_rectsize_tl
   {
     \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl}
   }
   {
     \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl + #3}
   }
   \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

   \tl_put_right:Nx \l_@@_tmpb_tl
   {
     {\dim_use:N \l_@@_tmpa_dim}
     {\dim_use:N \l_@@_tmpb_dim}
   }
 }
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl
 \group_end:
}
\cs_generate_variant:Nn \@@_translate:nnn {nVV}
\cs_new_protected_nopar:Npn \spath_translate:Nnnn #1#2#3#4
{
 \@@_translate:nnn {#2}{#3}{#4}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_translate:Nnnn {NVxx, NVVV, NVnn}
\cs_new_protected_nopar:Npn \spath_translate:Nnn #1#2#3
{
 \spath_translate:NVnn #1#1{#2}{#3}
}
\cs_generate_variant:Nn \spath_translate:Nnn {NVV, cnn, cVV}
\cs_new_protected_nopar:Npn \spath_gtranslate:Nnnn #1#2#3#4
{
 \@@_translate:nnn {#2}{#3}{#4}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gtranslate:Nnnn {NVxx, NVVV, NVnn}
\cs_new_protected_nopar:Npn \spath_gtranslate:Nnn #1#2#3
{
 \spath_gtranslate:NVnn #1#1{#2}{#3}
}
\cs_generate_variant:Nn \spath_gtranslate:Nnn {NVV, cnn, cVV}
%    \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_protected_nopar:Npn \spath_translate:Nn #1#2
{
 \spath_translate:Nnn #1 #2
}
\cs_generate_variant:Nn \spath_translate:Nn {NV}
\cs_new_protected_nopar:Npn \spath_gtranslate:Nn #1#2
{
 \spath_gtranslate:Nnn #1 #2
}
\cs_generate_variant:Nn \spath_gtranslate:Nn {NV}
%    \end{macrocode}
% \end{macro}
%
%
%
% \begin{macro}[internal]{
% \spath_translate_to:Nnnn,
% \spath_translate_to:Nnn,
% \spath_gtranslate_to:Nnnn,
% \spath_gtranslate_to:Nnn
% }
% Translates a path so that it starts at a point.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_translate_to:nnn #1#2#3
{
 \group_begin:
 \spath_initialpoint:Nn \l_@@_tmpa_tl {#1}

 \dim_set:Nn \l_@@_tmpa_dim
 {
   #2
   -
   \tl_item:Nn \l_@@_tmpa_tl {1}
 }
 \dim_set:Nn \l_@@_tmpb_dim
 {
   #3
   -
   \tl_item:Nn \l_@@_tmpa_tl {2}
 }

 \@@_translate:nVV {#1} \l_@@_tmpa_dim \l_@@_tmpb_dim
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_translate_to:Nnnn #1#2#3#4
{
 \@@_translate_to:nnn {#2}{#3}{#4}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_translate_to:Nnnn {NVxx, NVVV, NVnn}
\cs_new_protected_nopar:Npn \spath_translate_to:Nnn #1#2#3
{
 \spath_translate_to:NVnn #1#1{#2}{#3}
}
\cs_generate_variant:Nn \spath_translate_to:Nnn {NVV, cnn, cVV}
\cs_new_protected_nopar:Npn \spath_gtranslate_to:Nnnn #1#2#3#4
{
 \@@_translate_to:nnn {#2}{#3}{#4}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gtranslate_to:Nnnn {NVxx, NVVV, NVnn}
\cs_new_protected_nopar:Npn \spath_gtranslate_to:Nnn #1#2#3
{
 \spath_gtranslate_to:NVnn #1#1{#2}{#3}
}
\cs_generate_variant:Nn \spath_gtranslate_to:Nnn {NVV, cnn, cVV}
%    \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_protected_nopar:Npn \spath_translate_to:Nn #1#2
{
 \spath_translate_to:Nnn #1 #2
}
\cs_generate_variant:Nn \spath_translate_to:Nn {NV}
\cs_new_protected_nopar:Npn \spath_gtranslate_to:Nn #1#2
{
 \spath_gtranslate_to:Nnn #1 #2
}
\cs_generate_variant:Nn \spath_gtranslate_to:Nn {NV}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[internal]{
% \spath_scale:Nnnn,
% \spath_scale:Nnn,
% \spath_gscale:Nnnn,
% \spath_gscale:Nnn
% }
% Scale a path.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_scale:nnn #1#2#3
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpa_tl {#1}
 \tl_clear:N \l_@@_tmpb_tl
 \bool_until_do:nn {
   \tl_if_empty_p:N \l_@@_tmpa_tl
 }
 {
   \tl_put_right:Nx \l_@@_tmpb_tl {\tl_head:N \l_@@_tmpa_tl}
   \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

   \fp_set:Nn \l_@@_tmpa_fp {\tl_head:N \l_@@_tmpa_tl * #2}
   \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

   \fp_set:Nn \l_@@_tmpb_fp {\tl_head:N \l_@@_tmpa_tl * #3}
   \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

   \tl_put_right:Nx \l_@@_tmpb_tl
   {
     {\fp_to_dim:N \l_@@_tmpa_fp}
     {\fp_to_dim:N \l_@@_tmpb_fp}
   }
 }
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_scale:Nnnn #1#2#3#4
{
 \@@_scale:nnn {#2}{#3}{#4}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_scale:Nnnn {NVnn, Nnxx}
\cs_new_protected_nopar:Npn \spath_scale:Nnn #1#2#3
{
 \spath_scale:NVnn #1#1{#2}{#3}
}
\cs_generate_variant:Nn \spath_scale:Nnn {cnn, cVV, NVV}
\cs_new_protected_nopar:Npn \spath_gscale:Nnnn #1#2#3#4
{
 \@@_scale:nnn {#2}{#3}{#4}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gscale:Nnnn {NVnn, Nnxx}
\cs_new_protected_nopar:Npn \spath_gscale:Nnn #1#2#3
{
 \spath_gscale:NVnn #1#1{#2}{#3}
}
\cs_generate_variant:Nn \spath_gscale:Nnn {cnn, cVV, NVV}
%    \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_protected_nopar:Npn \spath_scale:Nn #1#2
{
 \spath_scale:Nnn #1 #2
}

\cs_generate_variant:Nn \spath_scale:Nn {NV}
\cs_new_protected_nopar:Npn \spath_gscale:Nn #1#2
{
 \spath_gscale:Nnn #1 #2
}

\cs_generate_variant:Nn \spath_gscale:Nn {NV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_transform:Nnnnnnnn,
% \spath_transform:Nnnnnnn,
% \spath_gtransform:Nnnnnnnn,
% \spath_gtransform:Nnnnnnn,
% }
% Applies an affine (matrix and vector) transformation to path.
% The matrix is specified in rows first.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_transform:nnnnnnn #1#2#3#4#5#6#7
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpa_tl {#1}
 \tl_clear:N \l_@@_tmpb_tl
 \bool_until_do:nn {
   \tl_if_empty_p:N \l_@@_tmpa_tl
 }
 {
   \tl_set:Nx \l_@@_tmpe_tl {\tl_head:N \l_@@_tmpa_tl}

   \tl_put_right:Nx \l_@@_tmpb_tl {\tl_head:N \l_@@_tmpa_tl}
   \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
   \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl}
   \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
   \tl_set:Nx \l_@@_tmpd_tl {\tl_head:N \l_@@_tmpa_tl}
   \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

   \tl_if_eq:NNTF \l_@@_tmpe_tl \c_spath_rectsize_tl
   {
     \fp_set:Nn \l_@@_tmpa_fp
     {\l_@@_tmpc_tl * #2 + \l_@@_tmpd_tl * #4}
     \fp_set:Nn \l_@@_tmpb_fp
     {\l_@@_tmpc_tl * #3 + \l_@@_tmpd_tl * #5}
   }
   {
     \fp_set:Nn \l_@@_tmpa_fp
     {\l_@@_tmpc_tl * #2 + \l_@@_tmpd_tl * #4 + #6}
     \fp_set:Nn \l_@@_tmpb_fp
     {\l_@@_tmpc_tl * #3 + \l_@@_tmpd_tl * #5 + #7}
   }

   \tl_put_right:Nx \l_@@_tmpb_tl
   {
     {\fp_to_dim:N \l_@@_tmpa_fp}
     {\fp_to_dim:N \l_@@_tmpb_fp}
   }
 }

 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_transform:Nnnnnnnn #1#2#3#4#5#6#7#8
{
 \@@_transform:nnnnnnn {#2}{#3}{#4}{#5}{#6}{#7}{#8}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_transform:Nnnnnnnn
{NVnnnnnn, Nnxxxxxx, cnnnnnnn}
\cs_new_protected_nopar:Npn \spath_transform:Nnnnnnn #1#2#3#4#5#6#7
{
 \spath_transform:NVnnnnnn #1#1{#2}{#3}{#4}{#5}{#6}{#7}
}
\cs_generate_variant:Nn \spath_transform:Nnnnnnn {cnnnnnn}
\cs_new_protected_nopar:Npn \spath_transform:Nnn #1#2#3
{
 \spath_transform:Nnnnnnnn #1{#2}#3
}
\cs_generate_variant:Nn \spath_transform:Nnn {cnn, cVn, NVn, NnV}
\cs_new_protected_nopar:Npn \spath_transform:Nn #1#2
{
 \spath_transform:NVnnnnnn #1#1#2
}
\cs_generate_variant:Nn \spath_transform:Nn {cn, cV, NV}

\cs_new_protected_nopar:Npn \spath_gtransform:Nnnnnnnn #1#2#3#4#5#6#7#8
{
 \@@_transform:nnnnnnn {#2}{#3}{#4}{#5}{#6}{#7}{#8}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gtransform:Nnnnnnnn {NVnnnnnn, Nnxxxxxx, cnnnnnnn}
\cs_new_protected_nopar:Npn \spath_gtransform:Nnnnnnn #1#2#3#4#5#6#7
{
 \spath_gtransform:NVnnnnnn #1#1{#2}{#3}{#4}{#5}{#6}{#7}
}
\cs_generate_variant:Nn \spath_gtransform:Nnnnnnn {cnnnnnn}
\cs_new_protected_nopar:Npn \spath_gtransform:Nnn #1#2#3
{
 \spath_gtransform:Nnnnnnnn #1{#2}#3
}
\cs_generate_variant:Nn \spath_gtransform:Nnn {cnn, cVn, NVn, NnV}
\cs_new_protected_nopar:Npn \spath_gtransform:Nn #1#2
{
 \spath_gtransform:NVnnnnnn #1#1#2
}
\cs_generate_variant:Nn \spath_gtransform:Nn {cn, cV, NV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
%  \spath_span:Nnnn,
%  \spath_span:Nnn,
%  \spath_gspan:Nnnn,
%  \spath_gspan:Nnn,
%  \spath_normalise:Nn,
%  \spath_normalise:N,
%  \spath_gnormalise:Nn,
%  \spath_gnormalise:N
% }
% The \Verb+span+ functions transform a path to start and end at specified points.
% The \Verb+normalise+ functions transform it to start at the origin and end at \((1\mathrm{pt},0\mathrm{pt})\).
%
% If the path starts and ends at the same point then it is translated to the specified point (or origin) but not otherwise changed.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_span:nnn #1#2#3
{
 \group_begin:
 \spath_initialpoint:Nn \l_@@_tmpa_tl {#1}
 \spath_finalpoint:Nn \l_@@_tmpb_tl {#1}

 \fp_set:Nn \l_@@_tmpa_fp
 {
   (\tl_item:Nn \l_@@_tmpb_tl {1}) -
   (\tl_item:Nn \l_@@_tmpa_tl {1})
 }
 \fp_set:Nn \l_@@_tmpb_fp
 {
   (\tl_item:Nn \l_@@_tmpb_tl {2}) -
   (\tl_item:Nn \l_@@_tmpa_tl {2})
 }
 \fp_set:Nn \l_@@_tmpc_fp
 {
   (\l_@@_tmpa_fp) * (\l_@@_tmpa_fp)
   +
   (\l_@@_tmpb_fp * \l_@@_tmpb_fp)
 }

 \fp_compare:nTF
 {
   \l_@@_tmpc_fp < 0.001
 }
 {
   \spath_translate_to:Nnnn \l_@@_tmpd_tl {#1} #2
 }
 {
   \fp_set:Nn \l_@@_tmpa_fp
   {
     (
     ((\tl_item:nn {#3} {1})
     -
     (\tl_item:nn {#2} {1}))
     *
     ((\tl_item:Nn \l_@@_tmpb_tl {1})
     -
     (\tl_item:Nn \l_@@_tmpa_tl {1}))
     +
     ((\tl_item:nn {#3} {2})
     -
     (\tl_item:nn {#2} {2}))
     *
     ((\tl_item:Nn \l_@@_tmpb_tl {2})
     -
     (\tl_item:Nn \l_@@_tmpa_tl {2}))
     )
     /
     \l_@@_tmpc_fp
   }
   \fp_set:Nn \l_@@_tmpb_fp
   {
     (
     ((\tl_item:nn {#3} {2})
     -
     (\tl_item:nn {#2} {2}))
     *
     ((\tl_item:Nn \l_@@_tmpb_tl {1})
     -
     (\tl_item:Nn \l_@@_tmpa_tl {1}))
     -
     ((\tl_item:nn {#3} {1})
     -
     (\tl_item:nn {#2} {1}))
     *
     ((\tl_item:Nn \l_@@_tmpb_tl {2})
     -
     (\tl_item:Nn \l_@@_tmpa_tl {2}))
     )
     /
     \l_@@_tmpc_fp
   }

   \tl_set:Nx \l_@@_tmpc_tl
   {
     {
       \fp_to_decimal:N \l_@@_tmpa_fp
     }
     {
       \fp_to_decimal:N \l_@@_tmpb_fp
     }
     {
       \fp_eval:n { - \l_@@_tmpb_fp }
     }
     {
       \fp_to_decimal:N \l_@@_tmpa_fp
     }
     {
       \fp_to_dim:n
       {
         \tl_item:nn {#2} {1}
         -
         \l_@@_tmpa_fp * (\tl_item:Nn \l_@@_tmpa_tl {1})
         +
         \l_@@_tmpb_fp * (\tl_item:Nn \l_@@_tmpa_tl {2})
       }
     }
     {
       \fp_to_dim:n
       {
         \tl_item:nn {#2} {2}
         -
         \l_@@_tmpb_fp * (\tl_item:Nn \l_@@_tmpa_tl {1})
         -
         \l_@@_tmpa_fp * (\tl_item:Nn \l_@@_tmpa_tl {2})
       }
     }
   }
   \spath_transform:NnV \l_@@_tmpd_tl {#1} \l_@@_tmpc_tl
 }
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpd_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_span:Nnnn #1#2#3#4
{
 \@@_span:nnn {#2}{#3}{#4}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_span:Nnnn {NVnn, NVVV, NnVV}
\cs_new_protected_nopar:Npn \spath_span:Nnn #1#2#3
{
 \spath_span:NVnn #1#1{#2}{#3}
}
\cs_generate_variant:Nn \spath_span:Nnn {NVV, cnn, cvv, cVV}
\cs_new_protected_nopar:Npn \spath_gspan:Nnnn #1#2#3#4
{
 \@@_span:nnn {#2}{#3}{#4}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gspan:Nnnn {NVnn, NVVV}
\cs_new_protected_nopar:Npn \spath_gspan:Nnn #1#2#3
{
 \spath_gspan:NVnn #1#1{#2}{#3}
}
\cs_generate_variant:Nn \spath_gspan:Nnn {NVV, cnn, cvv, cVV}
\cs_new_protected_nopar:Npn \@@_normalise:n #1
{
 \@@_span:nnn {#1}{{0pt}{0pt}}{{1pt}{0pt}}
}
\cs_new_protected_nopar:Npn \spath_normalise:Nn #1#2
{
 \@@_normalise:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_normalise:Nn {cn,NV, cV, cv}
\cs_new_protected_nopar:Npn \spath_normalise:N #1
{
 \spath_normalise:NV #1#1
}
\cs_generate_variant:Nn \spath_normalise:N {c}
\cs_new_protected_nopar:Npn \spath_gnormalise:Nn #1#2
{
 \@@_normalise:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gnormalise:Nn {cn,NV, cV, cv}
\cs_new_protected_nopar:Npn \spath_gnormalise:N #1
{
 \spath_gnormalise:NV #1#1
}
\cs_generate_variant:Nn \spath_gnormalise:N {c}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[internal]{
% \spath_splice_between:Nnnn,
% \spath_splice_between:Nnn,
% \spath_gsplice_between:Nnnn,
% \spath_gsplice_between:Nnn
% }
% This takes three paths and returns a single path in which the middle one is adjusted (and welded) so that it joins the first path to the third.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_splice_between:nnn #1#2#3
{
 \group_begin:
 \spath_finalpoint:Nn \l_@@_tmpd_tl {#1}
 \spath_initialpoint:Nn \l_@@_tmpe_tl {#3}
 \spath_span:NnVV \l_@@_tmpb_tl {#2} \l_@@_tmpd_tl \l_@@_tmpe_tl
 \spath_append_no_move:NnV \l_@@_tmpa_tl {#1} \l_@@_tmpb_tl
 \spath_append_no_move:Nn \l_@@_tmpa_tl {#3}
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_splice_between:Nnnn #1#2#3#4
{
 \@@_splice_between:nnn {#2}{#3}{#4}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_splice_between:Nnnn {NVnn, NVVV}
\cs_new_protected_nopar:Npn \spath_splice_between:Nnn #1#2#3
{
 \spath_splice_between:NVnn #1#1{#2}{#3}
}
\cs_generate_variant:Nn \spath_splice_between:Nnn {NVV, cnn, cvv, Nvn, NVn}
\cs_new_protected_nopar:Npn \spath_gsplice_between:Nnnn #1#2#3#4
{
 \@@_splice_between:nnn {#2}{#3}{#4}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gsplice_between:Nnnn {NVnn, NVVV}
\cs_new_protected_nopar:Npn \spath_gsplice_between:Nnn #1#2#3
{
 \spath_gsplice_between:NVnn #1#1{#2}{#3}
}
\cs_generate_variant:Nn \spath_gsplice_between:Nnn {NVV, cnn, cvv, Nvn, NVn}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_hobby_curve:Nnnnn
% }
% Construct the curve from Hobby's algorithm given the start, end, and tangent directions.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_hobby_curve:nnnn #1#2#3#4
{
 \group_begin:
%    \end{macrocode}
% First tangent vector projected onto vector between endpoints
%
% Something a bit weird here as the components are opposite to how I thought they should be ...
%    \begin{macrocode}
 \fp_set:Nn \l_@@_tmpa_fp
 {
   (
   (\tl_item:nn {#2} {1})
   *
   (\tl_item:nn {#4} {1} - \tl_item:nn {#1} {1})
   +
   (\tl_item:nn {#2} {2})
   *
   (\tl_item:nn {#4} {2} - \tl_item:nn {#1} {2})
   )
   /
   sqrt
   (
   (
   (\tl_item:nn {#2} {1})^2
   +
   (\tl_item:nn {#2} {2})^2
   )
   *
   (
   (\tl_item:nn {#4} {1} - \tl_item:nn {#1} {1})^2
   +
   (\tl_item:nn {#4} {2} - \tl_item:nn {#1} {2})^2
   )
   )
 }
 \fp_set:Nn \l_@@_tmpb_fp
 {
   (
   -
   (\tl_item:nn {#2} {1})
   *
   (\tl_item:nn {#4} {2} - \tl_item:nn {#1} {2})
   +
   (\tl_item:nn {#2} {2})
   *
   (\tl_item:nn {#4} {1} - \tl_item:nn {#1} {1})
   )
   /
   sqrt
   (
   (
   (\tl_item:nn {#2} {1})^2
   +
   (\tl_item:nn {#2} {2})^2
   )
   *
   (
   (\tl_item:nn {#4} {1} - \tl_item:nn {#1} {1})^2
   +
   (\tl_item:nn {#4} {2} - \tl_item:nn {#1} {2})^2
   )
   )
 }
%    \end{macrocode}
% Second tangent vector projected onto vector between endpoints
%    \begin{macrocode}
 \fp_set:Nn \l_@@_tmpc_fp
 {
   (
   (\tl_item:nn {#3} {1})
   *
   (\tl_item:nn {#4} {1} - \tl_item:nn {#1} {1})
   +
   (\tl_item:nn {#3} {2})
   *
   (\tl_item:nn {#4} {2} - \tl_item:nn {#1} {2})
   )
   /
   sqrt
   (
   (
   (\tl_item:nn {#3} {1})^2
   +
   (\tl_item:nn {#3} {2})^2
   )
   *
   (
   (\tl_item:nn {#4} {1} - \tl_item:nn {#1} {1})^2
   +
   (\tl_item:nn {#4} {2} - \tl_item:nn {#1} {2})^2
   )
   )
 }
 \fp_set:Nn \l_@@_tmpd_fp
 {
   (
   (\tl_item:nn {#3} {1})
   *
   (\tl_item:nn {#4} {2} - \tl_item:nn {#1} {2})
   -
   (\tl_item:nn {#3} {2})
   *
   (\tl_item:nn {#4} {1} - \tl_item:nn {#1} {1})
   )
   /
   sqrt
   (
   (
   (\tl_item:nn {#3} {1})^2
   +
   (\tl_item:nn {#3} {2})^2
   )
   *
   (
   (\tl_item:nn {#4} {1} - \tl_item:nn {#1} {1})^2
   +
   (\tl_item:nn {#4} {2} - \tl_item:nn {#1} {2})^2
   )
   )
 }

 \fp_set:Nn \l_@@_tmpe_fp
 {
   (
   2 + sqrt(2) *
   (\l_@@_tmpb_fp - 1/16 * \l_@@_tmpd_fp)
   *
   (\l_@@_tmpd_fp - 1/16 * \l_@@_tmpb_fp)
   *
   (\l_@@_tmpa_fp - \l_@@_tmpc_fp)
   )
   /
   (
   1
   +
   (1 - (3 - sqrt(5))/2)
   *
   \l_@@_tmpa_fp
   +
   (3 - sqrt(5))/2
   *
   \l_@@_tmpc_fp
   )
   *
   sqrt
   (
   (
   (\tl_item:nn {#4} {1} - \tl_item:nn {#1} {1})^2
   +
   (\tl_item:nn {#4} {2} - \tl_item:nn {#1} {2})^2
   )
   /
   (
   (\tl_item:nn {#2} {1})^2
   +
   (\tl_item:nn {#2} {2})^2
   )
   )
   /3
 }
 \fp_set:Nn \l_@@_tmpf_fp
 {
   (
   2 - sqrt(2) *
   (\l_@@_tmpb_fp - 1/16 * \l_@@_tmpd_fp)
   *
   (\l_@@_tmpd_fp - 1/16 * \l_@@_tmpb_fp)
   *
   (\l_@@_tmpa_fp - \l_@@_tmpc_fp)
   )
   /
   (
   1
   +
   (1 - (3 - sqrt(5))/2)
   *
   \l_@@_tmpc_fp
   +
   (3 - sqrt(5))/2
   *
   \l_@@_tmpa_fp
   )
   *
   sqrt
   (
   (
   (\tl_item:nn {#4} {1} - \tl_item:nn {#1} {1})^2
   +
   (\tl_item:nn {#4} {2} - \tl_item:nn {#1} {2})^2
   )
   /
   (
   (\tl_item:nn {#3} {1})^2
   +
   (\tl_item:nn {#3} {2})^2
   )
   )
   /3
 }

 \tl_set:Nx \l_@@_tmpa_tl
 {
   {
     \fp_to_dim:n
     {
       \tl_item:nn {#1} {1}
       +
       \l_@@_tmpe_fp
       *
       (\tl_item:nn {#2} {1})
     }
   }
   {
     \fp_to_dim:n
     {
       \tl_item:nn {#1} {2}
       +
       \l_@@_tmpe_fp
       *
       (\tl_item:nn {#2} {2})
     }
   }
 }
 \tl_set:Nx \l_@@_tmpb_tl
 {
   {
     \fp_to_dim:n
     {
       \tl_item:nn {#4} {1}
       -
       \l_@@_tmpf_fp
       *
       (\tl_item:nn {#3} {1})
     }
   }
   {
     \fp_to_dim:n
     {
       \tl_item:nn {#4} {2}
       -
       \l_@@_tmpf_fp
       *
       (\tl_item:nn {#3} {2})
     }
   }
 }

 \tl_clear:N \l_@@_tmpc_tl
 \tl_set:NV \l_@@_tmpc_tl \c_spath_moveto_tl
 \tl_put_right:Nn \l_@@_tmpc_tl {#1}
 \tl_put_right:NV \l_@@_tmpc_tl \c_spath_curvetoa_tl
 \tl_put_right:NV \l_@@_tmpc_tl \l_@@_tmpa_tl
 \tl_put_right:NV \l_@@_tmpc_tl \c_spath_curvetob_tl
 \tl_put_right:NV \l_@@_tmpc_tl \l_@@_tmpb_tl
 \tl_put_right:NV \l_@@_tmpc_tl \c_spath_curveto_tl
 \tl_put_right:Nn \l_@@_tmpc_tl {#4}
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpc_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_hobby_curve:Nnnnn #1#2#3#4#5
{
 \@@_hobby_curve:nnnn {#2}{#3}{#4}{#5}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_hobby_curve:Nnnnn {NVVVV}
\cs_new_protected_nopar:Npn \spath_ghobby_curve:Nnnnn #1#2#3#4#5
{
 \@@_hobby_curve:nnnn {#2}{#3}{#4}{#5}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_ghobby_curve:Nnnnn {NVVVV}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[internal]{
% \spath_curve_between:Nnn,
% \spath_curve_between:Nn,
% \spath_gcurve_between:Nnn,
% \spath_gcurve_between:Nn
% }
% This takes two paths and returns a single path formed by joining the two paths by a curve.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_curve_between:nn #1#2
{
 \group_begin:
 \spath_finalpoint:Nn \l_@@_tmpa_tl {#1}
 \spath_finaltangent:Nn \l_@@_tmpb_tl {#1}
 \spath_initialpoint:Nn \l_@@_tmpc_tl {#2}
 \spath_initialtangent:Nn \l_@@_tmpd_tl {#2}

 \spath_hobby_curve:NVVVV \l_@@_tmpe_tl
 \l_@@_tmpa_tl \l_@@_tmpb_tl \l_@@_tmpd_tl \l_@@_tmpc_tl

 \tl_set:Nn \l_@@_tmpa_tl {#1}
 \spath_append_no_move:NV \l_@@_tmpa_tl \l_@@_tmpe_tl
 \spath_append_no_move:Nn \l_@@_tmpa_tl {#2}
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_curve_between:Nnn #1#2#3
{
 \@@_curve_between:nn {#2}{#3}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_curve_between:Nnn {NVn, NVV}
\cs_new_protected_nopar:Npn \spath_curve_between:Nn #1#2
{
 \spath_curve_between:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_curve_between:Nn {NV, cn, cv}
\cs_new_protected_nopar:Npn \spath_gcurve_between:Nnn #1#2#3
{
 \@@_curve_between:nn {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gcurve_between:Nnn {NVn, NVV}
\cs_new_protected_nopar:Npn \spath_gcurve_between:Nn #1#2
{
 \spath_gcurve_between:NVnn #1#1{#2}
}
\cs_generate_variant:Nn \spath_gcurve_between:Nn {NV, cn, cv}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_close_with:Nn,
% \spath_gclose_with:Nn
% }
% Closes the first path by splicing in the second.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_close_with:nn #1#2
{
 \group_begin:
 \spath_finalmovepoint:Nn \l_@@_tmpa_tl {#1}
 \spath_finalpoint:Nn \l_@@_tmpb_tl {#1}
 \dim_compare:nTF
 {
   \dim_abs:n
   {
     \tl_item:Nn \l_@@_tmpa_tl {1}
     -
     \tl_item:Nn \l_@@_tmpb_tl {1}
   }
   +
   \dim_abs:n
   {
     \tl_item:Nn \l_@@_tmpa_tl {2}
     -
     \tl_item:Nn \l_@@_tmpb_tl {2}
   }
   < 0.01pt
 }
 {
   \@@_close:n {#1}
 }
 {
   \spath_span:NnVV \l_@@_tmpc_tl {#2} \l_@@_tmpb_tl \l_@@_tmpa_tl
   \spath_append_no_move:NnV \l_@@_tmpd_tl {#1} \l_@@_tmpc_tl
   \@@_close:V \l_@@_tmpd_tl
 }
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_close_with:Nnn #1#2#3
{
 \@@_close_with:nn {#2}{#3}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_close_with:Nnn {cnn, cVV, cvv, NVn}
\cs_new_protected_nopar:Npn \spath_close_with:Nn #1#2
{
 \spath_close_with:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_close_with:Nn {cn, cV, cv, NV}
\cs_new_protected_nopar:Npn \spath_gclose_with:Nnn #1#2#3
{
 \@@_close_with:nn {#2}{#3}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gclose_with:Nnn {cnn, cVV, cvv, NVn}
\cs_new_protected_nopar:Npn \spath_gclose_with:Nn #1#2
{
 \spath_gclose_with:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_gclose_with:Nn {cn, cV, cv, NV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_close_with_curve:N,
% \spath_gclose_with_curve:N
% }
% Closes the path with a curve.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_close_with_curve:n #1
{
 \group_begin:
 \spath_finalpoint:Nn \l_@@_tmpa_tl {#1}
 \spath_finaltangent:Nn \l_@@_tmpb_tl {#1}
 \spath_finalmovepoint:Nn \l_@@_tmpc_tl {#1}
 \spath_finalmovetangent:Nn \l_@@_tmpd_tl {#1}
 \dim_compare:nTF
 {
   \dim_abs:n
   {
     \tl_item:Nn \l_@@_tmpa_tl {1}
     -
     \tl_item:Nn \l_@@_tmpc_tl {1}
   }
   +
   \dim_abs:n
   {
     \tl_item:Nn \l_@@_tmpa_tl {2}
     -
     \tl_item:Nn \l_@@_tmpc_tl {2}
   }
   < 0.01pt
 }
 {
   \@@_close:n {#1}
 }
 {

   \spath_hobby_curve:NVVVV \l_@@_tmpe_tl
   \l_@@_tmpa_tl \l_@@_tmpb_tl \l_@@_tmpd_tl \l_@@_tmpc_tl

   \tl_set:Nn \l_@@_tmpa_tl {#1}
   \spath_append_no_move:NV \l_@@_tmpa_tl \l_@@_tmpe_tl
   \@@_close:V \l_@@_tmpa_tl
 }
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_close_with_curve:Nn #1#2
{
 \@@_close_with_curve:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_close_with_curve:Nn {cn, cV, cv, NV}
\cs_new_protected_nopar:Npn \spath_close_with_curve:N #1
{
 \spath_close_with_curve:NV #1#1
}
\cs_generate_variant:Nn \spath_close_with_curve:N {c}
\cs_new_protected_nopar:Npn \spath_gclose_with_curve:Nn #1#2
{
 \@@_close_with_curve:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gclose_with_curve:Nn {cn, cV, cv, NV}
\cs_new_protected_nopar:Npn \spath_gclose_with_curve:N #1
{
 \spath_gclose_with_curve:NV #1#1
}
\cs_generate_variant:Nn \spath_gclose_with_curve:N {c}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_weld:Nnn,
% \spath_weld:Nn,
% \spath_gweld:Nnn,
% \spath_gweld:Nn
% }
% This welds one path to another, moving the second so that its 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.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_weld:nn #1#2
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpa_tl {#1}
 \tl_set:Nn \l_@@_tmpb_tl {#2}
 \spath_finalpoint:Nn \l_@@_tmpc_tl {#1}
 \spath_translate_to:NV \l_@@_tmpb_tl \l_@@_tmpc_tl

 \@@_append_no_move:VV \l_@@_tmpa_tl \l_@@_tmpb_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_weld:Nnn #1#2#3
{
 \@@_weld:nn {#2}{#3}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_weld:Nnn {NVV,NVn}
\cs_new_protected_nopar:Npn \spath_weld:Nn #1#2
{
 \spath_weld:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_weld:Nn {NV, Nv, cV, cv}
\cs_new_protected_nopar:Npn \spath_gweld:Nnn #1#2#3
{
 \@@_weld:nn {#2}{#3}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gweld:Nnn {NVV, NVn}
\cs_new_protected_nopar:Npn \spath_gweld:Nn #1#2
{
 \spath_gweld:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_gweld:Nn {NV, Nv, cV, cv}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[internal]{
% \spath_append_no_move:Nnn,
% \spath_append_no_move:Nn,
% \spath_gappend_no_move:Nnn,
% \spath_gappend_no_move:Nn,
% }
% Append the path from the second \Verb+spath+ to the first, removing
% the adjoining move if neither path has a rectangle either side of the join or if the first path isn't closed.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_append_no_move:nn #1#2
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpa_tl {#1}
 \tl_set:Nn \l_@@_tmpb_tl {#2}
 \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpb_tl}
 \spath_finalaction:Nn \l_@@_tmpd_tl {#1}
 \bool_if:nT {
   ! \tl_if_eq_p:NN \l_@@_tmpd_tl \c_spath_closepath_tl
   &&
   ! \tl_if_eq_p:NN \l_@@_tmpd_tl \c_spath_rectcorner_tl
   &&
   \tl_if_eq_p:NN \l_@@_tmpc_tl \c_spath_moveto_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_@@_tmpa_tl \l_@@_tmpb_tl
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl
 \group_end:
}
\cs_generate_variant:Nn \@@_append_no_move:nn {VV}
\cs_new_protected_nopar:Npn \spath_append_no_move:Nnn #1#2#3
{
 \@@_append_no_move:nn {#2}{#3}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_append_no_move:Nnn {NVV, NVn, NnV}
\cs_new_protected_nopar:Npn \spath_append_no_move:Nn #1#2
{
 \spath_append_no_move:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_append_no_move:Nn {NV, cv, Nv, cV}
\cs_new_protected_nopar:Npn \spath_gappend_no_move:Nnn #1#2#3
{
 \@@_append_no_move:nn {#2}{#3}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gappend_no_move:Nnn {NVV, NVn}
\cs_new_protected_nopar:Npn \spath_gappend_no_move:Nn #1#2
{
 \spath_gappend_no_move:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_gappend_no_move:Nn {NV, cv, Nv, cV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_append:Nnn,
% \spath_append:Nn,
% \spath_gappend:Nnn,
% \spath_gappend:Nn,
% }
% Prepend the path from the second \Verb+spath+ to the first.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \spath_append:Nnn #1#2#3
{
 \tl_set:Nn #1 {#2}
 \tl_put_right:Nn #1 {#3}
}
\cs_generate_variant:Nn \spath_append:Nnn {NVV, NVn}
\cs_new_protected_nopar:Npn \spath_append:Nn #1#2
{
 \spath_append:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_append:Nn {NV, Nv, cv, cV}
\cs_new_protected_nopar:Npn \spath_gappend:Nnn #1#2#3
{
 \tl_gset:Nn #1 {#2}
 \tl_gput_right:Nn #1 {#3}
}
\cs_generate_variant:Nn \spath_gappend:Nnn {NVV, NVn}
\cs_new_protected_nopar:Npn \spath_gappend:Nn #1#2
{
 \spath_gappend:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_gappend:Nn {NV, Nv, cv, cV}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[internal]{
% \spath_prepend_no_move:Nnn,
% \spath_prepend_no_move:Nn,
% \spath_gprepend_no_move:Nnn,
% \spath_gprepend_no_move:Nn,
% }
% Prepend the path from the second \Verb+spath+ to the first, removing
% the adjoining move.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \spath_prepend_no_move:Nnn #1#2#3
{
 \spath_append_no_move:Nnn #1{#3}{#2}
}
\cs_generate_variant:Nn \spath_prepend_no_move:Nnn {NVV, NVn}
\cs_new_protected_nopar:Npn \spath_prepend_no_move:Nn #1#2
{
 \spath_prepend_no_move:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_prepend_no_move:Nn {NV, cv}
\cs_new_protected_nopar:Npn \spath_gprepend_no_move:Nnn #1#2#3
{
 \spath_gappend_no_move:Nnn #1{#3}{#2}
}
\cs_generate_variant:Nn \spath_gprepend_no_move:Nnn {NVV, NVn}
\cs_new_protected_nopar:Npn \spath_gprepend_no_move:Nn #1#2
{
 \spath_gprepend_no_move:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_gprepend_no_move:Nn {NV, cv}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_prepend:Nnn,
% \spath_prepend:Nn,
% \spath_gprepend:Nnn,
% \spath_gprepend:Nn
% }
% Prepend the path from the second \Verb+spath+ to the first.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \spath_prepend:Nnn #1#2#3
{
 \spath_append:Nnn #1{#3}{#2}
}
\cs_generate_variant:Nn \spath_prepend:Nnn {NVV, NVn}
\cs_new_protected_nopar:Npn \spath_prepend:Nn #1#2
{
 \spath_prepend:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_prepend:Nn {NV}
\cs_new_protected_nopar:Npn \spath_gprepend:Nnn #1#2#3
{
 \spath_gappend:Nnn #1{#3}{#2}
}
\cs_generate_variant:Nn \spath_gprepend:Nnn {NVV, NVn}
\cs_new_protected_nopar:Npn \spath_gprepend:Nn #1#2
{
 \spath_gprepend:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_gprepend:Nn {NV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_bake_round:Nn,
% \spath_bake_round:N,
% \spath_gbake_round:Nn,
% \spath_gbake_round:N
% }
%
% The corner rounding routine is applied quite late in the process of building a soft path so this ensures that it is done.
%
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_bake_round:n #1
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpa_tl {#1}
 \pgf@@@@processround \l_@@_tmpa_tl\l_@@_tmpb_tl
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_bake_round:Nn #1#2
{
 \@@_bake_round:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_bake_round:Nn {NV}
\cs_new_protected_nopar:Npn \spath_bake_round:N #1
{
 \spath_bake_round:NV #1#1
}
\cs_generate_variant:Nn \spath_bake_round:N {c}
\cs_new_protected_nopar:Npn \spath_gbake_round:Nn #1#2
{
 \@@_bake_round:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gbake_round:Nn {NV}
\cs_new_protected_nopar:Npn \spath_gbake_round:N #1
{
 \spath_gbake_round:NV #1#1
}
\cs_generate_variant:Nn \spath_gbake_round:N {c}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_bake_shorten:Nn,
% \spath_bake_shorten:N,
% \spath_gbake_shorten:Nn,
% \spath_gbake_shorten:N
% }
%
% The shortening routine is applied quite late in the process of building a soft path so this ensures that it is done.
%
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_bake_shorten:n #1
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpa_tl {#1}
 \pgfsyssoftpath@getcurrentpath\l_@@_tmpb_tl
 \pgfsyssoftpath@setcurrentpath\l_@@_tmpa_tl
 \pgf@prepare@end@of@path
 \pgf@prepare@start@of@path
 \pgfsyssoftpath@getcurrentpath\l_@@_tmpa_tl
 \pgfsyssoftpath@setcurrentpath\l_@@_tmpb_tl
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_bake_shorten:Nn #1#2
{
 \@@_bake_shorten:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_bake_shorten:Nn {NV}
\cs_new_protected_nopar:Npn \spath_bake_shorten:N #1
{
 \spath_bake_shorten:NV #1#1
}
\cs_generate_variant:Nn \spath_bake_shorten:N {c}
\cs_new_protected_nopar:Npn \spath_gbake_shorten:Nn #1#2
{
 \@@_bake_shorten:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gbake_shorten:Nn {NV}
\cs_new_protected_nopar:Npn \spath_gbake_shorten:N #1
{
 \spath_gbake_shorten:NV #1#1
}
\cs_generate_variant:Nn \spath_gbake_shorten:N {c}
%    \end{macrocode}
%
% Shortening the path when it is baked can cause issues with arrows.
% Putting an arrow in a path definition affects the path because the path gets shortened so that the arrow ends where the path was meant to end.
% So an arrow affects the path definition, but the arrow is not itself part of the path so if an arrow is used when the path is defined and again when the path is used, the path will be shortened twice which might not be what is intended.
% Therefore it is useful to have a way to disable the shortening and place an arrow tip at the actual end of the line.
% The following code achieves that.
%
% Save the original command that computes the arrow shortening.
%    \begin{macrocode}
\cs_set_eq:Nc \@@_pgf_arrow_compute_shortening:n {pgf@arrow@compute@shortening}
%    \end{macrocode}
%
% After \verb+\pgf@arrow@compute@shortening+ then \verb+\pgf@xa+ is the amount to shorten the line by, so we will be setting that to 0pt.
% Then \verb+\pgf@xb+ is the length of the arrow head which is used to position the arrow and so before zeroing \verb+\pgf@xa+ we subtract it from \verb+\pgf@xb+ so that the arrow is placed so that its back point is at the current position.
%
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_arrow_compute_shortening:n #1
{
 \@@_pgf_arrow_compute_shortening:n {#1}
 \bool_if:NF \l_spath_arrow_shortening_bool
 {
   \dim_sub:cn {pgf@xb} {\dim_use:c {pgf@xa}}
   \dim_zero:c {pgf@xa}
 }
}

\cs_set_eq:cN {pgf@arrow@compute@shortening} \@@_arrow_compute_shortening:n
%    \end{macrocode}
%
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_close:Nn,
% \spath_close:N,
% \spath_gclose:Nn,
% \spath_gclose:N
% }
% Appends a close path to the end of the path.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_close:n #1
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpa_tl {#1}
 \spath_finalmovepoint:NV \l_@@_tmpb_tl \l_@@_tmpa_tl
 \tl_put_right:NV \l_@@_tmpa_tl \c_spath_closepath_tl
 \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpb_tl
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl
 \group_end:
}
\cs_generate_variant:Nn \@@_close:n {V}
\cs_new_protected_nopar:Npn \spath_close:Nn #1#2
{
 \@@_close:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_close:Nn {NV}
\cs_new_protected_nopar:Npn \spath_close:N #1
{
 \spath_close:NV #1#1
}
\cs_generate_variant:Nn \spath_close:N {c}
\cs_new_protected_nopar:Npn \spath_gclose:Nn #1#2
{
 \@@_close:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gclose:Nn {NV}
\cs_new_protected_nopar:Npn \spath_gclose:N #1
{
 \spath_gclose:NV #1#1
}
\cs_generate_variant:Nn \spath_gclose:N {c}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_adjust_close:Nn,
% \spath_adjust_close:N,
% \spath_adjust_gclose:Nn,
% \spath_adjust_gclose:N
% }
% This closes a path and adjusts the end point to be where the final move point (so where the close points to) is.
% The intention is that this should be used if the two points are visually the same point but mathematically different.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_adjust_close:n #1
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpa_tl {#1}
 \spath_finalmovepoint:NV \l_@@_tmpb_tl \l_@@_tmpa_tl
 \spath_finalpoint:NV \l_@@_tmpc_tl \l_@@_tmpa_tl
 \tl_reverse: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_@@_tmpd_tl {\tl_head:N \l_@@_tmpa_tl}
 \tl_if_eq:NNT \l_@@_tmpd_tl \c_spath_curveto_tl
 {
   \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
   \tl_clear:N \l_@@_tmpe_tl
   \tl_set:Nx \l_@@_tmpe_tl {
     {
       \dim_eval:n
       {
         \tl_item:Nn \l_@@_tmpa_tl {1}
         -
         \tl_item:Nn \l_@@_tmpc_tl {2}
         +
         \tl_item:Nn \l_@@_tmpb_tl {2}
       }
     }
     {
       \dim_eval:n
       {
         \tl_item:Nn \l_@@_tmpa_tl {2}
         -
         \tl_item:Nn \l_@@_tmpc_tl {1}
         +
         \tl_item:Nn \l_@@_tmpb_tl {1}
       }
     }
   }
   \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_left:NV \l_@@_tmpa_tl \l_@@_tmpe_tl
   \tl_put_left:NV \l_@@_tmpa_tl \l_@@_tmpd_tl
 }
 \tl_reverse:N \l_@@_tmpa_tl
 \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpb_tl
 \tl_put_right:NV \l_@@_tmpa_tl \c_spath_closepath_tl
 \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpb_tl
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl
 \group_end:
}
\cs_generate_variant:Nn \@@_adjust_close:n {V}
\cs_new_protected_nopar:Npn \spath_adjust_close:Nn #1#2
{
 \@@_adjust_close:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_adjust_close:Nn {NV}
\cs_new_protected_nopar:Npn \spath_adjust_close:N #1
{
 \spath_adjust_close:NV #1#1
}
\cs_generate_variant:Nn \spath_adjust_close:N {c}
\cs_new_protected_nopar:Npn \spath_adjust_gclose:Nn #1#2
{
 \@@_adjust_close:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_adjust_gclose:Nn {NV}
\cs_new_protected_nopar:Npn \spath_adjust_gclose:N #1
{
 \spath_adjust_gclose:NV #1#1
}
\cs_generate_variant:Nn \spath_adjust_gclose:N {c}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[internal]{
% \spath_open:Nn,
% \spath_open:N,
% \spath_gopen:Nn,
% \spath_gopen:N
% }
% Removes all close paths from the path, replacing them by \Verb+lineto+ if they move any distance.
% Rectangles are replaced by lines with the start/end at the lower left corner.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_open:n #1
{
 \group_begin:
 \spath_replace_rectangles:Nn \l_@@_tmpa_tl {#1}
 \tl_clear:N \l_@@_tmpb_tl
 \bool_until_do:nn {
   \tl_if_empty_p:N \l_@@_tmpa_tl
 }
 {
   \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl}
   \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

   \token_case_meaning:NnF \l_@@_tmpc_tl
   {
     \c_spath_closepath_tl {

       \bool_if:nF
       {
         \dim_compare_p:n
         {
           \l_@@_move_x_dim == \l_@@_tmpa_dim
         }
         &&
         \dim_compare_p:n
         {
           \l_@@_move_y_dim == \l_@@_tmpb_dim
         }
       }
       {
         \tl_put_right:NV \l_@@_tmpb_tl \c_spath_lineto_tl

         \tl_put_right:Nx \l_@@_tmpb_tl {
           { \dim_use:N \l_@@_move_x_dim }
           { \dim_use:N \l_@@_move_y_dim }
         }
       }

       \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl}
       \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
       \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl}
       \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
     }

     \c_spath_moveto_tl {
       \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpc_tl

       \dim_set:Nn \l_@@_move_x_dim {\tl_head:N \l_@@_tmpa_tl}
       \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
       \dim_set:Nn \l_@@_move_y_dim {\tl_head:N \l_@@_tmpa_tl}
       \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

       \tl_put_right:Nx \l_@@_tmpb_tl {
         { \dim_use:N \l_@@_move_x_dim }
         { \dim_use:N \l_@@_move_y_dim }
       }

       \dim_set_eq:NN \l_@@_tmpa_dim \l_@@_move_x_dim
       \dim_set_eq:NN \l_@@_tmpb_dim \l_@@_move_y_dim
     }
   }
   {
     \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpc_tl

     \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl}
     \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
     \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl}
     \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

     \tl_put_right:Nx \l_@@_tmpb_tl {
       { \dim_use:N \l_@@_tmpa_dim }
       { \dim_use:N \l_@@_tmpb_dim }
     }
   }
 }
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl
 \group_end:
}
\cs_generate_variant:Nn \@@_open:n {V}
\cs_new_protected_nopar:Npn \spath_open:Nn #1#2
{
 \@@_open:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_open:Nn {NV}
\cs_new_protected_nopar:Npn \spath_open:N #1
{
 \spath_open:NV #1#1
}
\cs_new_protected_nopar:Npn \spath_gopen:Nn #1#2
{
 \@@_open:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gopen:Nn {NV}
\cs_new_protected_nopar:Npn \spath_gopen:N #1
{
 \spath_gopen:NV #1#1
}
\cs_generate_variant:Nn \spath_open:N {c}
\cs_generate_variant:Nn \spath_gopen:N {c}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_replace_lines:Nn,
% \spath_replace_lines:Nn,
% \spath_replace_lines:Nn,
% \spath_replace_lines:Nn,
% }
% Replace any line segments by B\'ezier curves.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_replace_lines:n #1
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpa_tl {#1}
 \tl_clear:N \l_@@_tmpb_tl
 \dim_set:Nn \l_@@_tmpa_dim {0pt}
 \dim_set:Nn \l_@@_tmpb_dim {0pt}

 \bool_do_until:nn
 {
   \tl_if_empty_p:N \l_@@_tmpa_tl
 }
 {
   \tl_set:Nx \l_@@_tmpc_tl {\tl_item:Nn \l_@@_tmpa_tl {1}}
   \tl_set:Nx \l_@@_tmpd_tl {\tl_item:Nn \l_@@_tmpa_tl {2}}
   \tl_set:Nx \l_@@_tmpe_tl {\tl_item:Nn \l_@@_tmpa_tl {3}}

   \tl_if_eq:NNTF \l_@@_tmpc_tl \c_spath_lineto_tl
   {
     \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curvetoa_tl
     \tl_put_right:Nx \l_@@_tmpb_tl
     {
       {
         \fp_to_dim:n
         {
           2/3 * (\l_@@_tmpa_dim)
           +
           1/3 * (\l_@@_tmpd_tl)
         }
       }
     }
     \tl_put_right:Nx \l_@@_tmpb_tl
     {
       {
         \fp_to_dim:n
         {
           2/3 * (\l_@@_tmpb_dim)
           +
           1/3 * (\l_@@_tmpe_tl)
         }
       }
     }
     \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curvetob_tl
     \tl_put_right:Nx \l_@@_tmpb_tl
     {
       {
         \fp_to_dim:n
         {
           1/3 * (\l_@@_tmpa_dim)
           +
           2/3 * (\l_@@_tmpd_tl)
         }
       }
     }
     \tl_put_right:Nx \l_@@_tmpb_tl
     {
       {
         \fp_to_dim:n
         {
           1/3 * (\l_@@_tmpb_dim)
           +
           2/3 * (\l_@@_tmpe_tl)
         }
       }
     }
     \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curveto_tl
     \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpd_tl
     \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpe_tl
   }
   {
     \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpc_tl
     \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpd_tl
     \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpe_tl
   }

   \dim_set:Nn \l_@@_tmpa_dim {\l_@@_tmpd_tl}
   \dim_set:Nn \l_@@_tmpb_dim {\l_@@_tmpe_tl}

   \prg_replicate:nn {3}
   {
     \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
   }
 }
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl
 \group_end:
}
\cs_generate_variant:Nn \@@_replace_lines:n {V}
\cs_new_protected_nopar:Npn \spath_replace_lines:Nn #1#2
{
 \@@_replace_lines:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_replace_lines:Nn {NV, cV, cv, Nv}
\cs_new_protected_nopar:Npn \spath_replace_lines:N #1
{
 \spath_replace_lines:NV #1#1
}
\cs_generate_variant:Nn \spath_replace_lines:N {c}
\cs_new_protected_nopar:Npn \spath_greplace_lines:Nn #1#2
{
 \@@_replace_lines:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_greplace_lines:Nn {NV, cV, cv, Nv}
\cs_new_protected_nopar:Npn \spath_greplace_lines:N #1
{
 \spath_greplace_lines:NV #1#1
}
\cs_generate_variant:Nn \spath_greplace_lines:N {c}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_replace_rectangles:Nn,
% \spath_replace_rectangles:Nn,
% \spath_replace_rectangles:Nn,
% \spath_replace_rectangles:Nn,
% }
% Replace any rectangle components by lines.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_replace_rectangles:n #1
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpa_tl {#1}
 \tl_clear:N \l_@@_tmpb_tl

 \bool_do_until:nn
 {
   \tl_if_empty_p:N \l_@@_tmpa_tl
 }
 {
   \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl }
   \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl }
   \tl_set:Nx \l_@@_tmpd_tl {\tl_head:N \l_@@_tmpa_tl }
   \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl }
   \tl_set:Nx \l_@@_tmpe_tl {\tl_head:N \l_@@_tmpa_tl }
   \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl }

   \tl_if_eq:NNTF \l_@@_tmpc_tl \c_spath_rectcorner_tl
   {

     \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl }
     \dim_set:Nn \l_@@_tmpa_dim
     {
       \tl_item:Nn \l_@@_tmpa_tl {1}
     }
     \dim_set:Nn \l_@@_tmpb_dim
     {
       \tl_item: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_put_right:NV \l_@@_tmpb_tl \c_spath_moveto_tl
     \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpd_tl
     \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpe_tl

     \tl_put_right:NV \l_@@_tmpb_tl \c_spath_lineto_tl
     \tl_put_right:Nx \l_@@_tmpb_tl
     {
       {
         \fp_to_dim:n { \l_@@_tmpd_tl + \l_@@_tmpa_dim }
       }
     }
     \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpe_tl

     \tl_put_right:NV \l_@@_tmpb_tl \c_spath_lineto_tl
     \tl_put_right:Nx \l_@@_tmpb_tl
     {
       {
         \fp_to_dim:n { \l_@@_tmpd_tl + \l_@@_tmpa_dim }
       }
     }
     \tl_put_right:Nx \l_@@_tmpb_tl
     {
       {
         \fp_to_dim:n { \l_@@_tmpe_tl + \l_@@_tmpb_dim }
       }
     }

     \tl_put_right:NV \l_@@_tmpb_tl \c_spath_lineto_tl
     \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpd_tl
     \tl_put_right:Nx \l_@@_tmpb_tl
     {
       {
         \fp_to_dim:n { \l_@@_tmpe_tl + \l_@@_tmpb_dim }
       }
     }

     \tl_put_right:NV \l_@@_tmpb_tl \c_spath_closepath_tl
     \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpd_tl
     \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpe_tl

   }
   {
     \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpc_tl
     \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpd_tl
     \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpe_tl
   }
 }
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl
 \group_end:
}
\cs_generate_variant:Nn \@@_replace_rectangles:n {V}
\cs_new_protected_nopar:Npn \spath_replace_rectangles:Nn #1#2
{
 \@@_replace_rectangles:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_replace_rectangles:Nn {NV, cV, cv, Nv}
\cs_new_protected_nopar:Npn \spath_replace_rectangles:N #1
{
 \spath_replace_rectangles:NV #1#1
}
\cs_generate_variant:Nn \spath_replace_rectangles:N {c}
\cs_new_protected_nopar:Npn \spath_greplace_rectangles:Nn #1#2
{
 \@@_replace_rectangles:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_greplace_rectangles:Nn {NV, cV, cv, Nv}
\cs_new_protected_nopar:Npn \spath_greplace_rectangles:N #1
{
 \spath_greplace_rectangles:NV #1#1
}
\cs_generate_variant:Nn \spath_greplace_rectangles:N {c}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_remove_empty_components:Nn,
% \spath_remove_empty_components:N,
% \spath_gremove_empty_components:Nn,
% \spath_gremove_empty_components:N
% }
% Remove any component that is simply a moveto.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_remove_empty_components:n #1
{
 \group_begin:
 \spath_components_to_seq:Nn \l_@@_tmpa_seq {#1}
 \tl_clear:N \l_@@_tmpa_tl
 \seq_map_inline:Nn \l_@@_tmpa_seq
 {
   \int_compare:nF
   {
     \tl_count:n {##1} == 3
   }
   {
     \tl_put_right:Nn \l_@@_tmpa_tl {##1}
   }
 }
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_remove_empty_components:Nn #1#2
{
 \@@_remove_empty_components:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_remove_empty_components:Nn {NV}
\cs_new_protected_nopar:Npn \spath_remove_empty_components:N #1
{
 \spath_remove_empty_components:NV #1#1
}
\cs_generate_variant:Nn \spath_remove_empty_components:N {c}
\cs_new_protected_nopar:Npn \spath_gremove_empty_components:Nn #1#2
{
 \@@_remove_empty_components:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gremove_empty_components:Nn {NV}
\cs_new_protected_nopar:Npn \spath_gremove_empty_components:N #1
{
 \spath_gremove_empty_components:NV #1#1
}
\cs_generate_variant:Nn \spath_gremove_empty_components:N {c}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\spath_if_eq:nn}
% Test if two soft paths are equal, we allow a little tolerance on the calculations.
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn \spath_if_eq:nn #1#2 { T, F, TF }
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpa_tl {#1}
 \tl_set:Nn \l_@@_tmpb_tl {#2}
 \bool_gset_true:N \g_@@_tmpa_bool
 \int_compare:nNnTF
 {\tl_count:N \l_@@_tmpa_tl}
 =
 {\tl_count:N \l_@@_tmpb_tl}
 {
   \int_step_inline:nnnn {1} {3} {\tl_count:N \l_@@_tmpa_tl}
   {
     \tl_set:Nx \l_@@_tmpc_tl {\tl_item:Nn \l_@@_tmpa_tl {##1}}
     \tl_set:Nx \l_@@_tmpd_tl {\tl_item:Nn \l_@@_tmpb_tl {##1}}
     \tl_if_eq:NNF \l_@@_tmpc_tl \l_@@_tmpd_tl
     {
       \bool_gset_false:N \g_@@_tmpa_bool
     }
     \dim_set:Nn \l_@@_tmpa_dim {\tl_item:Nn \l_@@_tmpa_tl {##1+1}}
     \dim_set:Nn \l_@@_tmpb_dim {\tl_item:Nn \l_@@_tmpb_tl {##1+1}}
     \dim_compare:nF
     {
       \dim_abs:n
       {
         \l_@@_tmpa_dim - \l_@@_tmpb_dim
       }
       < 0.001pt
     }
     {
       \bool_gset_false:N \g_@@_tmpa_bool
     }
     \dim_set:Nn \l_@@_tmpa_dim {\tl_item:Nn \l_@@_tmpa_tl {##1+2}}
     \dim_set:Nn \l_@@_tmpb_dim {\tl_item:Nn \l_@@_tmpb_tl {##1+2}}
     \dim_compare:nF
     {
       \dim_abs:n
       {
         \l_@@_tmpa_dim - \l_@@_tmpb_dim
       }
       < 0.001pt
     }
     {
       \bool_gset_false:N \g_@@_tmpa_bool
     }
   }
 }
 {
   \bool_gset_false:N \g_@@_tmpa_bool
 }
 \group_end:
 \bool_if:NTF \g_@@_tmpa_bool
 {
   \prg_return_true:
 }
 {
   \prg_return_false:
 }
}
\prg_generate_conditional_variant:Nnn \spath_if_eq:nn {VV, Vn, nV, vv} {TF, T, F}
%    \end{macrocode}
% \end{macro}
%
% \subsection{Splitting Commands}
%
% \begin{macro}[internal]{
% \spath_split_curve:NNnn,
% \spath_gsplit_curve:NNnn
% }
% Splits a Bezier cubic into pieces, storing the pieces in the first two arguments.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_split_curve:nn #1#2
{
 \group_begin:
 \tl_set_eq:NN \l_@@_tmpa_tl \c_spath_moveto_tl
 \tl_put_right:Nx \l_@@_tmpa_tl {
   {\tl_item:nn {#1} {2}}
   {\tl_item:nn {#1} {3}}
 }
 \tl_put_right:NV \l_@@_tmpa_tl \c_spath_curvetoa_tl
 \tl_put_right:Nx \l_@@_tmpa_tl
 {
   {\fp_to_dim:n
   {
     (1 - #2) * \tl_item:nn {#1} {2} + (#2) * \tl_item:nn {#1} {5}
   }}
   {\fp_to_dim:n
   {
     (1 - #2) * \tl_item:nn {#1} {3} + (#2) * \tl_item:nn {#1} {6}
   }}
 }

 \tl_put_right:NV \l_@@_tmpa_tl \c_spath_curvetob_tl
 \tl_put_right:Nx \l_@@_tmpa_tl
 {
   {\fp_to_dim:n
   {
     (1 - #2)^2 * \tl_item:nn {#1} {2}
     + 2 * (1 - #2) * (#2) * \tl_item:nn {#1} {5}
     + (#2)^2 * \tl_item:nn {#1} {8}
   }}
   {\fp_to_dim:n
   {
     (1 - #2)^2 * \tl_item:nn {#1} {3}
     + 2 * (1 - #2) * (#2) * \tl_item:nn {#1} {6}
     + (#2)^2 * \tl_item:nn {#1} {9}
   }}
 }

 \tl_put_right:NV \l_@@_tmpa_tl \c_spath_curveto_tl
 \tl_put_right:Nx \l_@@_tmpa_tl
 {
   {\fp_to_dim:n
     {
       (1 - #2)^3 * \tl_item:nn {#1} {2}
       + 3 * (1 - #2)^2 * (#2) * \tl_item:nn {#1} {5}
       + 3 * (1 - #2) * (#2)^2 * \tl_item:nn {#1} {8}
       + (#2)^3 * \tl_item:nn {#1} {11}
   }}
   {\fp_to_dim:n
   {
     (1 - #2)^3 * \tl_item:nn {#1} {3}
     + 3 * (1 - #2)^2 * (#2) * \tl_item:nn {#1} {6}
     + 3 * (1 - #2) * (#2)^2 * \tl_item:nn {#1} {9}
     + (#2)^3 * \tl_item:nn {#1} {12}
   }}
 }

 \tl_gclear:N \g_@@_output_tl
 \@@_tl_gput_right_braced:NV \g_@@_output_tl \l_@@_tmpa_tl

 \tl_clear:N \l_@@_tmpa_tl
 \tl_set_eq:NN \l_@@_tmpa_tl \c_spath_moveto_tl
 \tl_put_right:Nx \l_@@_tmpa_tl
 {
   {\fp_to_dim:n
     {
       (1 - #2)^3 * \tl_item:nn {#1} {2}
       + 3 * (1 - #2)^2 * (#2) * \tl_item:nn {#1} {5}
       + 3 * (1 - #2) * (#2)^2 * \tl_item:nn {#1} {8}
       + (#2)^3 * \tl_item:nn {#1} {11}
   }}
   {\fp_to_dim:n
   {
     (1 - #2)^3 * \tl_item:nn {#1} {3}
     + 3 * (1 - #2)^2 * (#2) * \tl_item:nn {#1} {6}
     + 3 * (1 - #2) * (#2)^2 * \tl_item:nn {#1} {9}
     + (#2)^3 * \tl_item:nn {#1} {12}
   }}
 }

 \tl_put_right:NV \l_@@_tmpa_tl \c_spath_curvetoa_tl
 \tl_put_right:Nx \l_@@_tmpa_tl
 {
   {\fp_to_dim:n
   {
     (1 - #2)^2 * \tl_item:nn {#1} {5}
     + 2 * (1 - #2) * (#2) * \tl_item:nn {#1} {8}
     + (#2)^2 * \tl_item:nn {#1} {11}
   }}
   {\fp_to_dim:n
   {
     (1 - #2)^2 * \tl_item:nn {#1} {6}
     + 2 * (1 - #2) * (#2) * \tl_item:nn {#1} {9}
     + (#2)^2 * \tl_item:nn {#1} {12}
   }}
 }
 \tl_put_right:NV \l_@@_tmpa_tl \c_spath_curvetob_tl
 \tl_put_right:Nx \l_@@_tmpa_tl
 {
   {\fp_to_dim:n
   {
     (1 - #2) * \tl_item:nn {#1} {8} + (#2) * \tl_item:nn {#1} {11}
   }}
   {\fp_to_dim:n
   {
     (1 - #2) * \tl_item:nn {#1} {9} + (#2) * \tl_item:nn {#1} {12}
   }}
 }
 \tl_put_right:NV \l_@@_tmpa_tl \c_spath_curveto_tl
 \tl_put_right:Nx \l_@@_tmpa_tl {
   {\tl_item:nn {#1} {11}}
   {\tl_item:nn {#1} {12}}
 }

 \@@_tl_gput_right_braced:NV \g_@@_output_tl \l_@@_tmpa_tl
 \group_end:
}
\cs_generate_variant:Nn \@@_split_curve:nn {nv, nV}
\cs_new_protected_nopar:Npn \spath_split_curve:NNnn #1#2#3#4
{
 \@@_split_curve:nn {#3}{#4}
 \tl_set:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}}
 \tl_set:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}}
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_split_curve:NNnn {NNnV, NNVn, NNVV}
\cs_new_protected_nopar:Npn \spath_gsplit_curve:NNnn #1#2#3#4
{
 \@@_split_curve:nn {#3}{#4}
 \tl_gset:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}}
 \tl_gset:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}}
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gsplit_curve:NNnn {NNnV, NNVn, NNVV}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[internal]{
% \spath_maybe_split_curve:Nn,
% \spath_maybe_gsplit_curve:Nn,
% }
% Possibly splits a bezier curve to ensure that the pieces don't self-intersect.
% 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 \@@_maybe_split_curve:n #1
{
 \group_begin:
 \fp_set:Nn \l_@@_tmpa_fp
 {
   (
   \tl_item:nn {#1} {3}
   - 3 * \tl_item:nn {#1} {6}
   + 3 * \tl_item:nn {#1} {9}
   - \tl_item:nn {#1} {12}
   )
   *
   (3 * \tl_item:nn {#1} {8} - 3 * \tl_item:nn {#1} {11})
   -
   (
   \tl_item:nn {#1} {2}
   - 3 * \tl_item:nn {#1} {5}
   + 3 * \tl_item:nn {#1} {8}
   - \tl_item:nn {#1} {11}
   )
   *
   (3 * \tl_item:nn {#1} {9} - 3 * \tl_item:nn {#1} {12})
 }
 \fp_set:Nn \l_@@_tmpb_fp
 {
   (
   \tl_item:nn {#1} {2}
   - 3 * \tl_item:nn {#1} {5}
   + 3 * \tl_item:nn {#1} {8}
   - \tl_item:nn {#1} {11}
   )
   *
   (
   3 * \tl_item:nn {#1} {6}
   - 6 * \tl_item:nn {#1} {9}
   + 3 * \tl_item:nn {#1} {12}
   )
   -
   (
   \tl_item:nn {#1} {3}
   - 3 * \tl_item:nn {#1} {6}
   + 3 * \tl_item:nn {#1} {9}
   - \tl_item:nn {#1} {12}
   )
   *
   (
   3 * \tl_item:nn {#1} {5}
   - 6 * \tl_item:nn {#1} {8}
   + 3 * \tl_item:nn {#1} {11}
   )
 }
 \fp_compare:nTF
 {
   \l_@@_tmpb_fp != 0
 }
 {
   \fp_set:Nn \l_@@_tmpa_fp {.5 * \l_@@_tmpa_fp / \l_@@_tmpb_fp}
   \bool_if:nTF
   {
     \fp_compare_p:n {0 < \l_@@_tmpa_fp}
     &&
     \fp_compare_p:n {\l_@@_tmpa_fp < 1}
   }
   {
     \@@_split_curve:nV {#1} \l_@@_tmpa_fp
   }
   {
     \tl_gset:Nn \g_@@_output_tl { {#1} {} }
   }
 }
 {
   \tl_gset:Nn \g_@@_output_tl { {#1} {} }
 }
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_maybe_split_curve:NNn #1#2#3
{
 \@@_maybe_split_curve:n {#3}
 \tl_set:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}}
 \tl_set:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}}
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_maybe_split_curve:NNn {NNn, NNV }
\cs_new_protected_nopar:Npn \spath_maybe_gsplit_curve:NNn #1#2#3
{
 \@@_maybe_split_curve:n {#3}
 \tl_gset:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}}
 \tl_gset:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}}
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_maybe_gsplit_curve:NNn {NNn, NNV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_split_curves:Nn,
% \spath_gsplit_curves:Nn,
% }
% Slurp through the path ensuring that beziers don't self-intersect.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_split_curves:n #1
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpa_tl {#1}
 \tl_clear:N \l_@@_tmpb_tl
 \tl_clear:N \l_@@_tmpc_tl
 \bool_do_until:nn
 {
   \tl_if_empty_p:N \l_@@_tmpa_tl
 }
 {
   \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl}
   \token_case_meaning:NnF \l_@@_tmpc_tl
   {
     \c_spath_curvetoa_tl
     {
       \tl_clear:N \l_@@_tmpd_tl
       \tl_set_eq:NN \l_@@_tmpd_tl \c_spath_moveto_tl
       \tl_put_right:Nx \l_@@_tmpd_tl
       {
         { \dim_use:N \l_@@_tmpa_dim }
         { \dim_use:N \l_@@_tmpb_dim }
       }
       \dim_set:Nn \l_@@_tmpa_dim
       {
         \tl_item:Nn \l_@@_tmpa_tl {8}
       }
       \dim_set:Nn \l_@@_tmpb_dim
       {
         \tl_item:Nn \l_@@_tmpa_tl {9}
       }
       \prg_replicate:nn {3}
       {
         \tl_put_right:Nx \l_@@_tmpd_tl
         {
           \tl_item:Nn \l_@@_tmpa_tl {1}
           {\tl_item:Nn \l_@@_tmpa_tl {2}}
           {\tl_item:Nn \l_@@_tmpa_tl {3}}
         }
         \prg_replicate:nn {3}
         {
           \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
         }
       }

       \spath_maybe_split_curve:NNV
       \l_@@_tmpd_tl
       \l_@@_tmpe_tl
       \l_@@_tmpd_tl
       \prg_replicate:nn {3}
       {
         \tl_set:Nx \l_@@_tmpd_tl {\tl_tail:N \l_@@_tmpd_tl}
         \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl}
       }
       \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpd_tl
       \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpe_tl
     }
   }
   {
     \dim_set:Nn \l_@@_tmpa_dim
     {
       \tl_item:Nn \l_@@_tmpa_tl {2}
     }
     \dim_set:Nn \l_@@_tmpb_dim
     {
       \tl_item:Nn \l_@@_tmpa_tl {3}
     }
     \tl_put_right:Nx \l_@@_tmpb_tl
     {
       \tl_item:Nn \l_@@_tmpa_tl {1}
       {\tl_item:Nn \l_@@_tmpa_tl {2}}
       {\tl_item:Nn \l_@@_tmpa_tl {3}}
     }
     \prg_replicate:nn {3}
     {
       \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
     }
   }
 }
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_split_curves:Nn #1#2
{
 \@@_split_curves:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_split_curves:Nn {NV, cV, cn, cv }
\cs_new_protected_nopar:Npn \spath_split_curves:N #1
{
 \spath_split_curves:NV #1#1
}
\cs_generate_variant:Nn \spath_split_curves:N {c}
\cs_new_protected_nopar:Npn \spath_gsplit_curves:Nn #1#2
{
 \@@_split_curves:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gsplit_curves:Nn {NV, cV, cn, cv }
\cs_new_protected_nopar:Npn \spath_gsplit_curves:N #1
{
 \spath_gsplit_curves:NV #1#1
}
\cs_generate_variant:Nn \spath_gsplit_curves:N {c}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_split_line:NNnn,
% \spath_gsplit_line:NNnn
% }
% Splits a line segment.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_split_line:nn #1#2
{
 \group_begin:
 \tl_set_eq:NN \l_@@_tmpa_tl \c_spath_moveto_tl
 \tl_put_right:Nx \l_@@_tmpa_tl {
   {\tl_item:nn {#1} {2}}
   {\tl_item:nn {#1} {3}}
 }
 \tl_put_right:NV \l_@@_tmpa_tl \c_spath_lineto_tl
 \tl_put_right:Nx \l_@@_tmpa_tl
 {
   {\fp_to_dim:n
   {
     (1 - #2) * \tl_item:nn {#1} {2} + (#2) * \tl_item:nn {#1} {5}
   }}
   {\fp_to_dim:n
   {
     (1 - #2) * \tl_item:nn {#1} {3} + (#2) * \tl_item:nn {#1} {6}
   }}
 }
 \tl_gclear:N \g_@@_output_tl
 \@@_tl_gput_right_braced:NV \g_@@_output_tl \l_@@_tmpa_tl

 \tl_clear:N \l_@@_tmpa_tl
 \tl_set_eq:NN \l_@@_tmpa_tl \c_spath_moveto_tl
 \tl_put_right:Nx \l_@@_tmpa_tl
 {
   {\fp_to_dim:n
   {
     (1 - #2) * \tl_item:nn {#1} {2} + (#2) * \tl_item:nn {#1} {5}
   }}
   {\fp_to_dim:n
   {
     (1 - #2) * \tl_item:nn {#1} {3} + (#2) * \tl_item:nn {#1} {6}
   }}
 }
 \tl_put_right:NV \l_@@_tmpa_tl \c_spath_lineto_tl
 \tl_put_right:Nx \l_@@_tmpa_tl {
   {\tl_item:nn {#1} {5}}
   {\tl_item:nn {#1} {6}}
 }

 \@@_tl_gput_right_braced:NV \g_@@_output_tl \l_@@_tmpa_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_split_line:NNnn #1#2#3#4
{
 \@@_split_line:nn {#3}{#4}
 \tl_set:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}}
 \tl_set:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}}
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_split_line:NNnn {NNnV, NNVn, NNVV}
\cs_new_protected_nopar:Npn \spath_gsplit_line:NNnn #1#2#3#4
{
 \@@_split_line:nn {#3}{#4}
 \tl_gset:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}}
 \tl_gset:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}}
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gsplit_line:NNnn {NNnV, NNVn, NNVV}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[internal]{
% \spath_split_rectangle:Nnn,
% \spath_gsplit_rectangle:Nnn
% }
% Cuts a rectangle at a point.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_split_rectangle:nn #1#2
{
 \group_begin:
 \spath_open:Nn \l_@@_tmpa_tl {#1}
 \fp_set:Nn \l_@@_tmpa_fp {4*(#2)}
 \spath_split_at:NNVV
 \l_@@_tmpa_tl \l_@@_tmpb_tl \l_@@_tmpa_tl \l_@@_tmpa_fp
 \@@_append_no_move:VV \l_@@_tmpb_tl \l_@@_tmpa_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_split_rectangle:Nnn #1#2#3
{
 \@@_split_rectangle:nn {#2}{#3}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_split_rectangle:Nnn {NnV, NVn, NVV}
\cs_new_protected_nopar:Npn \spath_gsplit_rectangle:Nnn #1#2#3
{
 \@@_split_rectangle:nn {#2}{#3}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gsplit_rectangle:Nnn {NnV, NVn, NVV}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[internal]{
% \spath_split_at:NNnn,
% \spath_split_at:Nnn,
% \spath_split_at:Nn,
% \spath_gsplit_at:NNnn
% \spath_gsplit_at:Nnn,
% \spath_gsplit_at:Nn,
% \spath_split_at_keep_start:Nnn,
% \spath_split_at_keep_start:Nn,
% \spath_gsplit_at_keep_start:Nnn,
% \spath_gsplit_at_keep_start:Nn,
% \spath_split_at_keep_end:Nnn,
% \spath_split_at_keep_end:Nn,
% \spath_gsplit_at_keep_end:Nnn,
% \spath_gsplit_at_keep_end:Nn,
% \spath_split_at_normalised:NNnn,
% \spath_split_at_normalised:Nnn,
% \spath_split_at_normalised:Nn,
% \spath_gsplit_at_normalised:NNnn
% \spath_gsplit_at_normalised:Nnn,
% \spath_gsplit_at_normalised:Nn,
% \spath_split_at_normalised_keep_start:Nnn,
% \spath_split_at_normalised_keep_start:Nn,
% \spath_gsplit_at_normalised_keep_start:Nnn,
% \spath_gsplit_at_normalised_keep_start:Nn,
% \spath_split_at_normalised_keep_end:Nnn,
% \spath_split_at_normalised_keep_end:Nn,
% \spath_gsplit_at_normalised_keep_end:Nnn,
% \spath_gsplit_at_normalised_keep_end:Nn,
% }
% Split a path according to the parameter generated by the intersection routine.
% The versions with two \texttt{N} arguments stores the two parts in two macros, the version with a single \texttt{N} joins them back into a single path (as separate components).
% The \texttt{keep} versions throw away the other part of the curve.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_split_at:nn #1#2
{
 \group_begin:
 \int_set:Nn \l_@@_tmpa_int {\fp_to_int:n {floor(#2) + 1}}
 \fp_set:Nn \l_@@_tmpa_fp {#2 - floor(#2)}

 % Is split point near one end or other of a component?
 \fp_compare:nT
 {
   \l_@@_tmpa_fp < 0.01
 }
 {
   % Near the start, so we'll place it at the start
   \fp_set:Nn \l_@@_tmpa_fp {0}
 }
 \fp_compare:nT
 {
   \l_@@_tmpa_fp > 0.99
 }
 {
   % Near the end, so we'll place it at the end
   \fp_set:Nn \l_@@_tmpa_fp {0}
   \int_incr:N \l_@@_tmpa_int
 }

 \int_zero:N \l_@@_tmpb_int
 \bool_set_true:N \l_@@_tmpa_bool

 \tl_set:Nn \l_@@_tmpe_tl {#1}

 \dim_zero:N \l_@@_tmpa_dim
 \dim_zero:N \l_@@_tmpb_dim

 % Remember if the component is closed
 \spath_finalaction:NV \l_@@_tmpa_tl \l_@@_tmpe_tl

 \bool_set:Nn \l_@@_closed_bool
 {
   \tl_if_eq_p:NN \l_@@_tmpa_tl \c_spath_closepath_tl
   ||
   \tl_if_eq_p:NN \l_@@_tmpa_tl \c_spath_rectcorner_tl
 }

 % Open it
 \spath_open:N \l_@@_tmpe_tl

 \tl_clear:N \l_@@_tmpa_tl
 \tl_clear:N \l_@@_tmpb_tl
 \tl_clear:N \l_@@_tmpc_tl
 \tl_clear:N \l_@@_tmpd_tl

 \bool_until_do:nn {
   \tl_if_empty_p:N \l_@@_tmpe_tl
   ||
   \int_compare_p:n { \l_@@_tmpa_int == \l_@@_tmpb_int  }
 }
 {
   \tl_set:Nx \l_@@_tmpf_tl {\tl_head:N \l_@@_tmpe_tl}
   \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl }
   \token_case_meaning:Nn \l_@@_tmpf_tl
   {
     \c_spath_lineto_tl
     {
       \int_incr:N \l_@@_tmpb_int
     }
     \c_spath_curvetoa_tl
     {
       \int_incr:N \l_@@_tmpb_int
     }
     \c_spath_rectcorner_tl
     {
       \int_incr:N \l_@@_tmpb_int
     }
   }
   \int_compare:nT { \l_@@_tmpb_int < \l_@@_tmpa_int  }
   {
     \tl_put_right:NV \l_@@_tmpc_tl \l_@@_tmpf_tl

     \tl_put_right:Nx \l_@@_tmpc_tl
     {{ \tl_head:N \l_@@_tmpe_tl }}
     \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpe_tl}
     \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl }

     \tl_put_right:Nx \l_@@_tmpc_tl
     {{ \tl_head:N \l_@@_tmpe_tl }}
     \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpe_tl}
     \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl }

   }
 }

 \tl_clear:N \l_@@_tmpd_tl
 \tl_put_right:NV \l_@@_tmpd_tl \c_spath_moveto_tl
 \tl_put_right:Nx \l_@@_tmpd_tl
 {
   {\dim_use:N \l_@@_tmpa_dim}
   {\dim_use:N \l_@@_tmpb_dim}
 }

 \fp_compare:nTF
 {
   \l_@@_tmpa_fp == 0
 }
 {
   \tl_set_eq:NN \l_@@_tmpb_tl \l_@@_tmpd_tl
   \tl_if_empty:NF \l_@@_tmpe_tl
   {
     \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpf_tl
     \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpe_tl
   }
 }
 {

   \token_case_meaning:Nn \l_@@_tmpf_tl
   {
     \c_spath_lineto_tl
     {
       \tl_put_right:NV \l_@@_tmpd_tl \l_@@_tmpf_tl
       \tl_put_right:Nx \l_@@_tmpd_tl
       {{ \tl_head:N \l_@@_tmpe_tl }}
       \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl }

       \tl_put_right:Nx \l_@@_tmpd_tl
       {{ \tl_head:N \l_@@_tmpe_tl }}
       \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl }

       \spath_split_line:NNVV
       \l_@@_tmpa_tl
       \l_@@_tmpb_tl
       \l_@@_tmpd_tl
       \l_@@_tmpa_fp

       \prg_replicate:nn {3} {
         \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
       }

       \tl_put_right:NV \l_@@_tmpc_tl \l_@@_tmpa_tl
       \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpe_tl
     }
     \c_spath_curvetoa_tl
     {
       \tl_put_right:NV \l_@@_tmpd_tl \l_@@_tmpf_tl
       \tl_put_right:Nx \l_@@_tmpd_tl
       {{ \tl_head:N \l_@@_tmpe_tl }}
       \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl }

       \tl_put_right:Nx \l_@@_tmpd_tl
       {{ \tl_head:N \l_@@_tmpe_tl }}
       \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl }

       \prg_replicate:nn {2} {

         \tl_put_right:Nx \l_@@_tmpd_tl
         { \tl_head:N \l_@@_tmpe_tl }
         \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl }

         \tl_put_right:Nx \l_@@_tmpd_tl
         {{ \tl_head:N \l_@@_tmpe_tl }}
         \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl }

         \tl_put_right:Nx \l_@@_tmpd_tl
         {{ \tl_head:N \l_@@_tmpe_tl }}
         \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl }
       }

       \spath_split_curve:NNVV
       \l_@@_tmpa_tl
       \l_@@_tmpb_tl
       \l_@@_tmpd_tl \l_@@_tmpa_fp

       \prg_replicate:nn {3} {
         \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
       }

       \tl_put_right:NV \l_@@_tmpc_tl \l_@@_tmpa_tl
       \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpe_tl
     }

     \c_spath_rectcorner_tl
     {
       \tl_clear:N \l_@@_tmpd_tl
       \tl_put_right:NV \l_@@_tmpd_tl \l_@@_tmpf_tl

       \tl_put_right:Nx \l_@@_tmpd_tl {{\tl_head:N \l_@@_tmpe_tl}}
       \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl}
       \tl_put_right:Nx \l_@@_tmpd_tl {{\tl_head:N \l_@@_tmpe_tl}}
       \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl}

       \tl_put_right:Nx \l_@@_tmpd_tl {\tl_head:N \l_@@_tmpe_tl}
       \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl}

       \tl_put_right:Nx \l_@@_tmpd_tl {{\tl_head:N \l_@@_tmpe_tl}}
       \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl}
       \tl_put_right:Nx \l_@@_tmpd_tl {{\tl_head:N \l_@@_tmpe_tl}}
       \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl}

       \spath_split_rectangle:NVV
       \l_@@_tmpa_tl
       \l_@@_tmpd_tl
       \l_@@_tmpa_fp

       \tl_put_right:NV \l_@@_tmpc_tl \l_@@_tmpa_tl
       \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpe_tl
     }

   }
 }

 \bool_if:NT \l_@@_closed_bool
 {
   \prg_replicate:nn {3}
   {
     \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl}
   }
   \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpc_tl
   \tl_set_eq:NN \l_@@_tmpc_tl \l_@@_tmpb_tl
   \tl_clear:N \l_@@_tmpb_tl
 }

 \tl_gclear:N \g_@@_output_tl
 \@@_tl_gput_right_braced:NV \g_@@_output_tl \l_@@_tmpc_tl
 \@@_tl_gput_right_braced:NV \g_@@_output_tl \l_@@_tmpb_tl
 \group_end:
}
\cs_generate_variant:Nn  \@@_split_at:nn {nV, VV}
\cs_new_protected_nopar:Npn \@@_split_at_normalised:nn #1#2
{
 \group_begin:
 \spath_reallength:Nn \l_@@_tmpa_int {#1}

 \tl_set:Nx \l_@@_tmpa_tl
 {\fp_to_decimal:n {(#2) * (\l_@@_tmpa_int)}}
 \@@_split_at:nV {#1} \l_@@_tmpa_tl
 \group_end:
}
\cs_generate_variant:Nn  \@@_split_at_normalised:nn {nV}
\cs_new_protected_nopar:Npn \spath_split_at:NNnn #1#2#3#4
{
 \@@_split_at:nn {#3}{#4}
 \tl_set:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}}
 \tl_set:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}}
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_split_at:NNnn {NNVn, NNVV, NNnV}
\cs_new_protected_nopar:Npn \spath_gsplit_at:NNnn #1#2#3#4
{
 \@@_split_at:nn {#3}{#4}
 \tl_gset:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}}
 \tl_gset:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}}
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gsplit_at:NNnn {NNVn, NNVV, NNnV}
\cs_new_protected_nopar:Npn \spath_split_at_keep_start:Nnn #1#2#3
{
 \@@_split_at:nn {#2}{#3}
 \tl_set:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}}
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_split_at_keep_start:Nnn {NVn}
\cs_new_protected_nopar:Npn \spath_split_at_keep_start:Nn #1#2
{
 \spath_split_at_keep_start:NVn #1#1{#2}
}
\cs_new_protected_nopar:Npn \spath_gsplit_at_keep_start:Nnn #1#2#3
{
 \@@_split_at:nn {#2}{#3}
 \tl_gset:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}}
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gsplit_at_keep_start:Nnn {NVn}
\cs_new_protected_nopar:Npn \spath_gsplit_at_keep_start:Nn #1#2
{
 \spath_gsplit_at_keep_start:NVn #1#1{#2}
}
\cs_new_protected_nopar:Npn \spath_split_at_keep_end:Nnn #1#2#3
{
 \@@_split_at:nn {#2}{#3}
 \tl_set:Nx #1 {\tl_item:Nn \g_@@_output_tl {2}}
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_split_at_keep_end:Nnn {NVn}
\cs_new_protected_nopar:Npn \spath_split_at_keep_end:Nn #1#2
{
 \spath_split_at_keep_end:NVn #1#1{#2}
}
\cs_new_protected_nopar:Npn \spath_gsplit_at_keep_end:Nnn #1#2#3
{
 \@@_split_at:nn {#2}{#3}
 \tl_gset:Nx #1 {\tl_item:Nn \g_@@_output_tl {2}}
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gsplit_at_keep_end:Nnn {NVn}
\cs_new_protected_nopar:Npn \spath_gsplit_at_keep_end:Nn #1#2
{
 \spath_gsplit_at_keep_end:NVn #1#1{#2}
}
\cs_new_protected_nopar:Npn \spath_split_at_normalised:NNnn #1#2#3#4
{
 \@@_split_at_normalised:nn {#3}{#4}
 \tl_set:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}}
 \tl_set:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}}
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_split_at_normalised:NNnn {NNVn, NNVV, NNnV, ccvn}
\cs_new_protected_nopar:Npn \spath_gsplit_at_normalised:NNnn #1#2#3#4
{
 \@@_split_at_normalised:nn {#3}{#4}
 \tl_gset:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}}
 \tl_gset:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}}
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gsplit_at_normalised:NNnn {NNVn, NNVV, NNnV, ccvn}
\cs_new_protected_nopar:Npn \spath_split_at_normalised_keep_start:Nnn #1#2#3
{
 \@@_split_at_normalised:nn {#2}{#3}
 \tl_set:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}}
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_split_at_normalised_keep_start:Nnn {NVn}
\cs_new_protected_nopar:Npn \spath_split_at_normalised_keep_start:Nn #1#2
{
 \spath_split_at_normalised_keep_start:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_split_at_normalised_keep_start:Nn {cn}
\cs_new_protected_nopar:Npn \spath_gsplit_at_normalised_keep_start:Nnn #1#2#3
{
 \@@_split_at_normalised:nn {#2}{#3}
 \tl_gset:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}}
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gsplit_at_normalised_keep_start:Nnn {NVn}
\cs_new_protected_nopar:Npn \spath_gsplit_at_normalised_keep_start:Nn #1#2
{
 \spath_gsplit_at_normalised_keep_start:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_gsplit_at_normalised_keep_start:Nn {cn}
\cs_new_protected_nopar:Npn \spath_split_at_normalised_keep_end:Nnn #1#2#3
{
 \@@_split_at_normalised:nn {#2}{#3}
 \tl_set:Nx #1 {\tl_item:Nn \g_@@_output_tl {2}}
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_split_at_normalised_keep_end:Nnn {NVn}
\cs_new_protected_nopar:Npn \spath_split_at_normalised_keep_end:Nn #1#2
{
 \spath_split_at_normalised_keep_end:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_split_at_normalised_keep_end:Nn {cn}
\cs_new_protected_nopar:Npn \spath_gsplit_at_normalised_keep_end:Nnn #1#2#3
{
 \@@_split_at_normalised:nn {#2}{#3}
 \tl_gset:Nx #1 {\tl_item:Nn \g_@@_output_tl {2}}
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gsplit_at_normalised_keep_end:Nnn {NVn}
\cs_new_protected_nopar:Npn \spath_gsplit_at_normalised_keep_end:Nn #1#2
{
 \spath_gsplit_at_normalised_keep_end:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_gsplit_at_normalised_keep_end:Nn {cn}
%    \end{macrocode}
%
%
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \spath_split_at:Nnn #1#2#3
{
 \@@_split_at:nn {#2}{#3}
 \tl_set:Nx #1
 {
   \tl_item:Nn \g_@@_output_tl {1}
   \tl_item:Nn \g_@@_output_tl {2}
 }
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_split_at:Nnn {NVn, NVV}
\cs_new_protected_nopar:Npn \spath_split_at:Nn #1#2
{
 \spath_split_at:NVn #1#1{#2}
}
\cs_new_protected_nopar:Npn \spath_gsplit_at:Nnn #1#2#3
{
 \@@_split_at:nn {#2}{#3}
 \tl_gset:Nx #1
 {
   \tl_item:Nn \g_@@_output_tl {1}
   \tl_item:Nn \g_@@_output_tl {2}
 }
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gsplit_at:Nnn {NVn, NVV}
\cs_new_protected_nopar:Npn \spath_gsplit_at:Nn #1#2
{
 \spath_gsplit_at:NVn #1#1{#2}
}
\cs_new_protected_nopar:Npn \spath_split_at_normalised:Nnn #1#2#3
{
 \@@_split_at_normalised:nn {#2}{#3}
 \tl_set:Nx #1
 {
   \tl_item:Nn \g_@@_output_tl {1}
   \tl_item:Nn \g_@@_output_tl {2}
 }
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_split_at_normalised:Nnn {NVn, NVV}
\cs_new_protected_nopar:Npn \spath_split_at_normalised:Nn #1#2
{
 \spath_split_at_normalised:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_split_at_normalised:Nn {cn}
\cs_new_protected_nopar:Npn \spath_gsplit_at_normalised:Nnn #1#2#3
{
 \@@_split_at_normalised:nn {#2}{#3}
 \tl_gset:Nx #1
 {
   \tl_item:Nn \g_@@_output_tl {1}
   \tl_item:Nn \g_@@_output_tl {2}
 }
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gsplit_at_normalised:Nnn {NVn, NVV}
\cs_new_protected_nopar:Npn \spath_gsplit_at_normalised:Nn #1#2
{
 \spath_gsplit_at_normalised:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_gsplit_at_normalised:Nn {cn}
%    \end{macrocode}
% \end{macro}
%
%
% \subsection{Shortening Paths}
%
% This code relates to shortening paths.
% For curved paths, the routine uses the derivative at the end to figure out how far back to shorten.
% This means that the actual length that it shortens by is approximate, but it is guaranteed to be along its length.
%
% As in the previous section, there are various versions.
% In particular, there are versions where the path can be specified by a macro and is saved back into that macro.
%
% \begin{macro}[internal]{
% \spath_shorten_at_end:Nnn
% }
% This macro shortens a path from the end by a dimension.
%
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_shorten_at_end:nn #1#2
{
 \int_compare:nTF
 {
   \tl_count:n {#1} > 3
 }
 {
   \group_begin:
   \tl_set:Nn \l_@@_tmpa_tl {#1}
   \tl_reverse:N \l_@@_tmpa_tl

   \tl_set:Nx \l_@@_tmpb_tl {\tl_item:Nn \l_@@_tmpa_tl {3}}

   \tl_clear:N \l_@@_tmpe_tl
   \tl_if_eq:NNTF \l_@@_tmpb_tl \c_spath_curveto_tl
   {
     \int_set:Nn \l_@@_tmpa_int {3}
   }
   {
     \int_set:Nn \l_@@_tmpa_int {1}
   }

   \prg_replicate:nn { \l_@@_tmpa_int }
   {
     \tl_put_right:Nx \l_@@_tmpe_tl
     {
       {\tl_head:N \l_@@_tmpa_tl}
     }
     \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
     \tl_put_right:Nx \l_@@_tmpe_tl
     {
       {\tl_head:N \l_@@_tmpa_tl}
     }
     \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
     \tl_put_right:Nx \l_@@_tmpe_tl
     {
       \tl_head:N \l_@@_tmpa_tl
     }
     \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
   }

   \tl_put_right:Nx \l_@@_tmpe_tl
   {
     {\tl_item:Nn \l_@@_tmpa_tl {1}}
     {\tl_item:Nn \l_@@_tmpa_tl {2}}
   }
   \tl_put_right:NV \l_@@_tmpe_tl \c_spath_moveto_tl

   \tl_reverse:N \l_@@_tmpa_tl

   \fp_set:Nn \l_@@_tmpa_fp
   {
     \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {4}}
     -
     \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {1}}
   }

   \fp_set:Nn \l_@@_tmpb_fp
   {
     \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {5}}
     -
     \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {2}}
   }

   \fp_set:Nn \l_@@_tmpc_fp
   {
     sqrt(
     \l_@@_tmpa_fp * \l_@@_tmpa_fp
     +
     \l_@@_tmpb_fp *  \l_@@_tmpb_fp
     ) * \l_@@_tmpa_int
   }

   \fp_compare:nTF
   {
     \l_@@_tmpc_fp > #2
   }
   {

     \fp_set:Nn \l_@@_tmpc_fp
     {
       (\l_@@_tmpc_fp - #2)/ \l_@@_tmpc_fp
     }

     \tl_reverse:N \l_@@_tmpe_tl

     \tl_if_eq:NNTF \l_@@_tmpb_tl \c_spath_curveto_tl
     {
       \spath_split_curve:NNVV
       \l_@@_tmpc_tl
       \l_@@_tmpd_tl
       \l_@@_tmpe_tl
       \l_@@_tmpc_fp
     }
     {
       \spath_split_line:NNVV
       \l_@@_tmpc_tl
       \l_@@_tmpd_tl
       \l_@@_tmpe_tl
       \l_@@_tmpc_fp
     }

     \prg_replicate:nn {3}
     {
       \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl}
     }

     \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpc_tl

   }
   {

     \int_compare:nT
     {
       \tl_count:N \l_@@_tmpa_tl > 3
     }
     {
       \dim_set:Nn \l_@@_tmpa_dim {\fp_to_dim:n {#2 - \l_@@_tmpc_fp } }
       \spath_shorten_at_end:NV \l_@@_tmpa_tl \l_@@_tmpa_dim
     }
   }

   \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl
   \group_end:
 }
 {
   \tl_gset:Nn \g_@@_output_tl {#1}
 }
}
\cs_new_protected_nopar:Npn \spath_shorten_at_end:Nnn #1#2#3
{
 \@@_shorten_at_end:nn {#2}{#3}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_shorten_at_end:Nnn {NVV, cnn, cVV, NVn}
\cs_new_protected_nopar:Npn \spath_shorten_at_end:Nn #1#2
{
 \spath_shorten_at_end:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_shorten_at_end:Nn {cn, cV, NV}
\cs_new_protected_nopar:Npn \spath_gshorten_at_end:Nnn #1#2#3
{
 \@@_shorten_at_end:nn {#2}{#3}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gshorten_at_end:Nnn {NVV, cnn, cVV, NVn}
\cs_new_protected_nopar:Npn \spath_gshorten_at_end:Nn #1#2
{
 \spath_gshorten_at_end:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_gshorten_at_end:Nn {cn, cV, NV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_shorten_at_start:Nnn,
% \spath_shorten_at_start:Nn,
% \spath_gshorten_at_start:Nnn,
% \spath_gshorten_at_start:Nn
% }
%
% This macro shortens a path from the start by a dimension.
%
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_shorten_at_start:nn #1#2
{
 \int_compare:nTF
 {
   \tl_count:n {#1} > 3
 }
 {
 \group_begin:
 \tl_set:Nn \l_@@_tmpa_tl {#1}

 \tl_set:Nx \l_@@_tmpb_tl {\tl_item:Nn \l_@@_tmpa_tl {4}}

   \tl_clear:N \l_@@_tmpe_tl

 \tl_if_eq:NNTF \l_@@_tmpb_tl \c_spath_curvetoa_tl
 {
   \int_set:Nn \l_@@_tmpa_int {3}
 }
 {
   \int_set:Nn \l_@@_tmpa_int {1}
 }

 \tl_set_eq:NN \l_@@_tmpe_tl \c_spath_moveto_tl
 \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl }

 \prg_replicate:nn { \l_@@_tmpa_int }
 {
   \@@_tl_put_right_braced:Nx
   \l_@@_tmpe_tl
   {\tl_item:Nn \l_@@_tmpa_tl {1}}
   \@@_tl_put_right_braced:Nx
   \l_@@_tmpe_tl
   {\tl_item:Nn \l_@@_tmpa_tl {2}}
   \tl_put_right:Nx \l_@@_tmpe_tl {\tl_item:Nn \l_@@_tmpa_tl {3}}

   \prg_replicate:nn {3}
   {
     \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl }
   }
 }
 \@@_tl_put_right_braced:Nx
 \l_@@_tmpe_tl
 {\tl_item:Nn \l_@@_tmpa_tl {1}}
 \@@_tl_put_right_braced:Nx
 \l_@@_tmpe_tl
 {\tl_item:Nn \l_@@_tmpa_tl {2}}

 \fp_set:Nn \l_@@_tmpa_fp
 {
   \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {5}}
   -
   \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {2}}
 }

 \fp_set:Nn \l_@@_tmpb_fp
 {
   \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {6}}
   -
   \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {3}}
 }

 \fp_set:Nn \l_@@_tmpc_fp
 {
   sqrt(
   \l_@@_tmpa_fp * \l_@@_tmpa_fp
   +
   \l_@@_tmpb_fp *  \l_@@_tmpb_fp
   )
   *
   \l_@@_tmpa_int
 }

 \fp_compare:nTF
 {
   \l_@@_tmpc_fp > #2
 }
 {

   \fp_set:Nn \l_@@_tmpc_fp
   {
     #2/ \l_@@_tmpc_fp
   }

   \tl_if_eq:NNTF \l_@@_tmpb_tl \c_spath_curvetoa_tl
   {
     \spath_split_curve:NNVV
     \l_@@_tmpc_tl
     \l_@@_tmpd_tl
     \l_@@_tmpe_tl
     \l_@@_tmpc_fp
   }
   {
     \spath_split_line:NNVV
     \l_@@_tmpc_tl
     \l_@@_tmpd_tl
     \l_@@_tmpe_tl
     \l_@@_tmpc_fp
   }

   \prg_replicate:nn {2}
   {
     \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
   }

   \tl_put_left:NV \l_@@_tmpa_tl \l_@@_tmpd_tl

 }
 {

   \tl_put_left:NV \l_@@_tmpa_tl \c_spath_moveto_tl

   \int_compare:nT
   {
     \tl_count:N \l_@@_tmpa_tl > 3
   }
   {
     \dim_set:Nn \l_@@_tmpa_dim {\fp_to_dim:n {#2 - \l_@@_tmpc_fp } }
     \spath_shorten_at_start:NV \l_@@_tmpa_tl \l_@@_tmpa_dim
   }
 }

 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl
 \group_end:
 }
 {
   \tl_gset:Nn \g_@@_output_tl {#1}
 }
}
\cs_new_protected_nopar:Npn \spath_shorten_at_start:Nnn #1#2#3
{
 \@@_shorten_at_start:nn {#2}{#3}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_shorten_at_start:Nnn {NVV, cnn, cVV, NVn}
\cs_new_protected_nopar:Npn \spath_shorten_at_start:Nn #1#2
{
 \spath_shorten_at_start:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_shorten_at_start:Nn {cn, cV, NV}
\cs_new_protected_nopar:Npn \spath_gshorten_at_start:Nnn #1#2#3
{
 \@@_shorten_at_start:nn {#2}{#3}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gshorten_at_start:Nnn {NVV, cnn, cVV, NVn}
\cs_new_protected_nopar:Npn \spath_gshorten_at_start:Nn #1#2
{
 \spath_gshorten_at_start:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_gshorten_at_start:Nn {cn, cV, NV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_shorten_at_both_ends:Nnn,
% \spath_shorten_at_both_ends:Nn,
% \spath_gshorten_at_both_ends:Nnn,
% \spath_gshorten_at_both_ends:Nn
% }
%
% This macro shortens a path from the start by a dimension.
%
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \spath_shorten_at_both_ends:Nnn #1#2#3
{
 \spath_shorten_at_start:Nnn #1{#2}{#3}
 \spath_shorten_at_end:Nnn #1{#2}{#3}
}
\cs_new_protected_nopar:Npn \spath_shorten_at_both_ends:Nn #1#2
{
 \spath_shorten_at_start:Nn #1{#2}
 \spath_shorten_at_end:Nn #1{#2}
}
\cs_generate_variant:Nn \spath_shorten_at_both_ends:Nn {cn, cV, NV}
\cs_new_protected_nopar:Npn \spath_gshorten_at_both_ends:Nnn #1#2#3
{
 \spath_gshorten_at_start:Nnn #1{#2}{#3}
 \spath_gshorten_at_end:Nnn #1{#2}{#3}
}
\cs_new_protected_nopar:Npn \spath_gshorten_at_both_ends:Nn #1#2
{
 \spath_gshorten_at_start:Nn #1{#2}
 \spath_gshorten_at_end:Nn #1{#2}
}
\cs_generate_variant:Nn \spath_gshorten_at_both_ends:Nn {cn, cV, NV}
%    \end{macrocode}
% \end{macro}
%
% \subsection{Points on a Path}
%
% \begin{macro}[internal]{
% \spath_point_at:Nnn,
% \spath_gpoint_at:Nnn,
% }
%
% Get the location of a point on a path, using the same location specification as the intersection library.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_point_at:nn #1#2
{
 \group_begin:
 \int_set:Nn \l_@@_tmpa_int {\fp_to_int:n {floor(#2) + 1}}
 \fp_set:Nn \l_@@_tmpa_fp {#2 - floor(#2)}

 \spath_segments_to_seq:Nn \l_@@_tmpa_seq {#1}

 \int_compare:nTF
 {
   \l_@@_tmpa_int < 1
 }
 {
   \spath_initialpoint:Nn \l_@@_tmpc_tl {#1}
 }
 {
   \int_compare:nTF
   {
     \l_@@_tmpa_int > \seq_count:N \l_@@_tmpa_seq
   }
   {
     \spath_finalpoint:Nn \l_@@_tmpc_tl {#1}
   }
   {

     \tl_set:Nx
     \l_@@_tmpa_tl
     {\seq_item:Nn \l_@@_tmpa_seq { \l_@@_tmpa_int} }

     \int_compare:nTF
     {
       \tl_count:N \l_@@_tmpa_tl > 3
     }
     {
       \tl_set:Nx \l_@@_tmpb_tl {\tl_item:Nn \l_@@_tmpa_tl {4}}
     }
     {
       \tl_set:Nx \l_@@_tmpb_tl {\tl_item:Nn \l_@@_tmpa_tl {1}}
     }

     \tl_clear:N \l_@@_tmpc_tl

     \token_case_meaning:Nn \l_@@_tmpb_tl
     {
       \c_spath_moveto_tl
       {
         \tl_set:Nx \l_@@_tmpc_tl
         {
           {
             \tl_item:Nn \l_@@_tmpa_tl {2}
           }
           {
             \tl_item:Nn \l_@@_tmpa_tl {3}
           }
         }
       }

       \c_spath_lineto_tl
       {
         \tl_set:Nx \l_@@_tmpc_tl
         {
           {\fp_to_dim:n
             {
               (1 - \l_@@_tmpa_fp) * ( \tl_item:Nn \l_@@_tmpa_tl {2} )
               +
               \l_@@_tmpa_fp * ( \tl_item:Nn \l_@@_tmpa_tl {5} )
             }
           }
           {\fp_to_dim:n
             {
               (1 - \l_@@_tmpa_fp) * ( \tl_item:Nn \l_@@_tmpa_tl {3} )
               +
               \l_@@_tmpa_fp * ( \tl_item:Nn \l_@@_tmpa_tl {6} )
             }
           }
         }
       }

       \c_spath_rectsize_tl
       {
         \fp_compare:nTF
         {
           \l_@@_tmpa_fp <= .25
         }
         {
           \tl_set:Nx \l_@@_tmpc_tl
           {
             {\fp_to_dim:n
               {
                 ( \tl_item:Nn \l_@@_tmpa_tl {2} )
                 +
                 4 * \l_@@_tmpa_fp * ( \tl_item:Nn \l_@@_tmpa_tl {5} )
               }
             }
             {\fp_to_dim:n {\tl_item:Nn \l_@@_tmpa_tl {3} } }
           }
         }
         {
           \fp_compare:nTF
           {
             \l_@@_tmpa_fp <= .5
           }
           {
             \tl_set:Nx \l_@@_tmpc_tl
             {
               {\fp_to_dim:n
                 {
                   ( \tl_item:Nn \l_@@_tmpa_tl {2} )
                   +
                   ( \tl_item:Nn \l_@@_tmpa_tl {5} )
                 }
               }
               {\fp_to_dim:n
                 {
                   ( \tl_item:Nn \l_@@_tmpa_tl {3} )
                   +
                   (4 * (\l_@@_tmpa_fp) - 1) * ( \tl_item:Nn \l_@@_tmpa_tl {6} )
                 }
               }
             }
           }
           {
             \fp_compare:nTF
             {
               \l_@@_tmpa_fp <= .75
             }
             {
               \tl_set:Nx \l_@@_tmpc_tl
               {
                 {\fp_to_dim:n
                   {
                     ( \tl_item:Nn \l_@@_tmpa_tl {2} )
                     +
                     (3 - 4 * (\l_@@_tmpa_fp)) *( \tl_item:Nn \l_@@_tmpa_tl {5} )
                   }
                 }
                 {\fp_to_dim:n
                   {
                     ( \tl_item:Nn \l_@@_tmpa_tl {3} )
                     +
                     ( \tl_item:Nn \l_@@_tmpa_tl {6} )
                   }
                 }
               }

             }
             {
               \tl_set:Nx \l_@@_tmpc_tl
               {
                 {\fp_to_dim:n
                   {
                     ( \tl_item:Nn \l_@@_tmpa_tl {2} )
                   }
                 }
                 {\fp_to_dim:n
                   {
                     ( \tl_item:Nn \l_@@_tmpa_tl {3} )
                     +
                     (4 - 4 *(\l_@@_tmpa_fp)) * ( \tl_item:Nn \l_@@_tmpa_tl {6} )
                   }
                 }
               }
             }
           }
         }
       }

       \c_spath_closepath_tl
       {
         \tl_set:Nx \l_@@_tmpc_tl
         {
           {\fp_to_dim:n
             {
               (1 - \l_@@_tmpa_fp) * ( \tl_item:Nn \l_@@_tmpa_tl {2} )
               +
               \l_@@_tmpa_fp * ( \tl_item:Nn \l_@@_tmpa_tl {5} )
             }
           }
           {\fp_to_dim:n
             {
               (1 - \l_@@_tmpa_fp) * ( \tl_item:Nn \l_@@_tmpa_tl {3} )
               +
               \l_@@_tmpa_fp * ( \tl_item:Nn \l_@@_tmpa_tl {6} )
             }
           }
         }
       }

       \c_spath_curvetoa_tl
       {
         \tl_set:Nx \l_@@_tmpc_tl
         {
           {\fp_to_dim:n
             {
               (1 - \l_@@_tmpa_fp)^3 * \tl_item:Nn \l_@@_tmpa_tl {2}
               + 3 * (1 - \l_@@_tmpa_fp)^2 * (\l_@@_tmpa_fp)
               * \tl_item:Nn \l_@@_tmpa_tl {5}
               + 3 * (1 - \l_@@_tmpa_fp) * (\l_@@_tmpa_fp)^2
               * \tl_item:Nn \l_@@_tmpa_tl {8}
               + (\l_@@_tmpa_fp)^3 * \tl_item:Nn \l_@@_tmpa_tl {11}
           }}
           {\fp_to_dim:n
             {
               (1 - \l_@@_tmpa_fp)^3 * \tl_item:Nn \l_@@_tmpa_tl {3}
               + 3 * (1 - \l_@@_tmpa_fp)^2 * (\l_@@_tmpa_fp)
               * \tl_item:Nn \l_@@_tmpa_tl {6}
               + 3 * (1 - \l_@@_tmpa_fp) * (\l_@@_tmpa_fp)^2
               * \tl_item:Nn \l_@@_tmpa_tl {9}
               + (\l_@@_tmpa_fp)^3 * \tl_item:Nn \l_@@_tmpa_tl {12}
           }}
         }
       }
     }
   }
 }

 \tl_gclear:N \g_@@_output_tl
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpc_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_point_at:Nnn #1#2#3
{
 \@@_point_at:nn {#2}{#3}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_point_at:Nnn {NVn, NVV, NnV}
\cs_new_protected_nopar:Npn \spath_gpoint_at:Nnn #1#2#3
{
 \@@_point_at:nn {#2}{#3}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gpoint_at:Nnn {NVn, NVV, NnV}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[internal]{
% \spath_tangent_at:Nnn,
% \spath_gtangent_at:Nnn,
% }
%
% Get the tangent at a point on a path, using the same location specification as the intersection library.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_tangent_at:nn #1#2
{
 \group_begin:
 \int_set:Nn \l_@@_tmpa_int {\fp_to_int:n {floor(#2) + 1}}
 \fp_set:Nn \l_@@_tmpa_fp {#2 - floor(#2)}

 \spath_segments_to_seq:Nn \l_@@_tmpa_seq {#1}

 \int_compare:nTF
 {
   \l_@@_tmpa_int < 1
 }
 {
   \spath_initialpoint:Nn \l_@@_tmpc_tl {#1}
 }
 {
   \int_compare:nTF
   {
     \l_@@_tmpa_int > \seq_count:N \l_@@_tmpa_seq
   }
   {
     \spath_finalpoint:Nn \l_@@_tmpc_tl {#1}
   }
   {

     \tl_set:Nx
     \l_@@_tmpa_tl
     {\seq_item:Nn \l_@@_tmpa_seq { \l_@@_tmpa_int} }

     \int_compare:nTF
     {
       \tl_count:N \l_@@_tmpa_tl > 3
     }
     {
       \tl_set:Nx \l_@@_tmpb_tl {\tl_item:Nn \l_@@_tmpa_tl {4}}
     }
     {
       \tl_set:Nx \l_@@_tmpb_tl {\tl_item:Nn \l_@@_tmpa_tl {1}}
     }

     \tl_clear:N \l_@@_tmpc_tl

     \token_case_meaning:Nn \l_@@_tmpb_tl
     {
       \c_spath_moveto_tl
       {
         \tl_set:Nx \l_@@_tmpc_tl
         {
           {
             \tl_item:Nn \l_@@_tmpa_tl {2}
           }
           {
             \tl_item:Nn \l_@@_tmpa_tl {3}
           }
         }
       }

       \c_spath_lineto_tl
       {
         \tl_set:Nx \l_@@_tmpc_tl
         {
           {\fp_to_dim:n
             {
               ( \tl_item:Nn \l_@@_tmpa_tl {5} )
               -
               ( \tl_item:Nn \l_@@_tmpa_tl {2} )
             }
           }
           {\fp_to_dim:n
             {
               ( \tl_item:Nn \l_@@_tmpa_tl {6} )
               -
               ( \tl_item:Nn \l_@@_tmpa_tl {3} )
             }
           }
         }
       }

       \c_spath_rectsize_tl
       {
         \fp_compare:nTF
         {
           \l_@@_tmpa_fp <= .25
         }
         {
           \tl_set:Nx \l_@@_tmpc_tl
           {
             {\fp_to_dim:n
               {
                 \tl_item:Nn \l_@@_tmpa_tl {5}
               }
             }
             {0pt}
           }
         }
         {
           \fp_compare:nTF
           {
             \l_@@_tmpa_fp <= .5
           }
           {
             \tl_set:Nx \l_@@_tmpc_tl
             {
               {0pt}
               {\fp_to_dim:n
                 {
                   ( \tl_item:Nn \l_@@_tmpa_tl {6} )
                 }
               }
             }
           }
           {
             \fp_compare:nTF
             {
               \l_@@_tmpa_fp <= .75
             }
             {
               \tl_set:Nx \l_@@_tmpc_tl
               {
                 {\fp_to_dim:n
                   {
                     -( \tl_item:Nn \l_@@_tmpa_tl {5} )
                   }
                 }
                 {0pt}
               }

             }
             {
               \tl_set:Nx \l_@@_tmpc_tl
               {
                 {0pt}
                 {\fp_to_dim:n
                   {
                     - ( \tl_item:Nn \l_@@_tmpa_tl {6} )
                   }
                 }
               }
             }
           }
         }
       }

       \c_spath_closepath_tl
       {
         \tl_set:Nx \l_@@_tmpc_tl
         {
           {\fp_to_dim:n
             {
               ( \tl_item:Nn \l_@@_tmpa_tl {5} )
               -
               ( \tl_item:Nn \l_@@_tmpa_tl {2} )
             }
           }
           {\fp_to_dim:n
             {
               ( \tl_item:Nn \l_@@_tmpa_tl {6} )
               -
               ( \tl_item:Nn \l_@@_tmpa_tl {3} )
             }
           }
         }
       }

       \c_spath_curvetoa_tl
       {
         \tl_set:Nx \l_@@_tmpc_tl
         {
           {\fp_to_dim:n
             {
               3*(1 - \l_@@_tmpa_fp)^2 * (\tl_item:Nn \l_@@_tmpa_tl {5}
               - \tl_item:Nn \l_@@_tmpa_tl {2})
               + 6 * (1 - \l_@@_tmpa_fp) * (\l_@@_tmpa_fp) *
               (\tl_item:Nn \l_@@_tmpa_tl {8}
               - \tl_item:Nn \l_@@_tmpa_tl {5})
               + 3*(\l_@@_tmpa_fp)^2 * (\tl_item:Nn \l_@@_tmpa_tl {11}
               - \tl_item:Nn \l_@@_tmpa_tl {8})
             }
           }
           {\fp_to_dim:n
             {
               3*(1 - \l_@@_tmpa_fp)^2 * (\tl_item:Nn \l_@@_tmpa_tl {6}
               - \tl_item:Nn \l_@@_tmpa_tl {3})
               + 6 * (1 - \l_@@_tmpa_fp) * (\l_@@_tmpa_fp) *
               (\tl_item:Nn \l_@@_tmpa_tl {9}
               - \tl_item:Nn \l_@@_tmpa_tl {6})
               + 3*(\l_@@_tmpa_fp)^2 * (\tl_item:Nn \l_@@_tmpa_tl {12}
               - \tl_item:Nn \l_@@_tmpa_tl {9})
           }}
         }
       }
     }
   }
 }

 \tl_gclear:N \g_@@_output_tl
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpc_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_tangent_at:Nnn #1#2#3
{
 \@@_tangent_at:nn {#2}{#3}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_tangent_at:Nnn {NVn, NVV, NnV}
\cs_new_protected_nopar:Npn \spath_gtangent_at:Nnn #1#2#3
{
 \@@_tangent_at:nn {#2}{#3}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gtangent_at:Nnn {NVn, NVV, NnV}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[internal]{
% \spath_transformation_at:Nnn,
% \spath_gtransformation_at:Nnn
% }
% Gets a transformation that will align to a point on the path with the x-axis along the path.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_transformation_at:nn #1#2
{
 \group_begin:
 \tl_clear:N \l_@@_tmpa_tl
 \@@_tangent_at:nn {#1}{#2}
 \tl_set_eq:NN \l_@@_tmpb_tl \g_@@_output_tl
 \fp_set:Nn \l_@@_tmpa_fp
 {
   sqrt(
   (\tl_item:Nn \l_@@_tmpb_tl {1})^2
   +
   (\tl_item:Nn \l_@@_tmpb_tl {2})^2
   )
 }
 \fp_compare:nTF {\l_@@_tmpa_fp = 0}
 {
   \fp_set:Nn \l_@@_tmpa_fp {1}
   \fp_set:Nn \l_@@_tmpb_fp {0}
 }
 {
   \fp_set:Nn \l_@@_tmpb_fp
   { (\tl_item:Nn \l_@@_tmpb_tl {2}) / \l_@@_tmpa_fp }
   \fp_set:Nn \l_@@_tmpa_fp
   { (\tl_item:Nn \l_@@_tmpb_tl {1}) / \l_@@_tmpa_fp }
 }
 \tl_set:Nx \l_@@_tmpa_tl
 {
   { \fp_to_decimal:n { \l_@@_tmpa_fp } }
   { \fp_to_decimal:n { \l_@@_tmpb_fp } }
   { \fp_to_decimal:n {- \l_@@_tmpb_fp } }
   { \fp_to_decimal:n { \l_@@_tmpa_fp } }
 }
 \@@_point_at:nn {#1}{#2}
 \tl_put_right:NV \l_@@_tmpa_tl \g_@@_output_tl
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_transformation_at:Nnn #1#2#3
{
 \@@_transformation_at:nn {#2}{#3}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_transformation_at:Nnn {NVn, NVV, NnV, NvV}
\cs_new_protected_nopar:Npn \spath_gtransformation_at:Nnn #1#2#3
{
 \@@_transformation_at:nn {#2}{#3}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gtransformation_at:Nnn {NVn, NVV, NnV}
%    \end{macrocode}
% \end{macro}
%
% \subsection{Intersection Routines}
%
% Note: I'm not consistent with number schemes.
% The intersection library is 0-based, but the user interface is 1-based (since if we ``count'' in a \Verb+\foreach+ then it starts at 1).
% This should be more consistent.
%
% \begin{macro}[internal]{
% \spath_intersect:NN,
% \spath_intersect:nn,
% }
% Pass two spaths to pgf's intersection routine.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \spath_intersect:NN #1#2
{
 \pgfintersectionofpaths%
 {%
   \pgfsetpath #1
 }{%
   \pgfsetpath #2
 }
}
\cs_new_protected_nopar:Npn \spath_intersect:nn #1#2
{
 \tl_set:Nn \l_@@_intersecta_tl {#1}
 \tl_set:Nn \l_@@_intersectb_tl {#2}
 \spath_intersect:NN \l_@@_intersecta_tl \l_@@_intersectb_tl
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_split_component_at_intersections:Nnn
% }
% Split a component where it intersects a path.
% Key assumption is that the first path is a single component, so if it is closed then the end joins up to the beginning.
% The component is modified but the path is not.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_split_component_at_intersections:nn #1#2
{
 \group_begin:
 \tl_clear:N \l_@@_tmpe_tl
 \seq_clear:N \l_@@_tmpb_seq

 % Find the intersections of these segments
 \tl_set:Nn \l_@@_tmpb_tl {#1}
 \tl_set:Nn \l_@@_tmpc_tl {#2}

 % Remember if the component is closed
 \spath_finalaction:NV \l_@@_tmpa_tl \l_@@_tmpb_tl

 \bool_set:Nn \l_@@_closed_bool
 {
   \tl_if_eq_p:NN \l_@@_tmpa_tl \c_spath_closepath_tl
   ||
   \tl_if_eq_p:NN \l_@@_tmpa_tl \c_spath_rectcorner_tl
 }

 % Open it
 \spath_open:N \l_@@_tmpb_tl

 \spath_reallength:NV \l_@@_tmpa_int \l_@@_tmpb_tl

 % Sort intersections along the component
 \pgfintersectionsortbyfirstpath
 \spath_intersect:NN \l_@@_tmpb_tl \l_@@_tmpc_tl

 % If we get intersections
 \int_compare:nT {\pgfintersectionsolutions > 0}
 {
   % Find the times of the intersections on the component
   \int_step_inline:nnnn {1} {1} {\pgfintersectionsolutions}
   {
     \pgfintersectiongetsolutiontimes{##1}{\l_@@_tmph_tl}{\l_@@_tmpi_tl}
     \seq_put_left:NV \l_@@_tmpb_seq \l_@@_tmph_tl
   }

   \seq_get_left:NN \l_@@_tmpb_seq \l_@@_tmpa_tl
   \fp_compare:nT
   {
     \l_@@_tmpa_tl > \l_@@_tmpa_int - .01
   }
   {
     \bool_set_false:N \l_@@_closed_bool
   }

   \seq_get_right:NN \l_@@_tmpb_seq \l_@@_tmpa_tl
   \fp_compare:nT
   {
     \l_@@_tmpa_tl < .01
   }
   {
     \bool_set_false:N \l_@@_closed_bool
   }

   \tl_set:Nn \l_@@_tmpg_tl {-1}

   \seq_map_inline:Nn \l_@@_tmpb_seq
   {
     \tl_set:Nn \l_@@_tmph_tl {##1}

     \tl_set_eq:NN \l_@@_tmpa_tl \l_@@_tmph_tl
     \int_compare:nT
     {
       \fp_to_int:n {floor( \l_@@_tmph_tl) }
       =
       \fp_to_int:n {floor( \l_@@_tmpg_tl) }
     }
     {
       \tl_set:Nx \l_@@_tmph_tl
       {
         \fp_eval:n {
           floor( \l_@@_tmph_tl )
           +
           ( \l_@@_tmph_tl - floor( \l_@@_tmph_tl) )
           /
           ( \l_@@_tmpg_tl - floor( \l_@@_tmpg_tl) )
         }
       }
     }
     \tl_set_eq:NN \l_@@_tmpg_tl \l_@@_tmpa_tl

     \spath_split_at:NNVV
     \l_@@_tmpd_tl
     \l_@@_tmpf_tl
     \l_@@_tmpb_tl
     \l_@@_tmph_tl

     \tl_put_left:NV \l_@@_tmpe_tl \l_@@_tmpf_tl
     \tl_set_eq:NN \l_@@_tmpb_tl \l_@@_tmpd_tl

   }


   \tl_put_left:NV \l_@@_tmpe_tl \l_@@_tmpb_tl

   \spath_remove_empty_components:N \l_@@_tmpe_tl

   \tl_set_eq:NN \l_@@_tmpb_tl \l_@@_tmpe_tl
 }


 \bool_if:NT \l_@@_closed_bool
 {
   \spath_join_component:Nn \l_@@_tmpb_tl {1}
 }

 \tl_gclear:N \g_@@_output_tl
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl

 \group_end:
}
\cs_new_protected_nopar:Npn \spath_split_component_at_intersections:Nnn #1#2#3
{
 \@@_split_component_at_intersections:nn {#2}{#3}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_split_component_at_intersections:Nnn {NVn, NVV}
\cs_new_protected_nopar:Npn \spath_split_component_at_intersections:Nn #1#2
{
 \spath_split_component_at_intersections:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_split_component_at_intersections:Nn {cn, cv}
\cs_new_protected_nopar:Npn \spath_gsplit_component_at_intersections:Nnn #1#2#3
{
 \@@_split_component_at_intersections:nn {#2}{#3}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gsplit_component_at_intersections:Nnn {NVn, NVV}
\cs_new_protected_nopar:Npn \spath_gsplit_component_at_intersections:Nn #1#2
{
 \spath_gsplit_component_at_intersections:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_gsplit_component_at_intersections:Nn {cn, cv}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[internal]{
% \spath_split_path_at_intersections:Nnn,
% \spath_split_path_at_intersections:Nn,
% \spath_gsplit_path_at_intersections:Nnn,
% \spath_gsplit_path_at_intersections:Nn,
% \spath_split_at_intersections:NNnn,
% \spath_split_at_intersections:NN,
% \spath_gsplit_at_intersections:NNnn,
% \spath_gsplit_at_intersections:NN,
% }
% Split paths at their intersections.
% The \Verb+path+ versions only split the first path.
% The others split both paths.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_split_path_at_intersections:nn #1#2
{
 \group_begin:

 \seq_clear:N \l_@@_tmpa_seq
 \seq_clear:N \l_@@_tmpb_seq

 \spath_components_to_seq:Nn \l_@@_tmpa_seq {#1}
 \seq_map_inline:Nn \l_@@_tmpa_seq
 {
   \spath_split_component_at_intersections:Nnn \l_@@_tmpa_tl {##1} {#2}
   \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpa_tl
 }

 \tl_gclear:N \g_@@_output_tl
 \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpb_seq {} }
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_split_path_at_intersections:Nnn #1#2#3
{
 \@@_split_path_at_intersections:nn {#2}{#3}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_split_path_at_intersections:Nnn
{NVn, NVV, cVn, cVV, cvn, cvv}
\cs_new_protected_nopar:Npn \spath_split_path_at_intersections:Nn #1#2
{
 \spath_split_path_at_intersections:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_split_path_at_intersections:Nn {cv, NV}
\cs_new_protected_nopar:Npn \spath_gsplit_path_at_intersections:Nnn #1#2#3
{
 \@@_split_path_at_intersections:nn {#2}{#3}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gsplit_path_at_intersections:Nnn
{NVn, NVV, cVn, cVV, cvn, cvv}
\cs_new_protected_nopar:Npn \spath_gsplit_path_at_intersections:Nn #1#2
{
 \spath_gsplit_path_at_intersections:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_gsplit_path_at_intersections:Nn {cv, NV}
\cs_new_protected_nopar:Npn \spath_split_at_intersections:NNnn #1#2#3#4
{
 \@@_split_path_at_intersections:nn {#3}{#4}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \@@_split_path_at_intersections:nn {#4}{#3}
 \tl_set_eq:NN #2 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_split_at_intersections:NNnn
{NNVn, NNVV, ccVn, ccVV, ccvn, ccvv}
\cs_new_protected_nopar:Npn \spath_split_at_intersections:NN #1#2
{
 \spath_split_at_intersections:NNVV #1#2#1#2
}
\cs_generate_variant:Nn \spath_split_at_intersections:NN {cc}
\cs_new_protected_nopar:Npn \spath_gsplit_at_intersections:NNnn #1#2#3#4
{
 \@@_split_path_at_intersections:nn {#3}{#4}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \@@_split_path_at_intersections:nn {#4}{#3}
 \tl_gset_eq:NN #2 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gsplit_at_intersections:NNnn
{NNVn, NNVV, ccVn, ccVV, ccvn, ccvv}
\cs_new_protected_nopar:Npn \spath_gsplit_at_intersections:NN #1#2
{
 \spath_gsplit_at_intersections:NNVV #1#2#1#2
}
\cs_generate_variant:Nn \spath_gsplit_at_intersections:NN {cc}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[internal]{
% \spath_split_component_at_self_intersections:Nn,
% \spath_split_component_at_self_intersections:N,
% \spath_gsplit_component_at_self_intersections:Nn,
% \spath_gsplit_component_at_self_intersections:N,
% }
% Given a component of a path, split it at points where it self-intersects.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_split_component_at_self_intersections:n #1
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpe_tl {#1}

 % Remember if the component is closed
 \spath_finalaction:NV \l_@@_tmpa_tl \l_@@_tmpe_tl

 \bool_set:Nn \l_@@_closed_bool
 {
   \tl_if_eq_p:NN \l_@@_tmpa_tl \c_spath_closepath_tl
 }

 % Copy the path
 \tl_set:Nn \l_@@_tmpe_tl {#1}

 % Open the path
 \spath_open:N \l_@@_tmpe_tl
 % Ensure beziers don't self-intersect
 \spath_split_curves:N \l_@@_tmpe_tl

 % Make a copy for later
 \tl_set_eq:NN \l_@@_tmpg_tl \l_@@_tmpe_tl

 % Clear some token lists and sequences
 \tl_clear:N \l_@@_tmpd_tl
 \seq_clear:N \l_@@_tmpb_seq
 \int_zero:N \l_@@_tmpa_int

 \pgfintersectionsortbyfirstpath

 % Split the path into a sequence of segments
 \spath_segments_to_seq:NV \l_@@_tmpa_seq \l_@@_tmpe_tl

 \seq_map_indexed_inline:Nn \l_@@_tmpa_seq
 {
   \seq_map_indexed_inline:Nn \l_@@_tmpa_seq
   {
     % Don't intersect a segment with itself
     \int_compare:nF
     {
       ##1 == ####1
     }
     {
       \spath_intersect:nn {##2} {####2}

       \int_compare:nT {\pgfintersectionsolutions > 0}
       {
         % Find the times of the intersections on each path
         \int_step_inline:nnnn {1} {1} {\pgfintersectionsolutions}
         {
           \pgfintersectiongetsolutiontimes
           {########1}{\l_@@_tmpb_tl}{\l_@@_tmpc_tl}

           \bool_if:nT
           {
             !(
             \fp_compare_p:n { \l_@@_tmpb_tl > .99 }
             &&
             \int_compare_p:n {##1 + 1 == ####1}
             )
             &&
             !(
             \fp_compare_p:n { \l_@@_tmpb_tl < .01 }
             &&
             \int_compare_p:n {##1 - 1 == ####1}
             )
             &&
             !(
             \l_@@_closed_bool
             &&
             \fp_compare_p:n { \l_@@_tmpb_tl < .01 }
             &&
             \int_compare_p:n {##1 == 1}
             &&
             \int_compare_p:n {\seq_count:N \l_@@_tmpa_seq == ####1}
             )
             &&
             !(
             \l_@@_closed_bool
             &&
             \fp_compare_p:n { \l_@@_tmpb_tl > .99 }
             &&
             \int_compare_p:n {####1 == 1}
             &&
             \int_compare_p:n {\seq_count:N \l_@@_tmpa_seq == ##1}
             )
           }
           {
             \tl_set:Nx \l_@@_tmpa_tl
             {\fp_to_decimal:n {\l_@@_tmpb_tl +  ##1 - 1}}
             \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpa_tl
           }
         }
       }
     }
   }
 }

 % Sort the sequence by reverse order along the path
 \seq_sort:Nn \l_@@_tmpb_seq
 {
   \fp_compare:nNnTF { ##1 } < { ##2 }
   { \sort_return_swapped: }
   { \sort_return_same: }
 }

 \seq_get_left:NN \l_@@_tmpb_seq \l_@@_tmpa_tl
 \fp_compare:nT
 {
   \l_@@_tmpa_tl > \seq_count:N \l_@@_tmpa_seq - .01
 }
 {
   \bool_set_false:N \l_@@_closed_bool
 }
 \seq_get_right:NN \l_@@_tmpb_seq \l_@@_tmpa_tl
 \fp_compare:nT
 {
   \l_@@_tmpa_tl < .01
 }
 {
   \bool_set_false:N \l_@@_closed_bool
 }

 % Restore the original copy of the path
 \tl_set_eq:NN \l_@@_tmpe_tl \l_@@_tmpg_tl

 % Clear the token lists
 \tl_clear:N \l_@@_tmpf_tl
 \tl_clear:N \l_@@_tmph_tl
 \tl_clear:N \l_@@_tmpg_tl

 \tl_set:Nn \l_@@_tmpi_tl {-1}

 \seq_map_inline:Nn \l_@@_tmpb_seq
 {
   \tl_set:Nn \l_@@_tmpb_tl {##1}
   \tl_set_eq:NN \l_@@_tmpa_tl \l_@@_tmpb_tl
   \int_compare:nT
   {
     \fp_to_int:n {floor( \l_@@_tmpb_tl ) }
     =
     \fp_to_int:n {floor( \l_@@_tmpi_tl) }
   }
   {
     \tl_set:Nx \l_@@_tmpb_tl
     {
       \fp_eval:n {
         floor( \l_@@_tmpb_tl )
         +
         ( \l_@@_tmpb_tl - floor( \l_@@_tmpb_tl) )
         /
         ( \l_@@_tmpi_tl - floor( \l_@@_tmpi_tl) )
       }
     }
   }
   \tl_set_eq:NN \l_@@_tmpi_tl \l_@@_tmpa_tl

   \spath_split_at:NNVV
   \l_@@_tmpf_tl
   \l_@@_tmph_tl
   \l_@@_tmpe_tl
   \l_@@_tmpb_tl

   \tl_put_left:NV \l_@@_tmpg_tl \l_@@_tmph_tl
   \tl_set_eq:NN \l_@@_tmpe_tl \l_@@_tmpf_tl

 }

 \tl_put_left:NV \l_@@_tmpg_tl \l_@@_tmpe_tl

 \tl_if_empty:NT \l_@@_tmpg_tl
 {
   \tl_set_eq:NN \l_@@_tmpg_tl \l_@@_tmpe_tl
 }

 \spath_remove_empty_components:N \l_@@_tmpg_tl

 % Do something with closed
 \bool_if:NT \l_@@_closed_bool
 {
   \spath_join_component:Nn \l_@@_tmpg_tl {1}
 }

 \tl_gclear:N \g_@@_output_tl
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpg_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_split_component_at_self_intersections:Nn #1#2
{
 \@@_split_component_at_self_intersections:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_split_component_at_self_intersections:Nn {NV}
\cs_new_protected_nopar:Npn \spath_split_component_at_self_intersections:N #1
{
 \spath_split_component_at_self_intersections:NV #1#1
}
\cs_generate_variant:Nn \spath_split_component_at_self_intersections:N {c}
\cs_new_protected_nopar:Npn \spath_gsplit_component_at_self_intersections:Nn #1#2
{
 \@@_split_component_at_self_intersections:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gsplit_component_at_self_intersections:Nn {NV}
\cs_new_protected_nopar:Npn \spath_gsplit_component_at_self_intersections:N #1
{
 \spath_gsplit_component_at_self_intersections:NV #1#1
}
\cs_generate_variant:Nn \spath_gsplit_component_at_self_intersections:N {c}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_split_at_self_intersections:Nn,
% \spath_split_at_self_intersections:N,
% \spath_gsplit_at_self_intersections:Nn,
% \spath_gsplit_at_self_intersections:N,
% }
% Split a path at its self intersections.
% We iterate over the components, splitting each where it meets all the others and itself.
% To make this more efficient, we split against the components of the original path rather than updating each time.
%
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_split_at_self_intersections:n #1
{
 \group_begin:
 \spath_components_to_seq:Nn \l_@@_tmpa_seq {#1}
 \seq_clear:N \l_@@_tmpb_seq
 \seq_clear:N \l_@@_tmpc_seq

 % Iterate over the components of the original path.
 \bool_do_until:nn
 {
   \seq_if_empty_p:N \l_@@_tmpa_seq
 }
 {
   % Get the next component
   \seq_pop_left:NN \l_@@_tmpa_seq \l_@@_tmpa_tl
   % Copy for later
   \tl_set_eq:NN \l_@@_tmpc_tl \l_@@_tmpa_tl
   \int_compare:nT
   {
     \tl_count:N \l_@@_tmpa_tl > 3
   }
   {

     % Split against itself
     \spath_split_component_at_self_intersections:N \l_@@_tmpa_tl
     % Grab the rest of the path
     \tl_set:Nx \l_@@_tmpb_tl
     {
       \seq_use:Nn \l_@@_tmpb_seq {}
       \seq_use:Nn \l_@@_tmpa_seq {}
     }
     % Split against the rest of the path
     \spath_split_path_at_intersections:NV \l_@@_tmpa_tl \l_@@_tmpb_tl
   }
   % Save the split path
   \seq_put_right:NV \l_@@_tmpc_seq \l_@@_tmpa_tl
   % Add the original copy to the sequence of processed components
   \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpc_tl
 }

 \tl_gclear:N \g_@@_output_tl
 \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpc_seq {} }
 \group_end:
}
\cs_generate_variant:Nn \@@_split_at_self_intersections:n {V, v}
\cs_new_protected_nopar:Npn \spath_split_at_self_intersections:Nn #1#2
{
 \@@_split_at_self_intersections:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_split_at_self_intersections:Nn {NV, cn, cV, cv}
\cs_new_protected_nopar:Npn \spath_split_at_self_intersections:N #1
{
 \spath_split_at_self_intersections:NV #1#1
}
\cs_generate_variant:Nn \spath_split_at_self_intersections:N {c}
\cs_new_protected_nopar:Npn \spath_gsplit_at_self_intersections:Nn #1#2
{
 \@@_split_at_self_intersections:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gsplit_at_self_intersections:Nn {NV, cn, cV, cv}
\cs_new_protected_nopar:Npn \spath_gsplit_at_self_intersections:N #1
{
 \spath_gsplit_at_self_intersections:NV #1#1
}
\cs_generate_variant:Nn \spath_gsplit_at_self_intersections:N {c}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_join_component:Nnn,
% \spath_join_component:Nn,
% \spath_gjoin_component:Nnn,
% \spath_gjoin_component:Nn,
% }
% Join the specified component of the spath to its predecessor.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_join_component:nn #1#2
{
 \group_begin:
 \spath_numberofcomponents:Nn \l_@@_tmpa_int {#1}

 \bool_if:nTF
 {
   \int_compare_p:n { #2 >= 1 }
   &&
   \int_compare_p:n { #2 <= \l_@@_tmpa_int }
 }
 {
   \int_compare:nTF
   {
     #2 == 1
   }
   {
     \int_compare:nTF
     {
       \l_@@_tmpa_int == 1
     }
     {
       \tl_set:Nn \l_@@_tmpa_tl {#1}
       \spath_initialpoint:Nn \l_@@_tmpb_tl {#1}
       \tl_put_right:NV \l_@@_tmpa_tl \c_spath_closepath_tl
       \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpb_tl
       \tl_gclear:N \g_@@_output_tl
       \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl
     }
     {
       \spath_components_to_seq:Nn \l_@@_tmpa_seq {#1}
       \seq_pop_left:NN \l_@@_tmpa_seq \l_@@_tmpa_tl

       \prg_replicate:nn {3}
       {
         \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
       }

       \seq_put_right:NV \l_@@_tmpa_seq \l_@@_tmpa_tl

       \tl_gclear:N \g_@@_output_tl
       \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpa_seq {}}
     }
   }
   {
     \spath_components_to_seq:Nn \l_@@_tmpa_seq {#1}

     \seq_clear:N \l_@@_tmpb_seq
     \seq_map_indexed_inline:Nn \l_@@_tmpa_seq
     {
       \tl_set:Nn \l_@@_tmpa_tl {##2}
       \int_compare:nT {##1 = #2}
       {
         \prg_replicate:nn {3}
         {
           \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
         }
       }
       \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpa_tl
     }

     \tl_gclear:N \g_@@_output_tl
     \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpb_seq {}}
   }
 }
 {
   \tl_gclear:N \g_@@_output_tl
   \tl_gset:Nn \g_@@_output_tl {#1}
 }

 \group_end:
}
\cs_new_protected_nopar:Npn \spath_join_component:Nnn #1#2#3
{
 \@@_join_component:nn {#2}{#3}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_join_component:Nnn {NVn, NVV}
\cs_new_protected_nopar:Npn \spath_join_component:Nn #1#2
{
 \spath_join_component:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_join_component:Nn {cn, NV, cV}
\cs_new_protected_nopar:Npn \spath_gjoin_component:Nnn #1#2#3
{
 \@@_join_component:nn {#2}{#3}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_gjoin_component:Nnn {NVn, NVV}
\cs_new_protected_nopar:Npn \spath_gjoin_component:Nn #1#2
{
 \spath_gjoin_component:NVn #1#1{#2}
}
\cs_generate_variant:Nn \spath_gjoin_component:Nn {cn, NV, cV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_spot_weld_components:Nn,
% \spath_spot_weld_components:N,
% \spath_spot_gweld_components:Nn,
% \spath_spot_gweld_components:N,
% }
% Weld together any components where the last point of one is at the start point of the next (within a tolerance).
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_spot_weld_components:n #1
{
 \group_begin:
 \dim_zero:N \l_@@_move_x_dim
 \dim_zero:N \l_@@_move_y_dim

 \spath_components_to_seq:Nn \l_@@_tmpa_seq {#1}
 \seq_clear:N \l_@@_tmpb_seq
 \dim_set:Nn \l_@@_move_x_dim {\tl_item:nn {#1} {2} + 10 pt}
 \dim_set:Nn \l_@@_move_y_dim {\tl_item:nn {#1} {3} + 10 pt}

 \int_set:Nn \l_@@_tmpa_int {\seq_count:N \l_@@_tmpa_seq}

 \seq_map_inline:Nn \l_@@_tmpa_seq
 {
   \tl_set:Nn \l_@@_tmpa_tl {##1}
   \bool_if:nT
   {
     \dim_compare_p:n
     {
       \dim_abs:n
       {\l_@@_move_x_dim - \tl_item:Nn \l_@@_tmpa_tl {2} }
       < 0.01pt
     }
     &&
     \dim_compare_p:n
     {
       \dim_abs:n
       {\l_@@_move_y_dim - \tl_item:Nn \l_@@_tmpa_tl {3} }
       < 0.01pt
     }
   }
   {
     \prg_replicate:nn {3}
     {
       \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
     }
     \int_decr:N \l_@@_tmpa_int
   }
   \tl_reverse:N \l_@@_tmpa_tl
   \dim_set:Nn \l_@@_move_x_dim {\tl_item:Nn \l_@@_tmpa_tl {2}}
   \dim_set:Nn \l_@@_move_y_dim {\tl_item:Nn \l_@@_tmpa_tl {1}}
   \tl_reverse:N \l_@@_tmpa_tl
   \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpa_tl
 }

 \tl_set:Nx \l_@@_tmpa_tl {\seq_use:Nn \l_@@_tmpb_seq {} }
 \spath_components_to_seq:NV \l_@@_tmpb_seq \l_@@_tmpa_tl


 \spath_initialpoint:Nn \l_@@_tmpa_tl {#1}
 \spath_finalpoint:Nn \l_@@_tmpb_tl {#1}

 \bool_if:nT
 {
   \dim_compare_p:n
   {
     \dim_abs:n
     {
       \tl_item:Nn \l_@@_tmpa_tl {1} - \tl_item:Nn \l_@@_tmpb_tl {1}
     }
     <
     0.01pt
   }
   &&
   \dim_compare_p:n
   {
     \dim_abs:n
     {
       \tl_item:Nn \l_@@_tmpa_tl {2} - \tl_item:Nn \l_@@_tmpb_tl {2}
     }
     <
     0.01pt
   }
 }
 {
   \int_compare:nTF
   {
     \seq_count:N \l_@@_tmpb_seq > 1
   }
   {
     \seq_pop_left:NN \l_@@_tmpb_seq \l_@@_tmpb_tl

     \prg_replicate:nn {3}
     {
       \tl_set:Nx \l_@@_tmpb_tl {\tl_tail:N \l_@@_tmpb_tl}
     }
     \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpb_tl
   }
   {
     \tl_set:NV \l_@@_tmpb_tl \c_spath_closepath_tl
     \tl_put_right:Nx \l_@@_tmpb_tl
     {
       { \tl_item:Nn \l_@@_tmpa_tl {1} }
       { \tl_item:Nn \l_@@_tmpa_tl {2} }
     }
     \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpb_tl
   }
 }

 \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpb_seq {}}
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_spot_weld_components:Nn #1#2
{
 \@@_spot_weld_components:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_spot_weld_components:Nn {NV, cV, cn}
\cs_new_protected_nopar:Npn \spath_spot_weld_components:N #1
{
 \spath_spot_weld_components:NV #1#1
}
\cs_generate_variant:Nn \spath_spot_weld_components:N {c}
\cs_new_protected_nopar:Npn \spath_spot_gweld_components:Nn #1#2
{
 \@@_spot_weld_components:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \spath_spot_gweld_components:Nn {NV, cV, cn}
\cs_new_protected_nopar:Npn \spath_spot_gweld_components:N #1
{
 \spath_spot_gweld_components:NV #1#1
}
\cs_generate_variant:Nn \spath_spot_gweld_components:N {c}
%    \end{macrocode}
% \end{macro}
%
% \subsection{Exporting Commands}
%
% \begin{macro}[internal]{
% \spath_convert_to_svg:Nn,
% \spath_gconvert_to_svg:Nn,
% }
% Convert the soft path to an SVG document.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_convert_to_svg:n #1
{
 \group_begin:
 \tl_clear:N \l_@@_tmpa_tl
 \tl_put_right:Nn \l_@@_tmpa_tl
 {
   <?xml~ version="1.0"~ standalone="no"?>
   \iow_newline:
   <!DOCTYPE~ svg~ PUBLIC~ "-//W3C//DTD SVG 1.1//EN"~
   "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
   \iow_newline:
   <svg~ xmlns="http://www.w3.org/2000/svg"~ version="1.1"~viewBox="
 }

 \spath_minbb:Nn \l_@@_tmpb_tl {#1}
 \spath_maxbb:Nn \l_@@_tmpc_tl {#1}
 \tl_put_right:Nx \l_@@_tmpa_tl
 {
   \dim_to_decimal:n
   {
     \tl_item:Nn \l_@@_tmpb_tl {1} - 10pt
   }
   \exp_not:n {~}
   \dim_to_decimal:n
   {
     \tl_item:Nn \l_@@_tmpb_tl {2} - 10pt
   }
   \exp_not:n {~}
   \dim_to_decimal:n
   {
     \tl_item:Nn \l_@@_tmpc_tl {1}
     -
     \tl_item:Nn \l_@@_tmpb_tl {1}
     + 20pt
   }
   \exp_not:n {~}
   \dim_to_decimal:n
   {
     \tl_item:Nn \l_@@_tmpc_tl {2}
     -
     \tl_item:Nn \l_@@_tmpb_tl {2}
     + 20pt
   }
 }

 \tl_put_right:Nn \l_@@_tmpa_tl
 {
   ">
   \iow_newline:
   <path~ d="
 }
 \tl_set:Nn \l_@@_tmpc_tl {use:n}
 \tl_map_inline:nn {#1}
 {
   \tl_set:Nn \l_@@_tmpb_tl {##1}
   \token_case_meaning:NnF \l_@@_tmpb_tl
   {
     \c_spath_moveto_tl
     {
       \tl_put_right:Nn \l_@@_tmpa_tl {M~}
       \tl_set:Nn \l_@@_tmpc_tl {use:n}
     }
     \c_spath_lineto_tl
     {
       \tl_put_right:Nn \l_@@_tmpa_tl {L~}
       \tl_set:Nn \l_@@_tmpc_tl {use:n}
     }
     \c_spath_closepath_tl
     {
       \tl_put_right:Nn \l_@@_tmpa_tl {Z~}
       \tl_set:Nn \l_@@_tmpc_tl {use_none:n}
     }
     \c_spath_curvetoa_tl
     {
       \tl_put_right:Nn \l_@@_tmpa_tl {C~}
       \tl_set:Nn \l_@@_tmpc_tl {use:n}
     }
     \c_spath_curvetob_tl {
       \tl_set:Nn \l_@@_tmpc_tl {use:n}
     }
     \c_spath_curveto_tl {
       \tl_set:Nn \l_@@_tmpc_tl {use:n}
     }
   }
   {
     \tl_put_right:Nx
     \l_@@_tmpa_tl
     {\use:c { \l_@@_tmpc_tl } {\dim_to_decimal:n {##1}} ~}
   }
 }
 \tl_put_right:Nn \l_@@_tmpa_tl
 {
   "~ fill="none"~ stroke="black"~ />
   \iow_newline:
   </svg>
   \iow_newline:
 }
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \spath_convert_to_svg:Nn #1#2
{
 \@@_convert_to_svg:n {#2}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_new_protected_nopar:Npn \spath_gconvert_to_svg:Nn #1#2
{
 \@@_convert_to_svg:n {#2}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_export_to_svg:nn
% }
% Save a soft path to an SVG file.
%    \begin{macrocode}
\iow_new:N \g_@@_stream
\cs_new_protected_nopar:Npn \spath_export_to_svg:nn #1#2
{
 \group_begin:
 \spath_convert_to_svg:Nn \l_@@_tmpa_tl {#2}
 \iow_open:Nn \g_@@_stream {#1 .svg}
 \iow_now:Nx \g_@@_stream
 {
   \tl_use:N \l_@@_tmpa_tl
 }
 \iow_close:N \g_@@_stream
 \group_end:
}
\cs_generate_variant:Nn \spath_export_to_svg:nn {nv, nV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\spath_show:n}
% Displays the soft path on the terminal.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \spath_show:n #1
{
 \int_step_inline:nnnn {1} {3} {\tl_count:n {#1}}
 {
   \iow_term:x {
     \tl_item:nn {#1} {##1}
     {\tl_item:nn {#1} {##1+1}}
     {\tl_item:nn {#1} {##1+2}}
   }
 }
}
\cs_generate_variant:Nn \spath_show:n {V, v}
%    \end{macrocode}
% \end{macro}
%
% \subsection{PGF and TikZ Interface Functions}
%
% Spaths come from PGF so we need some functions that get and set spaths from the pgf system.
%
% \begin{macro}[internal]{
% \spath_get_current_path:N,
% \spath_gget_current_path:N
% }
% Grab the current soft path from PGF.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \spath_get_current_path:N #1
{
 \pgfsyssoftpath@getcurrentpath #1
}
\cs_generate_variant:Nn \spath_get_current_path:N {c}
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \spath_gget_current_path:N #1
{
 \pgfsyssoftpath@getcurrentpath #1
 \tl_gset_eq:NN #1 #1
}
\cs_generate_variant:Nn \spath_gget_current_path:N {c}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\spath_protocol_path:n}
% This feeds the bounding box of the soft path to PGF to ensure that its current bounding box contains the soft path.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \spath_protocol_path:n #1
{
 \spath_minbb:Nn \l_@@_tmpa_tl {#1}
 \dim_set:Nn \l_@@_tmpa_dim {\tl_item:Nn \l_@@_tmpa_tl {1}}
 \dim_set:Nn \l_@@_tmpb_dim {\tl_item:Nn \l_@@_tmpa_tl {2}}
 \pgf@protocolsizes\l_@@_tmpa_dim\l_@@_tmpb_dim

 \spath_maxbb:Nn \l_@@_tmpa_tl {#1}
 \dim_set:Nn \l_@@_tmpa_dim {\tl_item:Nn \l_@@_tmpa_tl {1}}
 \dim_set:Nn \l_@@_tmpb_dim {\tl_item:Nn \l_@@_tmpa_tl {2}}
 \pgf@protocolsizes\l_@@_tmpa_dim\l_@@_tmpb_dim
}
\cs_generate_variant:Nn \spath_protocol_path:n {V}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_set_current_path:n,
% \spath_set_current_path:N
% }
% Sets the current path to the specified soft path.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \spath_set_current_path:n #1
{
 \spath_protocol_path:n {#1}
 \tl_set:Nn \l_@@_tmpa_tl {#1}
 \pgfsyssoftpath@setcurrentpath\l_@@_tmpa_tl
}
\cs_new_protected_nopar:Npn \spath_set_current_path:N #1
{
 \spath_protocol_path:V #1
 \pgfsyssoftpath@setcurrentpath #1
}
\cs_generate_variant:Nn \spath_set_current_path:N {c}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \spath_use_path:nn
% }
% Uses the given soft path at the PGF level.
%    \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}[internal]{\spath_tikz_path:nn}
% Uses the given soft path at the TikZ level.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \spath_tikz_path:nn #1#2
{
 \tl_if_empty:nF {#2}
 {
   \path[#1] \pgfextra{
     \spath_set_current_path:n {#2}
     \tl_put_left:Nn \tikz@preactions {\def\tikz@actions@path{#2}}
   };
 }
}
\cs_generate_variant:Nn \spath_tikz_path:nn {Vn, VV, nv, Vv, nV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\spath_set_tikz_data:n}
% Sets the \Verb+\tikz@lastx+ and other coordinates from the soft path.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \spath_set_tikz_data:n #1
{
 \spath_finalpoint:Nn \l_@@_tmpa_tl {#1}
 \tl_set:Nx \l_@@_tmpa_tl
 {
   \exp_not:c {pgf@x}=\tl_item:Nn \l_@@_tmpa_tl {1} \relax
   \exp_not:c {pgf@y}=\tl_item:Nn \l_@@_tmpa_tl {2} \relax
 }
 \use:c {pgf@process}{%
   \tl_use:N \l_@@_tmpa_tl
   \pgftransforminvert
   \use:c {pgf@pos@transform@glob}
 }
 \tl_set:Nx \l_@@_tmpa_tl
 {
   \exp_not:c {tikz@lastx}=\exp_not:c {pgf@x} \relax
   \exp_not:c {tikz@lasty}=\exp_not:c {pgf@y} \relax
   \exp_not:c {tikz@lastxsaved}=\exp_not:c {pgf@x} \relax
   \exp_not:c {tikz@lastysaved}=\exp_not:c {pgf@y} \relax
 }
 \tl_use:N \l_@@_tmpa_tl
 \spath_finalmovepoint:Nn \l_@@_tmpa_tl {#1}
 \bool_if:NT \l_spath_movetorelevant_bool
 {
   \ifpgfsyssoftpathmovetorelevant%
   \tl_gset_eq:cN {pgfsyssoftpath@lastmoveto} \l_@@_tmpa_tl
   \fi
 }
 \tl_set:Nx \l_@@_tmpa_tl
 {
   \exp_not:c {pgf@x}=\tl_item:Nn \l_@@_tmpa_tl {1} \relax
   \exp_not:c {pgf@y}=\tl_item:Nn \l_@@_tmpa_tl {2} \relax
 }
 \use:c {pgf@process}{%
   \tl_use:N \l_@@_tmpa_tl
   \pgftransforminvert
   \use:c {pgf@pos@transform@glob}
 }
 \bool_if:NT \l_spath_movetorelevant_bool
 {
   \dim_if_exist:cT {tikz@lastmovetox}
   {
     \tl_set:Nx \l_@@_tmpa_tl
     {
       \exp_not:c {tikz@lastmovetox}=\exp_not:c {pgf@x} \relax
       \exp_not:c {tikz@lastmovetoy}=\exp_not:c {pgf@y} \relax
     }
     \tl_use:N \l_@@_tmpa_tl
   }
 }
 \tl_clear_new:c {tikz@timer}
 \tl_set:cn {tikz@timer}
 {
   \pgftransformreset
   \spath_reallength:Nn \l_@@_tmpa_int {#1}
   \tl_set_eq:Nc \l_@@_tmpb_tl {tikz@time}
   \tl_set:Nx \l_@@_tmpb_tl
   {\fp_to_decimal:n {(\l_@@_tmpb_tl) * (\l_@@_tmpa_int)}}
   \spath_transformation_at:NnV \l_@@_tmpc_tl {#1} \l_@@_tmpb_tl

   \tl_set:Nx \l_@@_tmpa_tl
   {
     \exp_not:N \pgfpoint
     { \tl_item:Nn \l_@@_tmpc_tl {5} }
     { \tl_item:Nn \l_@@_tmpc_tl {6} }
   }
   \exp_args:NV \pgftransformshift \l_@@_tmpa_tl

   \ifpgfresetnontranslationattime
   \pgftransformresetnontranslations
   \fi

   \ifpgfslopedattime

   \tl_set:Nx \l_@@_tmpa_tl
   {
     { \tl_item:Nn \l_@@_tmpc_tl {1} }
     { \tl_item:Nn \l_@@_tmpc_tl {2} }
     { \tl_item:Nn \l_@@_tmpc_tl {3} }
     { \tl_item:Nn \l_@@_tmpc_tl {4} }
   }
   \ifpgfallowupsidedownattime
   \else
   \fp_compare:nT { \tl_item:Nn \l_@@_tmpc_tl {4} < 0}
   {
     \tl_set:Nx \l_@@_tmpa_tl
     {
       { \fp_eval:n { - (\tl_item:Nn \l_@@_tmpc_tl {1})} }
       { \fp_eval:n { - (\tl_item:Nn \l_@@_tmpc_tl {2})} }
       { \fp_eval:n { - (\tl_item:Nn \l_@@_tmpc_tl {3})} }
       { \fp_eval:n { - (\tl_item:Nn \l_@@_tmpc_tl {4})} }
     }
   }
   \fi
   \tl_put_right:Nn \l_@@_tmpa_tl {{\pgfpointorigin}}
   \exp_last_unbraced:NV \pgftransformcm \l_@@_tmpa_tl
   \fi
 }
}
\cs_generate_variant:Nn \spath_set_tikz_data:n {V, v}
%    \end{macrocode}
% \end{macro}
%
% \iffalse
%</spath3>
% \fi
%
% \section{The TikZ interface}
%
% \iffalse
%<*tikzspath3>
% \fi
%
%    \begin{macrocode}
%<@@=tikzspath>
%    \end{macrocode}
%
%
% This provides an interface to the soft path manipulation routines via a series of TikZ keys.
% They all live in the \texttt{spath} family.
%
%
%     \begin{macrocode}
\RequirePackage{spath3}
\RequirePackage{expl3}
\ExplSyntaxOn

\tl_new:N \l_@@_tmpa_tl
\tl_new:N \l_@@_tmpb_tl
\tl_new:N \l_@@_tmpc_tl
\tl_new:N \l_@@_tmpd_tl
\tl_new:N \l_@@_tmpe_tl
\tl_new:N \l_@@_tmpf_tl

\int_new:N \l_@@_tmpa_int
\seq_new:N \l_@@_tmpa_seq
\seq_new:N \l_@@_tmpb_seq
\seq_new:N \l_@@_tmpc_seq
\seq_new:N \l_@@_tmpd_seq

\tl_new:N \l_@@_current_tl
\tl_new:N \l_@@_reverse_tl
\tl_new:N \l_@@_prefix_tl
\tl_new:N \l_@@_suffix_tl
\tl_new:N \g_@@_smuggle_tl
\tl_new:N \g_@@_output_tl
\tl_new:N \l_@@_check_tl
\clist_new:N \g_@@_output_clist
\seq_new:N \g_@@_tmpa_seq
\seq_new:N \g_@@_tmpb_seq
\seq_new:N \g_@@_output_seq
\bool_new:N \l_@@_draft_bool
%    \end{macrocode}
%
% We surround all the keys with checks to ensure that the soft path under consideration does actually exist, but if it doesn't we should warn the user.
%
%    \begin{macrocode}
\msg_new:nnn { spath3 } { missing soft path }
{ Soft~ path~ #1~ doesn't~ exist~ \msg_line_context:}
\msg_new:nnnn { spath3 } { empty soft path }
{ Soft~ path~ #1~ is~ empty~ \msg_line_context:}
{If~ it~ was~ defined~ inside~ a~ group,~ try~ using~ "save~ global". }
\msg_new:nnn { spath3 } { load intersections }
{ You~ need~ to~ load~ the~ "intersections"~ library~
 to~ work~ with~ intersections }
%    \end{macrocode}
%
% When saving a soft path, by default we use a naming convention that is compatible with the intersections library so that paths saved here and paths saved by the \texttt{name path} facility of the intersections library are mutually exchangeable.
%
%    \begin{macrocode}
\tl_set:Nn \l_@@_prefix_tl {tikz@intersect@path@name@}
\tl_set:Nn \l_@@_suffix_tl {}
%    \end{macrocode}
%
% When a soft path is grabbed from TikZ we're usually deep in a group so I've adapted the code from the intersections library to dig the definition out of the group without making everything global.
%
% Interestingly, the intersections library doesn't clear its naming code once it is used meaning that it keeps resetting the definition of a path back to its original one every time a path command is called.
%
% Also, when the hook is restored outside a scope then no check is made to ensure that the inner one was actually invoked.
% This can cause issues when the syntax \Verb!\tikz .. ;! is used since the end of the path coincides with the end of the picture.
%    \begin{macrocode}
\tl_new:N \g_@@_tikzfinish_tl
\tl_new:N \l_@@_tikzfinish_outside_tl
\cs_new_protected_nopar:Npn \spath_at_end_of_path:
{
 \tl_use:N \g_@@_tikzfinish_tl
 \tl_gclear:N \g_@@_tikzfinish_tl
}
\tl_put_right:Nn \tikz@finish {\spath_at_end_of_path:}

\tikzset{
 every~ scope/.append~ style={
   execute~ at~ begin~ scope={
     \tl_set_eq:NN \l_@@_tikzfinish_outside_tl \g_@@_tikzfinish_tl
   },
   execute~ at~ end~ scope={
     \tl_use:N \g_@@_tikzfinish_tl
     \tl_gclear:N \g_@@_tikzfinish_tl
     \tl_gset_eq:NN \g_@@_tikzfinish_tl \l_@@_tikzfinish_outside_tl
   },
 },
}
%    \end{macrocode}
%
% This is for delaying something until the path is fully constructed (but no later), sometimes useful to be able to specify this in the path options rather than directly at the end of the path.
%
%    \begin{macrocode}
\tl_new:N \l_@@_tikzpath_finish_tl

\cs_new_protected_nopar:Npn \@@_at_end_of_path_construction:
{
 \tl_use:N \l_@@_tikzpath_finish_tl
 \tl_clear:N \l_@@_tikzpath_finish_tl
}

\tl_put_left:Nn \tikz@finish {\@@_at_end_of_path_construction:}
%    \end{macrocode}
%
% Code for saving a path
%
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \spath_save_path:Nn #1#2
{
 \tl_if_empty:NF \g_@@_tikzfinish_tl
 {
   \tl_use:N \g_@@_tikzfinish_tl
 }
 \tl_gput_right:Nn \g_@@_tikzfinish_tl
 {
   \tl_clear_new:N #1
   \tl_set:Nn #1 {#2}
 }
}
\cs_generate_variant:Nn \spath_save_path:Nn {cn, NV, cV}

\cs_new_protected_nopar:Npn \spath_gsave_path:Nn #1#2
{
 \tl_gput_right:Nn \g_@@_tikzfinish_tl
 {
   \tl_gclear_new:N #1
   \tl_gset:Nn #1 {#2}
 }
}
\cs_generate_variant:Nn \spath_gsave_path:Nn {cn, NV, cV}
%    \end{macrocode}
%
% \begin{macro}[internal]{
% \@@_process_tikz_point:Nn
% }
% Process a point via TikZ and store the resulting dimensions.
% \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_process_tikz_point:Nn #1#2
{
 \group_begin:
 \use:c {tikz@scan@one@point} \use:n #2 \scan_stop:
 \tl_gset:Nx \g_@@_output_tl
 {
   { \dim_use:c {pgf@x} }
   { \dim_use:c {pgf@y} }
 }
 \group_end:
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \@@_tikzset:n
% }
% Wrapper around \Verb+\tikzset+ for expansion.
% \begin{macrocode}
\cs_set_eq:NN \@@_tikzset:n \tikzset
\cs_generate_variant:Nn \@@_tikzset:n {V, v}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \@@_check_path:nnn
% \@@_check_two_paths:nnnn
% \@@_check_three_paths:nnnnn
% }
% Given a path name as the second argument, check if it exists and is not empty, and if so reinsert it after the first argument.
% The third argument is code to be executed in case of a missing or empty path.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_check_path:nnn #1#2#3
{
 \tl_set:Nn \l_@@_check_tl {#3}
 \tl_if_exist:cTF {\@@_path_name:n {#2}}
 {
   \tl_if_empty:cTF {\@@_path_name:n {#2}}
   {
     \msg_warning:nnn { spath3 } { empty soft path } { #2 }
   }
   {
     \tl_set:Nn \l_@@_check_tl {
       #1 {\@@_path_name:n {#2}}
     }
   }
 }
 {
   \msg_warning:nnx { spath3 } { missing soft path } { #2 }
 }
 \tl_use:N \l_@@_check_tl
}
\cs_new_protected_nopar:Npn \@@_check_two_paths:nnnn #1#2#3#4
{
 \@@_check_path:nnn {
   \@@_check_path:nnn {#1}{#2}{#4}
 }{#3}{#4}
}
\cs_new_protected_nopar:Npn \@@_check_three_paths:nnnnn #1#2#3#4#5
{
 \@@_check_path:nnn {
   \@@_check_path:nnn {
     \@@_check_path:nnn {#1}{#2}{#5}
   }{#3}{#5}
 }{#4}{#5}
}
\cs_generate_variant:Nn \@@_check_path:nnn {nVn}
\cs_generate_variant:Nn \@@_check_two_paths:nnnn {nnVn}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \@@_maybe_current_path:nn
% \@@_maybe_current_path_reuse:nnn
% \@@_maybe_current_two_paths_reuse_both:nnnn
% \@@_maybe_current_two_paths_reuse_first:nnnn
% \@@_maybe_current_two_paths_reuse_second:nnnn
% }
% If the named path is ``current'' then get the current path and use that.
% The second version puts the resulting path back as the current path.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_maybe_current_path:nn #1#2
{
 \tl_if_eq:nnT {#2} {current}
 {
   \spath_get_current_path:c {\@@_path_name:n {#2}}
 }
 #1 {#2}
}
\cs_new_protected_nopar:Npn \@@_maybe_current_path_reuse:nnn #1#2#3
{
 \bool_set_true:N \l_spath_movetorelevant_bool
 \tl_if_eq:nnT {#2} {current}
 {
   \spath_get_current_path:c {\@@_path_name:n {#2}}
 }
 #1 {#2} #3
 \tl_if_eq:nnT {#2} {current}
 {
   \tl_if_empty:cF {\@@_path_name:n {#2}}
   {
     \spath_set_current_path:c {\@@_path_name:n {#2}}
     \spath_set_tikz_data:v {\@@_path_name:n {#2}}
   }
 }
}
\cs_new_protected_nopar:Npn \@@_maybe_current_two_paths_reuse_both:nnnn #1#2#3#4
{
 \bool_set_true:N \l_spath_movetorelevant_bool
 \tl_if_eq:nnT {#2} {current}
 {
   \spath_get_current_path:c {\@@_path_name:n {#2}}
 }
 \tl_if_eq:nnT {#3} {current}
 {
   \spath_get_current_path:c {\@@_path_name:n {#3}}
 }
 #1 {#2} {#3} #4
 \tl_if_eq:nnT {#2} {current}
 {
   \tl_if_empty:cF {\@@_path_name:n {#2}}
   {
     \spath_set_current_path:c {\@@_path_name:n {#2}}
     \spath_set_tikz_data:v {\@@_path_name:n {#2}}
   }
 }
 \tl_if_eq:nnT {#3} {current}
 {
   \tl_if_empty:cF {\@@_path_name:n {#3}}
   {
     \spath_set_current_path:c {\@@_path_name:n {#3}}
     \spath_set_tikz_data:v {\@@_path_name:n {#3}}
   }
 }
}
\cs_new_protected_nopar:Npn \@@_maybe_current_two_paths_reuse_first:nnnn #1#2#3#4
{
 \bool_set_true:N \l_spath_movetorelevant_bool
 \tl_if_eq:nnT {#2} {current}
 {
   \spath_get_current_path:c {\@@_path_name:n {#2}}
 }
 \tl_if_eq:nnT {#3} {current}
 {
   \spath_get_current_path:c {\@@_path_name:n {#3}}
 }
 #1 {#2} {#3} #4
 \tl_if_eq:nnT {#2} {current}
 {
   \tl_if_empty:cF {\@@_path_name:n {#2}}
   {
     \spath_set_current_path:c {\@@_path_name:n {#2}}
     \spath_set_tikz_data:v {\@@_path_name:n {#2}}
   }
 }
}
\cs_new_protected_nopar:Npn \@@_maybe_current_two_paths_reuse_second:nnnn #1#2#3#4
{
 \bool_set_true:N \l_spath_movetorelevant_bool
 \tl_if_eq:nnT {#2} {current}
 {
   \spath_get_current_path:c {\@@_path_name:n {#2}}
 }
 \tl_if_eq:nnT {#3} {current}
 {
   \spath_get_current_path:c {\@@_path_name:n {#3}}
 }
 #1 {#2} {#3} #4
 \tl_if_eq:nnT {#3} {current}
 {
   \tl_if_empty:cF {\@@_path_name:n {#3}}
   {
     \spath_set_current_path:c {\@@_path_name:n {#3}}
     \spath_set_tikz_data:v {\@@_path_name:n {#3}}
   }
 }
}
\cs_generate_variant:Nn \@@_maybe_current_path:nn {nV}
\cs_generate_variant:Nn \@@_maybe_current_path_reuse:nnn {nVn}
%    \end{macrocode}
% \end{macro}
%
%
%
% \begin{macro}{
% \@@_seq_from_foreach:NNn
% }
%
% Convert a PGF foreach list, as the third argument, to a sequence.
% The second argument is the maximum number on the list.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_seq_from_foreach:Nnn #1#2#3
{
 \group_begin:
 \seq_gclear:N \g_@@_output_seq

 \tl_if_empty:nTF {#3}
 {
   \int_step_inline:nnnn {1}{1} {#2}
   {
     \seq_gput_right:Nn \g_@@_output_seq {##1}
   }
 }
 {
   \foreach \l_@@_tmpa_tl in {#3}
   {
     \int_compare:nTF { \l_@@_tmpa_tl > 0 }
     {
       \seq_gput_right:NV \g_@@_output_seq \l_@@_tmpa_tl
     }
     {
       \seq_gput_right:Nx \g_@@_output_seq
       {\int_eval:n {#2 - \l_@@_tmpa_tl}}
     }
   }
   \seq_gsort:Nn \g_@@_output_seq
   {
     \int_compare:nNnTF {##1} < {##2}
     { \sort_return_same: }
     { \sort_return_swapped: }
   }
 }
 \group_end:
 \seq_set_eq:NN #1 \g_@@_output_seq
 \seq_gclear:N \g_@@_output_seq
}
\cs_generate_variant:Nn \@@_seq_from_foreach:Nnn {NVV, NVn}
%
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \@@_path_name:n
% }
% Wrap the argument in the prefix and suffix to generate the proper name.
%    \begin{macrocode}
\cs_new:Npn \@@_path_name:n #1
{
 \tl_use:N \l_@@_prefix_tl
 #1
 \tl_use:N \l_@@_suffix_tl
}
\cs_generate_variant:Nn \@@_path_name:n {V}
%    \end{macrocode}
% \end{macro}
%
% When joining two paths we provide a set of options for how to process the second path.
%
%    \begin{macrocode}
\bool_new:N \l_@@_reverse_bool
\bool_new:N \l_@@_weld_bool
\bool_new:N \l_@@_move_bool
\bool_new:N \l_@@_global_bool
\bool_new:N \l_@@_current_transformation_bool
\tl_new:N \l_@@_joinpath_tl
\tl_new:N \l_@@_transformation_tl

\cs_new_protected_nopar:Npn \@@_set_bool:Nn #1#2
{
 \tl_if_eq:nnTF {#2}{false}
 {
   \bool_set_false:N #1
 }
 {
   \bool_set_true:N #1
 }
}
\tikzset {
 spath/join/.is~ family,
 spath/join/.cd,
 reverse/.code = {
   \@@_set_bool:Nn \l_@@_reverse_bool {#1}
 },
 reverse/.default = true,
 weld/.code = {
   \@@_set_bool:Nn \l_@@_weld_bool {#1}
 },
 weld/.default = true,
 no~ weld/.code = {
   \@@_set_bool:Nn \l_@@_weld_bool {#1}
   \bool_set:Nn \l_@@_weld_bool {! \l_@@_weld_bool}
 },
 no~ weld/.default = true,
 move/.code = {
   \@@_set_bool:Nn \l_@@_move_bool {#1}
 },
 move/.default = true,
 no~ move/.code = {
   \@@_set_bool:Nn \l_@@_move_bool {#1}
   \bool_set:Nn \l_@@_move_bool {! \l_@@_move_bool}
 },
 no~ move/.default = true,
 global/.code = {
   \@@_set_bool:Nn \l_@@_global_bool {#1}
 },
 global/.default = true,
 use~ current~ transformation/.code={
   \@@_set_bool:Nn \l_@@_current_transformation_bool {#1}
 },
 use~ current~ transformation/.default = true,
 transform/.store~in=\l_@@_transformation_tl,
 .unknown/.code = {
   \tl_set_eq:NN \l_@@_joinpath_tl \pgfkeyscurrentname
 }
}
%    \end{macrocode}
%
%
% When we split a soft path into components, we make it a comma separated list so that it can be fed into a \Verb+\foreach+ loop.
% This can also make it possible to extract a single component, but to do this we need a wrapper around \Verb+\clist_item:Nn+ (there doesn't appear to be a PGF way of getting an item of a CS list).
%
%    \begin{macrocode}
\cs_set_eq:NN \getComponentOf \clist_item:Nn
%    \end{macrocode}
%
% \subsection{Helper Functions}
%
% \begin{macro}[internal]{
% \@@_use_path:n
% }
% Use a path, possibly manipulating it first.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_use_path:n #1
{
 \tl_set:Nn \l_@@_joinpath_tl {#1}
 \spath_get_current_path:N \l_@@_current_tl

 \bool_if:NT \l_@@_reverse_bool
 {
   \spath_reverse:N \l_@@_joinpath_tl
 }

 \bool_if:NT \l_@@_current_transformation_bool
 {
   \pgfgettransform \l_@@_tmpa_tl
   \spath_transform:NV
   \l_@@_joinpath_tl
   \l_@@_tmpa_tl
 }

 \tl_if_empty:NF \l_@@_transformation_tl
 {
   \group_begin:
   \pgftransformreset
   \@@_tikzset:V \l_@@_transformation_tl
   \pgfgettransform \l_@@_tmpa_tl
   \tl_gset:Nn \g_@@_smuggle_tl
   {
     \spath_transform:Nnnnnnn
     \l_@@_joinpath_tl
   }
   \tl_gput_right:NV \g_@@_smuggle_tl \l_@@_tmpa_tl
   \group_end:
   \tl_use:N \g_@@_smuggle_tl
 }

 \bool_if:NT \l_@@_move_bool
 {
   \tl_if_empty:NTF \l_@@_current_tl
   {
     \tl_set:Nn \l_@@_tmpc_tl { {0pt} {0pt} }
   }
   {
     \spath_finalpoint:NV
     \l_@@_tmpc_tl
     \l_@@_current_tl
   }
   \spath_translate_to:NV \l_@@_joinpath_tl \l_@@_tmpc_tl
 }

 \tl_if_empty:NTF \l_@@_current_tl
 {
   \tl_if_empty:NTF \l_@@_joinpath_tl
   {
     \tl_set_eq:NN \l_@@_current_tl \c_spath_moveto_tl
     \tl_put_right:Nn \l_@@_current_tl {{0pt}{0pt}}
   }
   {
     \tl_set_eq:NN \l_@@_current_tl \l_@@_joinpath_tl
   }
 }
 {

   \tl_clear:N \l_@@_tmpa_tl
   \tl_set:Nn \l_@@_tmpa_tl {spath_}

   \tl_put_right:Nn \l_@@_tmpa_tl {append}

   \bool_if:NT \l_@@_weld_bool
   {
     \tl_put_right:Nn \l_@@_tmpa_tl {_no_move}
     \spath_numberofcomponents:NV \l_@@_tmpa_int \l_@@_joinpath_tl
     \int_compare:nT {\l_@@_tmpa_int == 1}
     {
       \bool_set_false:N \l_spath_movetorelevant_bool
     }
   }
   \tl_put_right:Nn \l_@@_tmpa_tl {:NV}

   \use:c {\tl_use:N \l_@@_tmpa_tl }
   \l_@@_current_tl
   \l_@@_joinpath_tl
 }

 \spath_set_current_path:N \l_@@_current_tl
 \spath_set_tikz_data:V \l_@@_joinpath_tl
}
\cs_generate_variant:Nn \@@_use_path:n {V, v}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[internal]{
% \@@_join_with:nn
% }
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_join_with:Nn #1#2
{
 \tl_set:Nn \l_@@_joinpath_tl {#2}

 \bool_if:NT \l_@@_reverse_bool
 {
   \spath_reverse:N \l_@@_joinpath_tl
 }

 \tl_if_empty:NF \l_@@_transformation_tl
 {
   \group_begin:
   \pgftransformreset
   \@@_tikzset:V \l_@@_transformation_tl
   \pgfgettransform \l_@@_tmpa_tl
   \tl_gset:Nn \g_@@_smuggle_tl
   {
     \spath_transform:Nnnnnnn
     \l_@@_joinpath_tl
   }
   \tl_gput_right:NV \g_@@_smuggle_tl \l_@@_tmpa_tl
   \group_end:
   \tl_use:N \g_@@_smuggle_tl
 }

 \bool_if:NT \l_@@_move_bool
 {
   \spath_finalpoint:NV
   \l_@@_tmpc_tl
   #1
   \spath_translate_to:NV \l_@@_joinpath_tl \l_@@_tmpc_tl
 }

 \tl_clear:N \l_@@_tmpa_tl
 \tl_set:Nn \l_@@_tmpa_tl {spath_}

 \bool_if:NT \l_@@_global_bool
 {
   \tl_put_right:Nn \l_@@_tmpa_tl {g}
 }

 \tl_put_right:Nn \l_@@_tmpa_tl {append}

 \bool_if:NT \l_@@_weld_bool
 {
   \tl_put_right:Nn \l_@@_tmpa_tl {_no_move}
 }
 \tl_put_right:Nn \l_@@_tmpa_tl {:NV}

 \cs_if_exist:cF {\tl_use:N \l_@@_tmpa_tl}
 {
   \tl_show:N \l_@@_tmpa_tl
 }

 \use:c {\tl_use:N \l_@@_tmpa_tl } #1
 \l_@@_joinpath_tl
}
\cs_generate_variant:Nn \@@_join_with:Nn {cv, cn}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{
% \@@_join_components_with:Nnn
% \@@_join_components_upright_with:Nnn
% }
% Join the specified components of the first path by splicing in the second.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_join_components_with_aux:nnn #1#2#3
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpc_tl {#1}
 \tl_if_empty:nT {#3}
 {
   \spath_spot_weld_components:N \l_@@_tmpc_tl
 }

 \spath_numberofcomponents:NV \l_@@_tmpa_int \l_@@_tmpc_tl
 \@@_seq_from_foreach:NVn \l_@@_tmpb_seq \l_@@_tmpa_int {#3}

 \spath_components_to_seq:NV \l_@@_tmpa_seq \l_@@_tmpc_tl

 \seq_pop_left:NN \l_@@_tmpa_seq \l_@@_tmpa_tl
 \seq_pop_left:NN \l_@@_tmpb_seq \l_@@_tmpb_tl

 \seq_map_indexed_inline:Nn \l_@@_tmpa_seq
 {
   \int_compare:nTF
   {
     ##1 == \l_@@_tmpb_tl
   }
   {
     \seq_pop_left:NNF \l_@@_tmpb_seq \l_@@_tmpb_tl
     {
       \tl_set:Nn \l_@@_tmpb_tl {-1}
     }
     \spath_splice_between:Nnn \l_@@_tmpa_tl {#2} {##2}
   }
   {
     \tl_put_right:Nn \l_@@_tmpa_tl {##2}
   }
 }
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \@@_join_components_with:Nnnn #1#2#3#4
{
 \@@_join_components_with_aux:nnn {#2}{#3}{#4}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \@@_join_components_with:Nnnn {NVnn}
\cs_new_protected_nopar:Npn \@@_join_components_with:Nnn #1#2#3
{
 \@@_join_components_with:NVnn #1#1{#2}{#3}
}
\cs_generate_variant:Nn \@@_join_components_with:Nnn {cvV}
\cs_new_protected_nopar:Npn \@@_gjoin_components_with:Nnnn #1#2#3#4
{
 \@@_join_components_with_aux:nnn {#2}{#3}{#4}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \@@_gjoin_components_with:Nnnn {NVnn}
\cs_new_protected_nopar:Npn \@@_gjoin_components_with:Nnn #1#2#3
{
 \@@_gjoin_components_with:NVnn #1#1{#2}{#3}
}
\cs_generate_variant:Nn \@@_gjoin_components_with:Nnn {cvV}
\cs_new_protected_nopar:Npn \@@_join_components_upright_with_aux:nnn #1#2#3
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpc_tl {#1}
 \tl_if_empty:nT {#3}
 {
   \spath_spot_weld_components:N \l_@@_tmpc_tl
 }

 \spath_numberofcomponents:NV \l_@@_tmpa_int \l_@@_tmpc_tl
 \@@_seq_from_foreach:NVn \l_@@_tmpb_seq \l_@@_tmpa_int {#3}

 \spath_components_to_seq:NV \l_@@_tmpa_seq \l_@@_tmpc_tl

 \seq_pop_left:NN \l_@@_tmpa_seq \l_@@_tmpa_tl
 \seq_pop_left:NN \l_@@_tmpb_seq \l_@@_tmpb_tl

 \tl_set:Nn \l_@@_tmpc_tl {#2}
 \spath_transform:NVnnnnnn \l_@@_tmpd_tl \l_@@_tmpc_tl {1}{0}{0}{-1}{0pt}{0pt}

 \seq_map_indexed_inline:Nn \l_@@_tmpa_seq
 {
   \int_compare:nTF
   {
     ##1 == \l_@@_tmpb_tl
   }
   {
     \seq_pop_left:NNF \l_@@_tmpb_seq \l_@@_tmpb_tl
     {
       \tl_set:Nn \l_@@_tmpb_tl {-1}
     }

     \spath_finalpoint:NV \l_@@_tmpe_tl \l_@@_tmpa_tl
     \spath_initialpoint:Nn \l_@@_tmpf_tl {##2}

     \dim_compare:nTF
     {
       \tl_item:Nn \l_@@_tmpe_tl {1}
       >
       \tl_item:Nn \l_@@_tmpf_tl {1}
     }
     {
       \spath_splice_between:NVn
       \l_@@_tmpa_tl
       \l_@@_tmpd_tl
       {##2}
     }
     {
       \spath_splice_between:NVn
       \l_@@_tmpa_tl
       \l_@@_tmpc_tl
       {##2}
     }
   }
   {
     \tl_put_right:Nn \l_@@_tmpa_tl {##2}
   }
 }
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \@@_join_components_upright_with:Nnnn #1#2#3#4
{
 \@@_join_components_upright_with_aux:nnn {#2}{#3}{#4}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \@@_join_components_upright_with:Nnnn {NVnn}
\cs_new_protected_nopar:Npn \@@_join_components_upright_with:Nnn #1#2#3
{
 \@@_join_components_upright_with:NVnn #1#1{#2}{#3}
}
\cs_generate_variant:Nn \@@_join_components_upright_with:Nnn {cvV}
\cs_new_protected_nopar:Npn \@@_gjoin_components_upright_with:Nnnn #1#2#3#4
{
 \@@_join_components_upright_with_aux:nnn {#2}{#3}{#4}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \@@_gjoin_components_upright_with:Nnnn {NVnn}
\cs_new_protected_nopar:Npn \@@_gjoin_components_upright_with:Nnn #1#2#3
{
 \@@_gjoin_components_upright_with:NVnn #1#1{#2}{#3}
}
\cs_generate_variant:Nn \@@_gjoin_components_upright_with:Nnn {cvV}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{
% \@@_get_components:Nn
% }
% Get the components of the named path to the token list.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_get_components_aux:n #1
{
 \clist_gclear_new:N \g_@@_output_clist
 \spath_components_to_seq:Nn \l_@@_tmpa_seq {#1}

 \seq_map_inline:Nn \l_@@_tmpa_seq
 {
   \spath_anonymous:N \l_@@_tmpa_tl
   \tl_new:c {\@@_path_name:V \l_@@_tmpa_tl}
   \tl_set:cn {\@@_path_name:V \l_@@_tmpa_tl} {##1}
   \clist_gput_right:NV \g_@@_output_clist \l_@@_tmpa_tl
 }
}
\cs_new_protected_nopar:Npn \@@_get_components:Nn #1#2
{
 \clist_clear_new:N #1
 \@@_get_components_aux:n {#2}
 \clist_set_eq:NN #1 \g_@@_output_clist
 \clist_gclear:N \g_@@_output_clist
}
\cs_generate_variant:Nn \@@_get_components:Nn {NV, Nv}
\cs_new_protected_nopar:Npn \@@_gget_components:Nn #1#2
{
 \clist_gclear_new:N #1
 \@@_get_components_aux:n {#2}
 \clist_gset_eq:NN #1 \g_@@_output_clist
 \clist_gclear:N \g_@@_output_clist
}
\cs_generate_variant:Nn \@@_gget_components:Nn {NV, Nv}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{
% \@@_render_components:n
% }
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_render_components:nn #1#2
{
 \group_begin:
 \spath_components_to_seq:Nn \l_@@_tmpa_seq {#2}
 \seq_map_indexed_inline:Nn \l_@@_tmpa_seq
 {
   \spath_tikz_path:nn
   {
     every~ spath~ component/.try,
     spath ~component~ ##1/.try,
     spath ~component/.try={##1},
     every~ #1~ component/.try,
     #1 ~component~ ##1/.try,
     #1 ~component/.try={##1},
   }
   {
     ##2
   }
 }
 \group_end:
}
\cs_generate_variant:Nn \@@_render_components:nn {nv}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \@@_insert_gaps_after_components:nn
% }
%
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_insert_gaps_after_components_aux:nnn #1#2#3
{
 \group_begin:
 \spath_numberofcomponents:Nn \l_@@_tmpa_int {#1}
 \@@_seq_from_foreach:NVn \l_@@_tmpa_seq \l_@@_tmpa_int {#3}

 \tl_if_empty:nT {#3}
 {
   \seq_pop_right:NN \l_@@_tmpa_seq \l_@@_tmpa_tl
 }

 \seq_clear:N \l_@@_tmpb_seq
 \seq_map_inline:Nn \l_@@_tmpa_seq {
   \seq_put_right:Nx
   \l_@@_tmpb_seq
   {\int_eval:n
     {
       \int_mod:nn { ##1 }{ \l_@@_tmpa_int } + 1
     }
   }
 }

 \spath_components_to_seq:Nn \l_@@_tmpc_seq {#1}

 \seq_clear:N \l_@@_tmpd_seq
 \seq_map_indexed_inline:Nn \l_@@_tmpc_seq
 {
   \tl_set:Nn \l_@@_tmpa_tl {##2}
   \seq_if_in:NnT \l_@@_tmpa_seq {##1}
   {
     \spath_shorten_at_end:Nn \l_@@_tmpa_tl {(#2)/2}
   }
   \seq_if_in:NnT \l_@@_tmpb_seq {##1}
   {
     \spath_shorten_at_start:Nn \l_@@_tmpa_tl {(#2)/2}
   }
   \seq_put_right:NV \l_@@_tmpd_seq \l_@@_tmpa_tl
 }
 \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpd_seq {} }
 \group_end:
}
\cs_new_protected_nopar:Npn \@@_insert_gaps_after_components:Nnnn #1#2#3#4
{
 \@@_insert_gaps_after_components_aux:nnn {#2}{#3}{#4}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \@@_insert_gaps_after_components:Nnnn {NVnn}
\cs_new_protected_nopar:Npn \@@_insert_gaps_after_components:Nnn #1#2#3
{
 \@@_insert_gaps_after_components:NVnn #1#1{#2}{#3}
}
\cs_generate_variant:Nn \@@_insert_gaps_after_components:Nnn {cnn, cVV}
\cs_new_protected_nopar:Npn \@@_ginsert_gaps_after_components:Nnnn #1#2#3#4
{
 \@@_insert_gaps_after_components_aux:nnn {#2}{#3}{#4}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \@@_ginsert_gaps_after_components:Nnnn {NVnn}
\cs_new_protected_nopar:Npn \@@_ginsert_gaps_after_components:Nnn #1#2#3
{
 \@@_ginsert_gaps_after_components:NVnn #1#1{#2}{#3}
}
\cs_generate_variant:Nn \@@_ginsert_gaps_after_components:Nnn {cnn, cVV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{
% \@@_insert_gaps_after_segments:Nn
% }
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_insert_gaps_after_segments_aux:nnn #1#2#3
{
 \group_begin:
 \spath_reallength:Nn \l_@@_tmpa_int {#1}
 \@@_seq_from_foreach:NVn \l_@@_tmpa_seq \l_@@_tmpa_int {#3}

 \tl_if_empty:nT {#3}
 {
   \seq_pop_right:NN \l_@@_tmpb_seq \l_@@_tmpa_tl
 }

 \seq_clear:N \l_@@_tmpb_seq
 \seq_map_inline:Nn \l_@@_tmpa_seq {
   \seq_put_right:Nx
   \l_@@_tmpb_seq
   {\int_eval:n
     {
       \int_mod:nn { ##1 }{ \l_@@_tmpa_int } + 1
     }
   }
 }

 \spath_segments_to_seq:Nn \l_@@_tmpc_seq {#1}

 \seq_clear:N \l_@@_tmpd_seq
 \seq_map_indexed_inline:Nn \l_@@_tmpc_seq
 {
   \tl_set:Nn \l_@@_tmpa_tl {##2}
   \seq_if_in:NnT \l_@@_tmpa_seq {##1}
   {
     \spath_shorten_at_end:Nn \l_@@_tmpa_tl {(#2)/2}
   }
   \seq_if_in:NnT \l_@@_tmpb_seq {##1}
   {
     \spath_shorten_at_start:Nn \l_@@_tmpa_tl {(#2)/2}
   }
   \seq_put_right:NV \l_@@_tmpd_seq \l_@@_tmpa_tl
 }
 \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpd_seq {} }
 \group_end:
}
\cs_new_protected_nopar:Npn \@@_insert_gaps_after_segments:Nnnn #1#2#3#4
{
 \@@_insert_gaps_after_segments_aux:nnn {#2}{#3}{#4}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \@@_insert_gaps_after_segments:Nnnn {NVnn}
\cs_new_protected_nopar:Npn \@@_insert_gaps_after_segments:Nnn #1#2#3
{
 \@@_insert_gaps_after_segments:NVnn #1#1{#2}{#3}
}
\cs_generate_variant:Nn \@@_insert_gaps_after_segments:Nnn {cnn, cVV}
\cs_new_protected_nopar:Npn \@@_ginsert_gaps_after_segments:Nnnn #1#2#3#4
{
 \@@_insert_gaps_after_segments_aux:nnn {#2}{#3}{#4}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \@@_ginsert_gaps_after_segments:Nnnn {NVnn}
\cs_new_protected_nopar:Npn \@@_ginsert_gaps_after_segments:Nnn #1#2#3
{
 \@@_ginsert_gaps_after_segments:NVnn #1#1{#2}{#3}
}
\cs_generate_variant:Nn \@@_ginsert_gaps_after_segments:Nnn {cnn, cVV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{
% \@@_join_components:Nn
% }
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_join_components_aux:nn #1#2
{
 \group_begin:

 \tl_set:Nn \l_@@_tmpa_tl {#1}
 \spath_numberofcomponents:NV \l_@@_tmpa_int \l_@@_tmpa_tl
 \@@_seq_from_foreach:NVn \l_@@_tmpa_seq \l_@@_tmpa_int {#2}

 \seq_reverse:N \l_@@_tmpa_seq

 \seq_map_inline:Nn \l_@@_tmpa_seq
 {
   \spath_join_component:Nn \l_@@_tmpa_tl {##1}
 }
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \@@_join_components:Nnn #1#2#3
{
 \@@_join_components_aux:nn {#2}{#3}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \@@_join_components:Nnn {NVn}
\cs_new_protected_nopar:Npn \@@_join_components:Nn #1#2
{
 \@@_join_components:NVn #1#1{#2}
}
\cs_generate_variant:Nn \@@_join_components:Nn {cn}
\cs_new_protected_nopar:Npn \@@_gjoin_components:Nnn #1#2#3
{
 \@@_join_components_aux:nn {#2}{#3}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \@@_gjoin_components:Nnn {NVn}
\cs_new_protected_nopar:Npn \@@_gjoin_components:Nn #1#2
{
 \@@_gjoin_components:NVn #1#1{#2}
}
\cs_generate_variant:Nn \@@_gjoin_components:Nn {cn}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{
% \@@_join_components_with_bezier:Nn
% }
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_join_components_with_bezier_aux:nn #1#2
{
 \group_begin:
 \tl_set:Nn \l_@@_tmpc_tl {#1}
 \tl_if_empty:nT {#2}
 {
   \spath_spot_weld_components:N \l_@@_tmpc_tl
 }

 \spath_numberofcomponents:NV \l_@@_tmpa_int \l_@@_tmpc_tl
 \@@_seq_from_foreach:NVn \l_@@_tmpb_seq \l_@@_tmpa_int {#2}

 \spath_components_to_seq:NV \l_@@_tmpa_seq \l_@@_tmpc_tl

 \seq_pop_left:NN \l_@@_tmpa_seq \l_@@_tmpa_tl
 \seq_pop_left:NN \l_@@_tmpb_seq \l_@@_tmpb_tl

 \seq_map_indexed_inline:Nn \l_@@_tmpa_seq
 {
   \int_compare:nTF
   {
     ##1 == \l_@@_tmpb_tl
   }
   {
     \seq_pop_left:NNF \l_@@_tmpb_seq \l_@@_tmpb_tl
     {
       \tl_set:Nn \l_@@_tmpb_tl {-1}
     }
     \spath_curve_between:Nn \l_@@_tmpa_tl {##2}
   }
   {
     \tl_put_right:Nn \l_@@_tmpa_tl {##2}
   }
 }
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \@@_join_components_with_bezier:Nnn #1#2#3
{
 \@@_join_components_with_bezier_aux:nn {#2}{#3}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \@@_join_components_with_bezier:Nnn {NVn}
\cs_new_protected_nopar:Npn \@@_join_components_with_bezier:Nn #1#2
{
 \@@_join_components_with_bezier:NVn #1#1{#2}
}
\cs_generate_variant:Nn \@@_join_components_with_bezier:Nn {cV}
\cs_new_protected_nopar:Npn \@@_gjoin_components_with_bezier:Nnn #1#2#3
{
 \@@_join_components_with_bezier_aux:nn {#2}{#3}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \@@_gjoin_components_with_bezier:Nnn {NVn}
\cs_new_protected_nopar:Npn \@@_gjoin_components_with_bezier:Nn #1#2
{
 \@@_gjoin_components_with_bezier:NVn #1#1{#2}
}
\cs_generate_variant:Nn \@@_gjoin_components_with_bezier:Nn {cV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{
% \@@_remove_components:nn
% }
%
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_remove_components_aux:nn #1#2
{
 \group_begin:

 \spath_numberofcomponents:Nn \l_@@_tmpa_int {#1}
 \@@_seq_from_foreach:NVn \l_@@_tmpa_seq \l_@@_tmpa_int {#2}

 \spath_components_to_seq:Nn \l_@@_tmpb_seq {#1}

 \seq_pop_left:NNF \l_@@_tmpa_seq \l_@@_tmpa_tl
 {
   \tl_clear:N \l_@@_tmpa_tl
 }

 \seq_clear:N \l_@@_tmpc_seq
 \seq_map_indexed_inline:Nn \l_@@_tmpb_seq
 {
   \tl_set:Nn \l_@@_tmpb_tl {##1}
   \tl_if_eq:NNTF \l_@@_tmpb_tl \l_@@_tmpa_tl
   {
     \seq_pop_left:NNF \l_@@_tmpa_seq \l_@@_tmpa_tl
     {
       \tl_clear:N \l_@@_tmpa_tl
     }
   }
   {
     \seq_put_right:Nn \l_@@_tmpc_seq {##2}
   }
 }

 \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpc_seq {} }
 \group_end:
}
\cs_new_protected_nopar:Npn \@@_remove_components:Nnn #1#2#3
{
 \@@_remove_components_aux:nn {#2}{#3}
 \tl_set_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \@@_remove_components:Nnn {NVn}
\cs_new_protected_nopar:Npn \@@_remove_components:Nn #1#2
{
 \@@_remove_components:NVn #1#1{#2}
}
\cs_generate_variant:Nn \@@_remove_components:Nn {cn}
\cs_new_protected_nopar:Npn \@@_gremove_components:Nnn #1#2#3
{
 \@@_remove_components_aux:nn {#2}{#3}
 \tl_gset_eq:NN #1 \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \@@_gremove_components:Nnn {NVn}
\cs_new_protected_nopar:Npn \@@_gremove_components:Nn #1#2
{
 \@@_gremove_components:NVn #1#1{#2}
}
\cs_generate_variant:Nn \@@_gremove_components:Nn {cn}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[internal]{
% \@@_transform_to:nn,
% \@@_transform_upright_to:nn
% }
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_transform_to_aux:nn #1#2
{
 \group_begin:
 \spath_reallength:Nn \l_@@_tmpa_int {#2}

 \tl_set:Nx \l_@@_tmpb_tl
 {\fp_to_decimal:n {(#1) * (\l_@@_tmpa_int)}}
 \spath_transformation_at:NnV \l_@@_tmpc_tl {#2} \l_@@_tmpb_tl
 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpc_tl
 \group_end:
}
\cs_new_protected_nopar:Npn \@@_transform_to:nn #1#2
{
 \@@_transform_to_aux:nn {#1}{#2}
 \exp_last_unbraced:NV \pgfsettransformentries \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \@@_transform_to:nn {nv}
\cs_new_protected_nopar:Npn \@@_transform_upright_to:nn #1#2
{
 \@@_transform_to_aux:nn {#1}{#2}
 \fp_compare:nT { \tl_item:Nn \g_@@_output_tl {4} < 0}
 {
   \tl_gset:Nx \g_@@_output_tl
   {
     { \fp_eval:n { - (\tl_item:Nn \g_@@_output_tl {1})} }
     { \fp_eval:n { - (\tl_item:Nn \g_@@_output_tl {2})} }
     { \fp_eval:n { - (\tl_item:Nn \g_@@_output_tl {3})} }
     { \fp_eval:n { - (\tl_item:Nn \g_@@_output_tl {4})} }
     { \tl_item:Nn \g_@@_output_tl {5} }
     { \tl_item:Nn \g_@@_output_tl {6} }
   }
 }
 \exp_last_unbraced:NV \pgfsettransformentries \g_@@_output_tl
 \tl_gclear:N \g_@@_output_tl
}
\cs_generate_variant:Nn \@@_transform_upright_to:nn {nv}
%    \end{macrocode}
% \end{macro}
%
%
%
% \subsection{Keys}
% Now we define all of our keys.
%
%    \begin{macrocode}
\tikzset{
%    \end{macrocode}
%
% We're in the \texttt{spath} key family.
%
%    \begin{macrocode}
 spath/.is~family,
 spath/.cd,
%    \end{macrocode}
%
% We provide for saving soft paths with a specific prefix and suffix in the name.
% The default is to make it compatible with the intersections library.
%
%    \begin{macrocode}
 set~ prefix/.store~ in=\l_@@_prefix_tl,
 prefix/.is~choice,
 prefix/default/.style={
   /tikz/spath/set~ prefix=tikz@intersect@path@name@
 },
 set~ suffix/.store~ in=\l_@@_suffix_tl,
 suffix/.is~choice,
 suffix/default/.style={
   /tikz/spath/set~ suffix={}
 },
 set~ name/.style={
   /tikz/spath/prefix=#1,
   /tikz/spath/suffix=#1
 },
%    \end{macrocode}
%
% Hook in to the end of the path construction
%
%    \begin{macrocode}
 at~ end~ path~ construction/.code={
   \tl_put_right:Nn \l_@@_tikzpath_finish_tl {#1}
 },
%    \end{macrocode}
%
% Keys for saving and cloning a soft path.
%
%    \begin{macrocode}
 save/.code={
   \tikz@addmode{
     \spath_get_current_path:N \l_@@_tmpa_tl
     \spath_bake_round:NV \l_@@_tmpa_tl \l_@@_tmpa_tl
     \spath_bake_shorten:NV \l_@@_tmpa_tl \l_@@_tmpa_tl
     \spath_save_path:cV {\@@_path_name:n {#1}}
     \l_@@_tmpa_tl
   }
 },
 save~ global/.code={
   \tikz@addmode{
     \spath_get_current_path:N \l_@@_tmpa_tl
     \spath_bake_round:NV \l_@@_tmpa_tl \l_@@_tmpa_tl
     \spath_bake_shorten:NV \l_@@_tmpa_tl \l_@@_tmpa_tl
     \spath_gsave_path:cV {\@@_path_name:n {#1}}
     \l_@@_tmpa_tl
   }
 },
 clone/.code~ 2~ args={
   \@@_maybe_current_path:nn
   {
     \@@_check_path:nnn {
       \tl_clear_new:c {\@@_path_name:n {#1}}
       \tl_set_eq:cc {\@@_path_name:n {#1}}
     }
   }
   {#2}{}
 },
 clone~ global/.code~ 2~ args={
   \@@_maybe_current_path:nn
   {
     \@@_check_path:nnn {
       \tl_gclear_new:c {\@@_path_name:n {#1}}
       \tl_gset_eq:cc {\@@_path_name:n {#1}}
     }
   }
   {#2}{}
 },
%    \end{macrocode}
%
% Saves a soft path to the aux file.
%
%    \begin{macrocode}
 save~ to~ aux/.code={
   \@@_maybe_current_path:nn
   {
     \@@_check_path:nnn {
       \spath_save_to_aux:c
     }
   }
   {#1}
   {}
 },
%    \end{macrocode}
%
% Exports the path as an SVG file.
%
%    \begin{macrocode}
 export~ to~ svg/.code={
   \@@_maybe_current_path:nn
   {
     \@@_check_path:nnn {
       \spath_export_to_svg:nv {#1}
     }
   }
   {#1}
   {}
 },
%    \end{macrocode}
%
% Inserts the named path at the current point in the path, with options for how this is accomplished.
% The inserted path can be transformed, reversed, moved to the current point, and welded to the current path.
% If this is used before the path has been started then it becomes the start of the path (and the ``current point'' is taken as the origin).
%
%
%    \begin{macrocode}
 use/.code={
   \bool_set_false:N \l_@@_reverse_bool
   \bool_set_false:N \l_@@_weld_bool
   \bool_set_false:N \l_@@_move_bool
   \bool_set_false:N \l_@@_current_transformation_bool
   \bool_set_true:N \l_spath_movetorelevant_bool
   \tl_clear:N \l_@@_joinpath_tl
   \tl_clear:N \l_@@_transformation_tl
   \tikzset{
     spath/join/.cd,
     #1
   }

   \@@_check_path:nVn
   {
     \@@_use_path:v
   } \l_@@_joinpath_tl {}

 },
%    \end{macrocode}
%
% Some aliases for the above.
%
%    \begin{macrocode}
 restore/.style={/tikz/spath/use={#1}},
 restore~ reverse/.style={/tikz/spath/use={reverse, #1}},
 append/.style={/tikz/spath/use={move, weld, #1}},
 append~ no~ move/.style={/tikz/spath/use={weld, #1}},
 append~ reverse/.style={/tikz/spath/use={move, weld, reverse, #1}},
 append~ reverse~ no~ move/.style={/tikz/spath/use={weld, reverse, #1}},
 insert/.style={/tikz/spath/use={#1}},
 insert~ reverse/.style={/tikz/spath/use={reverse, #1}},
%    \end{macrocode}
%
% Diagnostic, show the current path in the terminal and log.
%
%    \begin{macrocode}
 show~current~path/.code={
   \tikz@addmode{
     \pgfsyssoftpath@getcurrentpath\l_@@_tmpa_tl
     \iow_term:n {---~ current~ soft~ path~ ---}
     \spath_show:V \l_@@_tmpa_tl
   }
 },
%    \end{macrocode}
%
% Diagnostic, show the named soft path in the terminal and log.
%
%    \begin{macrocode}
 show/.code={
   \@@_check_path:nnn {
     \iow_term:n {---~ soft~ path~ #1~ ---}
     \spath_show:v
   } {#1} {}
 },
%    \end{macrocode}
%
% This joins a path on to an existing path, possibly modifying it first.
% The possible options are the same as those for \Verb+use+.
% It is possible to specify the same path both for the initial and the joining path as a copy is made internally first.
%
%    \begin{macrocode}
 join~ with/.code~ 2~ args={
   \bool_set_false:N \l_@@_reverse_bool
   \bool_set_false:N \l_@@_weld_bool
   \bool_set_false:N \l_@@_move_bool
   \bool_set_false:N \l_@@_global_bool
   \bool_set_false:N \l_@@_current_transformation_bool
   \tl_clear:N \l_@@_joinpath_tl
   \tl_clear:N \l_@@_transformation_tl
   \tikzset{
     spath/join/.cd,
     #2
   }

   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_two_paths:nnVn
     {
       \@@_join_with:cv
     }
   } {#1} { \l_@@_joinpath_tl {} }
 },
%    \end{macrocode}
%
% Does a ``spot weld'' on a soft path, which means that any components that start where the previous component ends are welded together.
%
%    \begin{macrocode}
 spot~ weld/.code={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_spot_weld_components:c
     }
   } {#1} { {} }
 },
 spot~ weld~ globally/.code={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_spot_gweld_components:c
     }
   } {#1} { {} }
 },
%    \end{macrocode}
%
% Reverses the named path.
%
%    \begin{macrocode}
 reverse/.code={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_reverse:c
     }
   } {#1} { {} }
 },
 reverse~ globally/.code={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_greverse:c
     }
   } {#1} { {} }
 },
%    \end{macrocode}
%
% Adjust a path to span between two points.
%
%    \begin{macrocode}
 span/.code ~n~ args={3}{
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \@@_process_tikz_point:Nn \l_@@_tmpa_tl {#2}
       \@@_process_tikz_point:Nn \l_@@_tmpb_tl {#3}
       \spath_span:cVV
     }
   } {#1} { {} \l_@@_tmpa_tl \l_@@_tmpb_tl }
 },
 span~ global/.code ~n~ args={3}{
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \@@_process_tikz_point:Nn \l_@@_tmpa_tl {#2}
       \@@_process_tikz_point:Nn \l_@@_tmpb_tl {#3}
       \spath_span:cVV
     }
   } {#1} { {} \l_@@_tmpa_tl \l_@@_tmpb_tl }
 },
%    \end{macrocode}
%
% Defines a to path
%
%    \begin{macrocode}
 to/.style={
   to~path={
     [
       spath/span={#1}{(\tikztostart)}{(\tikztotarget)},
       spath/use={#1,weld},
     ]
     \tikztonodes
   }
 },
%    \end{macrocode}
%
%
% Splice three paths together, transforming the middle one so that it exactly fits between the first and third.
%
%    \begin{macrocode}
 splice/.code ~n~ args={3}{
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_three_paths:nnnnn
     {
       \spath_splice_between:cvv
     }
   } {#1} { {#2} {#3} {} }
 },
 splice~ global/.code ~n~ args={3}{
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_three_paths:nnnnn
     {
       \spath_gsplice_between:cvv
     }
   } {#1} { {#2} {#3} {} }
 },
%    \end{macrocode}
%
% Join the components of a path by splicing in the second path whenever the components are sufficiently far apart.
% The third argument is a list of components to splice after, if it is empty then all components are used and a spot weld is done first so that the splicing only happens if there is an actual gap.
%
% The \Verb+upright+ versions will join with the reflection of the splice path if it detects that the gap is ``upside-down''.
%
%
%    \begin{macrocode}
 join~ components~ with/.code~2~args={
   \tl_if_head_is_group:nTF {#2}
   {
     \tl_set:Nx \l_@@_tmpc_tl { \tl_item:nn {#2} {1} }
     \tl_set:Nx \l_@@_tmpd_tl { \tl_item:nn {#2} {2} }
   }
   {
     \tl_set:Nn \l_@@_tmpc_tl {#2}
     \tl_clear:N \l_@@_tmpd_tl
   }

   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_two_paths:nnVn
     {
       \@@_join_components_with:cvV
     }
   } {#1} { \l_@@_tmpc_tl {} \l_@@_tmpd_tl  }
 },
 join~ components~ globally~ with/.code~2~args={
   \tl_if_head_is_group:nTF {#2}
   {
     \tl_set:Nx \l_@@_tmpc_tl { \tl_item:nn {#2} {1} }
     \tl_set:Nx \l_@@_tmpd_tl { \tl_item:nn {#2} {2} }
   }
   {
     \tl_set:Nn \l_@@_tmpc_tl {#2}
     \tl_clear:N \l_@@_tmpd_tl
   }

   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_two_paths:nnVn
     {
       \@@_gjoin_components_with:cvV
     }
   } {#1} { \l_@@_tmpc_tl {} \l_@@_tmpd_tl  }
 },
 join~ components~ upright~ with/.code~2~args={
   \tl_if_head_is_group:nTF {#2}
   {
     \tl_set:Nx \l_@@_tmpc_tl { \tl_item:nn {#2} {1} }
     \tl_set:Nx \l_@@_tmpd_tl { \tl_item:nn {#2} {2} }
   }
   {
     \tl_set:Nn \l_@@_tmpc_tl {#2}
     \tl_clear:N \l_@@_tmpd_tl
   }

   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_two_paths:nnVn
     {
       \@@_join_components_upright_with:cvV
     }
   } {#1} { \l_@@_tmpc_tl {} \l_@@_tmpd_tl  }
 },
 join~ components~ globally~ upright~ with/.code~2~args={
   \tl_if_head_is_group:nTF {#2}
   {
     \tl_set:Nx \l_@@_tmpc_tl { \tl_item:nn {#2} {1} }
     \tl_set:Nx \l_@@_tmpd_tl { \tl_item:nn {#2} {2} }
   }
   {
     \tl_set:Nn \l_@@_tmpc_tl {#2}
     \tl_clear:N \l_@@_tmpd_tl
   }

   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_two_paths:nnVn
     {
       \@@_gjoin_components_upright_with:cvV
     }
   } {#1} { \l_@@_tmpc_tl {} \l_@@_tmpd_tl  }
 },
 join~ components~ with~ bezier/.code={
   \tl_if_head_is_group:nTF {#1}
   {
     \tl_set:Nx \l_@@_tmpc_tl { \tl_item:nn {#1} {1} }
     \tl_set:Nx \l_@@_tmpd_tl { \tl_item:nn {#1} {2} }
   }
   {
     \tl_set:Nn \l_@@_tmpc_tl {#1}
     \tl_clear:N \l_@@_tmpd_tl
   }

   \@@_maybe_current_path_reuse:nVn
   {
     \@@_check_path:nnn
     {
       \@@_join_components_with_bezier:cV
     }
   } \l_@@_tmpc_tl { {} \l_@@_tmpd_tl }
 },
 join~ components~ globally~ with~ bezier/.code~2~args={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \@@_gjoin_components_with_bezier:cn
     }
   } {#1} { {} {#2} }
 },
%    \end{macrocode}
%
% Close a path.
%
%    \begin{macrocode}
 close/.code={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_close:c
     }
   } {#1} { {} }
 },
 close~ globally/.code={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_gclose:c
     }
   } {#1} { {} }
 },
%    \end{macrocode}
%
% Open a path.
%
%    \begin{macrocode}
 open/.code={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_open:c
     }
   } {#1} { {} }
 },
 open~ globally/.code={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_gopen:c
     }
   } {#1} { {} }
 },
%    \end{macrocode}
%
% Close a path, ensuring that the end point is exactly where it will close up to.
%
%    \begin{macrocode}
 adjust~ and~ close/.code={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_adjust_close:c
     }
   } {#1} { {} }
 },
 adjust~ and~ close~ globally/.code={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_adjust_gclose:c
     }
   } {#1} { {} }
 },
%    \end{macrocode}
%
% Close a path with another path.
%
%    \begin{macrocode}
 close~ with/.code~ 2~ args={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_two_paths:nnnn
     {
       \spath_close_with:cv
     }
   } {#1} { {#2} {} }
 },
 close~ globally~ with/.code~ 2~ args={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_two_paths:nnnn
     {
       \spath_gclose_with:cv
     }
   } {#1} { {#2} {} }
 },
%    \end{macrocode}
%
% Close a path with a curve.
%
%    \begin{macrocode}
 close~ with~ curve/.code={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_close_with_curve:c
     }
   } {#1} { {} }
 },
 close~ globally~ with~ curve/.code={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_gclose_with_curve:c
     }
   } {#1} { {} }
 },
%    \end{macrocode}
%
% These keys shorten the path by a dimension.
%
%    \begin{macrocode}
 shorten~ at~ end/.code~ 2~ args={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_shorten_at_end:cn
     }
   } {#1} { {} {#2} }
 },
 shorten~ at~ start/.code~ 2~ args ={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_shorten_at_start:cn
     }
   } {#1} { {} {#2} }
 },
 shorten~ at~ both~ ends/.code~ 2~ args={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_shorten_at_both_ends:cn
     }
   } {#1} { {} {#2} }
 },
 shorten~ globally~ at~ end/.code~ 2~ args={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_gshorten_at_end:cn
     }
   } {#1} { {} {#2} }
 },
 shorten~ globally~ at~ start/.code~ 2~ args ={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_gshorten_at_start:cn
     }
   } {#1} { {} {#2} }
 },
 shorten~ globally~ at~ both~ ends/.code~ 2~ args={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_gshorten_at_both_ends:cn
     }
   } {#1} { {} {#2} }
 },
%    \end{macrocode}
%
% These keys split a path at a parameter, the \texttt{keep} versions only keep one part of the resultant path.
%
%    \begin{macrocode}
 split~ at/.code~ 2~ args={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_split_at_normalised:cn
     }
   } {#1} { {} {#2} }
 },
 split~ globally~ at/.code~ 2~ args={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_gsplit_at_normalised:cn
     }
   } {#1} { {} {#2} }
 },
 split~ at~ into/.code~ n~ args={4}{
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_split_at_normalised:ccvn {\@@_path_name:n {#1}}
       {\@@_path_name:n {#2}}
     }
   } {#3} { {} {#4} }
 },
 split~ globally~ at~ into/.code~ n~ args={4}{
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_gsplit_at_normalised:ccvn {\@@_path_name:n {#1}}
       {\@@_path_name:n {#2}}
     }
   } {#3} { {} {#4} }
 },
 split~ at~ keep~ start/.code~ 2~ args={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_split_at_normalised_keep_start:cn
     }
   } {#1} { {} {#2} }
 },
 split~ globally~ at~ keep~ start/.code~ 2~ args={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_gsplit_at_normalised_keep_start:cn
     }
   } {#1} { {} {#2} }
 },
 split~ at~ keep~ end/.code~ 2~ args={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_split_at_normalised_keep_end:cn
     }
   } {#1} { {} {#2} }
 },
 split~ globally~ at~ keep~ end/.code~ 2~ args={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_gsplit_at_normalised_keep_end:cn
     }
   } {#1} { {} {#2} }
 },
 split~ at~ keep~ middle/.style~ n~ args={3}{
   /tikz/spath/split~ at~ keep~ start={#1}{#3},
   /tikz/spath/split~ at~ keep~ end={#1}{(#2)/(#3)},
 },
 split~ globally~ at~ keep~ middle/.style~ n~ args={3}{
   /tikz/spath/split~ globally~ at~ keep~ start={#1}{#3},
   /tikz/spath/split~ globally~ at~ keep~ end={#1}{(#2)/(#3)},
 },
%    \end{macrocode}
%
% This translates the named path.
%
%    \begin{macrocode}
 translate/.code~ n~ args={3}{
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_translate:cnn
     }
   } {#1} { {} {#2}{#3} }
 },
 translate~ globally/.code~ n~ args={3}{
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_gtranslate:cnn
     }
   } {#1} { {} {#2}{#3} }
 },
%    \end{macrocode}
%
% This normalises the named path.
%
%    \begin{macrocode}
 normalise/.code={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_normalise:c
     }
   } {#1} { {} }
 },
 normalise~ globally/.code={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_gnormalise:c
     }
   } {#1} { {} }
 },
%    \end{macrocode}
%
% Transforms the named path using TikZ transformation specifications.
%
%    \begin{macrocode}
 transform/.code~ 2~ args={
   \group_begin:
   \pgftransformreset
   \tikzset{#2}
   \pgfgettransform \l_@@_tmpa_tl
   \tl_gset_eq:NN \g_@@_smuggle_tl \l_@@_tmpa_tl
   \group_end:

   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_transform:cV
     }
   } {#1} { {} \g_@@_smuggle_tl }
 },
 transform~globally/.code~ 2~ args={
   \group_begin:
   \pgftransformreset
   \tikzset{#2}
   \pgfgettransform \l_@@_tmpa_tl
   \tl_gset_eq:NN \g_@@_smuggle_tl \l_@@_tmpa_tl
   \group_end:

   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_gtransform:cV
     }
   } {#1} { {} \g_@@_smuggle_tl }
 },
%    \end{macrocode}
%
% Splits first path where it intersects with the second.
%
%    \begin{macrocode}
 split~ at~ intersections~ with/.code~ 2~ args={
   \tl_if_exist:cTF
   {
     tikz@library@intersections@loaded
   }
   {
     \@@_maybe_current_two_paths_reuse_first:nnnn
     {
       \@@_check_two_paths:nnnn
       {
         \spath_split_path_at_intersections:cv
       }
     } {#1} {#2} { {} }
   }
   {
     \msg_warning:nn { spath3 } { load intersections }
   }
 },
 split~ globally~ at~ intersections~ with/.code~ n~ args={2}{
   \tl_if_exist:cTF
   {
     tikz@library@intersections@loaded
   }
   {
     \@@_maybe_current_two_paths_reuse_first:nnnn
     {
       \@@_check_two_paths:nnnn
       {
         \spath_gsplit_path_at_intersections:cv
       }
     } {#1} {#2} { {} }
   }
   {
     \msg_warning:nn { spath3 } { load intersections }
   }
 },
%    \end{macrocode}
%
% Splits two paths at their mutual intersections.
%
%    \begin{macrocode}
 split~ at~ intersections/.code~ n~ args={2}{
   \tl_if_exist:cTF
   {
     tikz@library@intersections@loaded
   }
   {
     \@@_maybe_current_two_paths_reuse_both:nnnn
     {
       \@@_check_two_paths:nnnn
       {
         \spath_split_at_intersections:cc
       }
     } {#1} {#2} { {} }
   }
   {
     \msg_warning:nn { spath3 } { load intersections }
   }
 },
 split~ globally~ at~ intersections/.code~ n~ args={2}{
   \tl_if_exist:cTF
   {
     tikz@library@intersections@loaded
   }
   {
     \@@_maybe_current_two_paths_reuse_both:nnnn
     {
       \@@_check_two_paths:nnnn
       {
         \spath_gsplit_at_intersections:cc
       }
     } {#1} {#2} { {} }
   }
   {
     \msg_warning:nn { spath3 } { load intersections }
   }
 },
%    \end{macrocode}
%
% Splits a path at its self-intersections.
%
%    \begin{macrocode}
 split~ at~ self~ intersections/.code={
   \tl_if_exist:cTF
   {
     tikz@library@intersections@loaded
   }
   {
     \@@_maybe_current_path_reuse:nnn
     {
       \@@_check_path:nnn
       {
         \spath_split_at_self_intersections:c
       }
     } {#1} { {} }
   }
   {
     \msg_warning:nn { spath3 } { load intersections }
   }
 },
 split~ globally~ at~ self~ intersections/.code={
   \tl_if_exist:cTF
   {
     tikz@library@intersections@loaded
   }
   {
     \@@_maybe_current_path_reuse:nnn
     {
       \@@_check_path:nnn
       {
         \spath_gsplit_at_self_intersections:c
       }
     } {#1} { {} }
   }
   {
     \msg_warning:nn { spath3 } { load intersections }
   }
 },
%    \end{macrocode}
%
% Extract the components of a path into a comma separated list (suitable for using in a \Verb+\foreach+ loop).
%
%    \begin{macrocode}
 get~ components~ of/.code~ 2~ args={
   \@@_maybe_current_path:nn
   {
     \@@_check_path:nnn {
       \@@_get_components:Nv #2
     }
   }
   {#1}
   {}
 },
 get~ components~ of~ globally/.code~ 2~ args={
   \@@_maybe_current_path:nn
   {
     \@@_check_path:nnn {
       \@@_gget_components:Nv #2
     }
   }
   {#1}
   {}
 },
%    \end{macrocode}
%
% Loop through the components of a soft path and render each as a separate TikZ path so that they can be individually styled.
%
%    \begin{macrocode}
 render~ components/.code={
   \@@_maybe_current_path:nn
   {
     \@@_check_path:nnn {
       \@@_render_components:nv {#1}
     }
   }
   {#1}
   {}
 },
%    \end{macrocode}
%
% This puts gaps between components of a soft path.
% The list of components is passed through a \Verb+\foreach+ loop so can use the shortcut syntax from those loops.
%
%    \begin{macrocode}
 insert~ gaps~ after~ components/.code~ 2~ args={
   \tl_if_head_is_group:nTF {#2}
   {
     \tl_set:Nx \l_@@_tmpc_tl { \tl_item:nn {#2} {1} }
     \tl_set:Nx \l_@@_tmpd_tl { \tl_item:nn {#2} {2} }
   }
   {
     \tl_set:Nn \l_@@_tmpc_tl {#2}
     \tl_clear:N \l_@@_tmpd_tl
   }

   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \@@_insert_gaps_after_components:cVV
     }
   } {#1} { {} \l_@@_tmpc_tl \l_@@_tmpd_tl  }
 },
 insert~ gaps~ globally~ after~ components/.code~ 2~ args={
   \tl_if_head_is_group:nTF {#2}
   {
     \tl_set:Nx \l_@@_tmpc_tl { \tl_item:nn {#2} {1} }
     \tl_set:Nx \l_@@_tmpd_tl { \tl_item:nn {#2} {2} }
   }
   {
     \tl_set:Nn \l_@@_tmpc_tl {#2}
     \tl_clear:N \l_@@_tmpd_tl
   }

   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \@@_ginsert_gaps_after_components:cVV
     }
   } {#1} { {} \l_@@_tmpc_tl \l_@@_tmpd_tl  }
 },
%    \end{macrocode}
%
% This puts gaps between segments of a soft path.
% The list of segments is passed through a \Verb+\foreach+ loop so can use the shortcut syntax from those loops.
%
%    \begin{macrocode}
 insert~ gaps~ after~ segments/.code~ 2~ args={
   \tl_if_head_is_group:nTF {#2}
   {
     \tl_set:Nx \l_@@_tmpc_tl { \tl_item:nn {#2} {1} }
     \tl_set:Nx \l_@@_tmpd_tl { \tl_item:nn {#2} {2} }
   }
   {
     \tl_set:Nn \l_@@_tmpc_tl {#2}
     \tl_clear:N \l_@@_tmpd_tl
   }

   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \@@_insert_gaps_after_segments:cVV
     }
   } {#1} { {} \l_@@_tmpc_tl \l_@@_tmpd_tl  }
 },
 insert~ gaps~ globally~ after~ segments/.code~ 2~ args={
   \tl_if_head_is_group:nTF {#2}
   {
     \tl_set:Nx \l_@@_tmpc_tl { \tl_item:nn {#2} {1} }
     \tl_set:Nx \l_@@_tmpd_tl { \tl_item:nn {#2} {2} }
   }
   {
     \tl_set:Nn \l_@@_tmpc_tl {#2}
     \tl_clear:N \l_@@_tmpd_tl
   }

   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \@@_ginsert_gaps_after_segments:cVV
     }
   } {#1} { {} \l_@@_tmpc_tl \l_@@_tmpd_tl  }
 },
%    \end{macrocode}
%
% Join the specified components together, joining each to its previous one.
%
%    \begin{macrocode}
 join~ components/.code~ 2~ args={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \@@_join_components:cn
     }
   } {#1} { {} {#2} }
 },
 join~ components~ globally/.code~ 2~ args={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \@@_gjoin_components:cn
     }
   } {#1} { {} {#2} }
 },
%    \end{macrocode}
%
% Remove all components of the path that don't actually draw anything.
%
%    \begin{macrocode}
 remove~ empty~ components/.code={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_remove_empty_components:c
     }
   } {#1} { {} }
 },
 remove~ empty~ components~ globally/.code={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_gremove_empty_components:c
     }
   } {#1} { {} }
 },
%    \end{macrocode}
%
% Replace all line segments by B\'ezier curves.
%
%    \begin{macrocode}
 replace~ lines/.code={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_replace_lines:c
     }
   } {#1} { {} }
 },
 replace~ lines~ globally/.code={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \spath_greplace_lines:c
     }
   } {#1} { {} }
 },
%    \end{macrocode}
%
%
% Remove the specified components.
%
%    \begin{macrocode}
 remove~ components/.code~ 2~ args={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \@@_remove_components:cn
     }
   } {#1} { {} {#2} }
 },
 remove~ components~ globally/.code~ 2~ args={
   \@@_maybe_current_path_reuse:nnn
   {
     \@@_check_path:nnn
     {
       \@@_gremove_components:cn
     }
   } {#1} { {} {#2} }
 },
%    \end{macrocode}
%
% This puts a conditional around the \texttt{spot weld} key because when figuring out a knot drawing then we will initially want to render it without the spot weld to keep the number of components constant.
%
%    \begin{macrocode}
 draft~ mode/.code={
   \@@_set_bool:Nn \l_@@_draft_bool {#1}
 },
 maybe~ spot~ weld/.code={
   \bool_if:NF \l_@@_draft_bool
   {
     \@@_maybe_current_path_reuse:nnn
     {
       \@@_check_path:nnn
       {
         \spath_spot_weld_components:c
       }
     } {#1} { {} }
   }
 },
 maybe~ spot~ weld~ globally/.code={
   \bool_if:NF \l_@@_draft_bool
   {
     \@@_maybe_current_path_reuse:nnn
     {
       \@@_check_path:nnn
       {
         \spath_spot_gweld_components:c
       }
     } {#1} { {} }
   }
 },
%    \end{macrocode}
%
% Set the transformation to lie along a path.
%
%    \begin{macrocode}
 transform~ to/.code~ 2~ args={
   \@@_maybe_current_path:nn
   {
     \@@_check_path:nnn {
       \@@_transform_to:nv {#2}
     }
   }
   {#1}
   {
     \pgfsettransformentries {1}{0}{0}{1}{0pt}{0pt}
   }
 },
%    \end{macrocode}
%
% As above, but with a possible extra \(180^\circ\) rotation if needed to ensure that the new \(y\)--axis points vaguely upwards.
%
%    \begin{macrocode}
 upright~ transform~ to/.code~ 2~ args={
   \@@_maybe_current_path:nn
   {
     \@@_check_path:nnn {
       \@@_transform_upright_to:nv {#2}
     }
   }
   {#1}
   {
     \pgfsettransformentries {1}{0}{0}{1}{0pt}{0pt}
   }
 },
%    \end{macrocode}
%
% This is a useful set of styles for drawing a knot diagram.
%
%    \begin{macrocode}
 knot/.style~ n~ args={3}{
   /tikz/spath/split~ at~ self~ intersections=#1,
   /tikz/spath/remove~ empty~ components=#1,
   /tikz/spath/insert~ gaps~ after~ components={#1}{#2}{#3},
   /tikz/spath/maybe~ spot~ weld=#1,
   /tikz/spath/render~ components=#1
 },
 global~ knot/.style~ n~ args={3}{
   /tikz/spath/split~ globally~ at~ self~ intersections=#1,
   /tikz/spath/remove~ empty~ components~ globally=#1,
   /tikz/spath/insert~ gaps~ globally ~after~ components={#1}{#2}{#3},
   /tikz/spath/maybe~ spot~ weld~ globally=#1,
   /tikz/spath/render~ components=#1
 },
%    \end{macrocode}
%
%    \begin{macrocode}
 arrow~ shortening/.code={
   \@@_set_bool:Nn \l_spath_arrow_shortening_bool {#1}
 },
%    \end{macrocode}
%
% For single argument commands which take a path as their argument, set the default to be \Verb+current+ so that they use the current path.
%
%    \begin{macrocode}
 show/.default=current,
 spot~ weld/.default=current,
 spot~ weld~ globally/.default=current,
 reverse/.default=current,
 reverse~ globally/.default=current,
 close/.default=current,
 close~ globally/.default=current,
 open/.default=current,
 open~ globally/.default=current,
 adjust~ and~ close/.default=current,
 adjust~ and~ close~ globally/.default=current,
 close~ with~ curve/.default=current,
 close~ globally~ with~ curve/.default=current,
 remove~ empty~ components/.default=current,
 remove~ empty~ components~ globally/.default=current,
 replace~ lines/.default=current,
 replace~ lines~ globally/.default=current,
 maybe~ spot~ weld/.default=current,
 maybe~ spot~ weld~ globally/.default=current,
}
%    \end{macrocode}
%
% This defines a coordinate system that finds a position on a soft path.
%
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_get_point_at:nn #1#2
{
 \group_begin:
 \spath_reallength:Nn \l_@@_tmpa_int {#2}
 \tl_set:Nx \l_@@_tmpb_tl
 {\fp_to_decimal:n {(#1) * (\l_@@_tmpa_int)}}
 \spath_point_at:NnV \l_@@_tmpc_tl {#2} \l_@@_tmpb_tl

 \tl_clear:N \l_@@_tmpd_tl
 \tl_put_right:Nn \l_@@_tmpd_tl {\pgf@x=}
 \tl_put_right:Nx \l_@@_tmpd_tl {\tl_item:Nn \l_@@_tmpc_tl {1}}
 \tl_put_right:Nn \l_@@_tmpd_tl {\relax}
 \tl_put_right:Nn \l_@@_tmpd_tl {\pgf@y=}
 \tl_put_right:Nx \l_@@_tmpd_tl {\tl_item:Nn \l_@@_tmpc_tl {2}}
 \tl_put_right:Nn \l_@@_tmpd_tl {\relax}

 \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpd_tl
 \group_end:
}
\cs_generate_variant:Nn \@@_get_point_at:nn {VV, Vn, Vv}

\tikzdeclarecoordinatesystem{spath}{%
 \group_begin:
 \tl_set:Nn \l_@@_tmpa_tl {#1}
 \tl_trim_spaces:N \l_@@_tmpa_tl

 \seq_set_split:NnV \l_@@_tmpa_seq {~} \l_@@_tmpa_tl
 \seq_pop_right:NN \l_@@_tmpa_seq \l_@@_tmpb_tl

 \tl_set:Nx \l_@@_tmpa_tl { \seq_use:Nn \l_@@_tmpa_seq {~} }

 \@@_maybe_current_path:nV
 {
   \@@_check_path:nnn {
     \@@_get_point_at:Vv \l_@@_tmpb_tl
   }
 }
 \l_@@_tmpa_tl
 {
   \tl_gset_eq:NN \g_@@_output_tl \pgfpointorigin
 }

 \group_end:
 \use:c {pgf@process}{%
   \tl_use:N \g_@@_output_tl
   \pgftransforminvert
   \use:c {pgf@pos@transform@glob}
 }
 \tl_gclear:N \g_@@_output_tl
}

\ExplSyntaxOff
%    \end{macrocode}
% \iffalse
%</tikzspath3>
% \fi
%
% \iffalse
%<*calligraphy>
% \fi
%
% \section{The Calligraphy Package}
%
%    \begin{macrocode}
%<@@=cal>
%    \end{macrocode}
%
% \subsection{Initialisation}
%    \begin{macrocode}
\RequirePackage{spath3}
\ExplSyntaxOn

\tl_new:N \l_@@_tmpa_tl
\tl_new:N \l_@@_tmpb_tl
\tl_new:N \l_@@_tmp_path_tl
\tl_new:N \l_@@_tmp_rpath_tl
\tl_new:N \l_@@_tmp_rpathb_tl
\tl_new:N \l_@@_tmp_patha_tl

\seq_new:N \l_@@_tmpa_seq

\int_new:N \l_@@_tmpa_int
\int_new:N \l_@@_tmpb_int
\int_new:N \g_@@_path_component_int
\int_new:N \g_@@_label_int

\fp_new:N \l_@@_tmpa_fp
\fp_new:N \l_@@_tmpb_fp
\fp_new:N \l_@@_tmpc_fp
\fp_new:N \l_@@_tmpd_fp
\fp_new:N \l_@@_tmpe_fp

\dim_new:N \l_@@_tmpa_dim
\dim_new:N \l_@@_tmpb_dim
\dim_new:N \l_@@_tmpc_dim
\dim_new:N \l_@@_tmpd_dim
\dim_new:N \l_@@_tmpe_dim
\dim_new:N \l_@@_tmpf_dim
\dim_new:N \l_@@_tmpg_dim
\dim_new:N \l_@@_tmph_dim

\bool_new:N \l_@@_annotate_bool
\bool_new:N \l_@@_taper_start_bool
\bool_new:N \l_@@_taper_end_bool
\bool_new:N \l_@@_taperable_bool

\dim_new:N \l_@@_taper_width_dim
\dim_new:N \l_@@_line_width_dim

\bool_set_true:N \l_@@_taper_start_bool
\bool_set_true:N \l_@@_taper_end_bool

\cs_generate_variant:Nn \tl_put_right:Nn {Nv}

\msg_new:nnn { calligraphy } { undefined pen } { The~ pen~ "#1"~ is~ not~ defined. }
%    \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_@@_tmpa_tl
     \spath_components_to_seq:NV \l_@@_tmpa_seq \l_@@_tmpa_tl
     \seq_gclear_new:c {g_@@_pen_\pgfkeysvalueof{/tikz/pen~name}_seq}
     \seq_gset_eq:cN
     {g_@@_pen_\pgfkeysvalueof{/tikz/pen~name}_seq} \l_@@_tmpa_seq
     \pgfusepath{discard}%
   }
 },
 define~pen/.default={default},
 use~pen/.code={
   \tikzset{pen~name=#1}
   \int_gzero:N \g_@@_path_component_int
   \cs_set_eq:NN \pgfpathmoveto \cal_moveto:n
   \tikz@addmode{
     \pgfsyssoftpath@getcurrentpath\l_@@_tmpa_tl
     \spath_components_to_seq:NV \l_@@_tmpa_seq \l_@@_tmpa_tl
     \tl_if_exist:cTF {g_@@_pen_\pgfkeysvalueof{/tikz/pen~name}_seq}
     {
       \cal_path_create:Nc \l_@@_tmpa_seq
       {g_@@_pen_\pgfkeysvalueof{/tikz/pen~name}_seq}
     }
     {
       \msg_warning:nnx { calligraphy } { undefined pen }
       { \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_@@_taper_start_bool
   }
   {
     \bool_set_false:N \l_@@_taper_start_bool
   }
 },
 taper~start/.default={true},
 taper~end/.code={
   \tl_if_eq:nnTF {#1} {true}
   {
     \bool_set_true:N \l_@@_taper_end_bool
   }
   {
     \bool_set_false:N \l_@@_taper_end_bool
   }
 },
 taper~end/.default={true},
 taper~width/.code={\dim_set:Nn \l_@@_taper_width_dim {#1}},
 nib~style/.code~2~args={
   \tl_clear_new:c {l_@@_nib_style_#1}
   \tl_set:cn {l_@@_nib_style_#1} {#2}
 },
 stroke~style/.code~2~args={
   \tl_clear_new:c {l_@@_stroke_style_#1}
   \tl_set:cn {l_@@_stroke_style_#1} {#2}
 },
 this~stroke~style/.code={
   \tl_clear_new:c
   {l_@@_stroke_inline_style_ \int_use:N \g_@@_path_component_int}
   \tl_set:cn
   {l_@@_stroke_inline_style_ \int_use:N \g_@@_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_@@_annotate_bool
   }
   {
     \bool_set_false:N \l_@@_annotate_bool
   }
 },
 annotate~reset/.code={
   \int_gzero:N \g_@@_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_clear_new:c {l_@@_annotation_style_ #1 _tl}
   \tl_set:cn {l_@@_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}[internal]{\cal_path_create:NN}
% This is the main command for creating the calligraphic paths.
% First argument is the given path
% Second argument is the pen path
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \cal_path_create:NN #1#2
{
 \int_zero:N \l_@@_tmpa_int
 \seq_map_inline:Nn #1
 {
   \int_compare:nT {\tl_count:n {##1} > 3}
   {

     \int_incr:N \l_@@_tmpa_int
     \int_zero:N \l_@@_tmpb_int

     \tl_set:Nn \l_@@_tmp_path_tl {##1}
     \spath_open:N \l_@@_tmp_path_tl
     \spath_reverse:NV \l_@@_tmp_rpath_tl \l_@@_tmp_path_tl

     \seq_map_inline:Nn #2
     {
       \int_incr:N \l_@@_tmpb_int
       \group_begin:
       \pgfsys@beginscope
       \cal_apply_style:c {l_@@_stroke_style_ \int_use:N \l_@@_tmpa_int}
       \cal_apply_style:c {l_@@_stroke_inline_style_ \int_use:N \l_@@_tmpa_int}
       \cal_apply_style:c {l_@@_nib_style_ \int_use:N \l_@@_tmpb_int}

       \spath_initialpoint:Nn \l_@@_tmpa_tl {####1}
       \tl_set_eq:NN \l_@@_tmp_patha_tl \l_@@_tmp_path_tl
       \spath_translate:NV \l_@@_tmp_patha_tl \l_@@_tmpa_tl

       \int_compare:nTF {\tl_count:n {####1} == 3}
       {
         \cal_at_least_three:N \l_@@_tmp_patha_tl
         \spath_protocol_path:V \l_@@_tmp_patha_tl

         \tikz@options
         \dim_set:Nn \l_@@_line_width_dim {\pgflinewidth}
         \cal_maybe_taper:N \l_@@_tmp_patha_tl
       }
       {
         \spath_weld:Nn \l_@@_tmp_patha_tl {####1}
         \spath_weld:NV \l_@@_tmp_patha_tl \l_@@_tmp_rpath_tl
         \spath_reverse:Nn \l_@@_tmp_rpathb_tl {####1}
         \spath_weld:NV \l_@@_tmp_patha_tl \l_@@_tmp_rpathb_tl

         \tl_clear:N \l_@@_tmpa_tl
         \tl_set:Nn \l_@@_tmpa_tl
         {
           fill=\pgfkeysvalueof{/tikz/pen~colour},
           draw=none
         }
         \tl_if_exist:cT  {l_@@_stroke_style_ \int_use:N \l_@@_tmpa_int}
         {
           \tl_put_right:Nv \l_@@_tmpa_tl
           {l_@@_stroke_style_ \int_use:N \l_@@_tmpa_int}
         }
         \tl_if_exist:cT  {l_@@_stroke_inline_style_ \int_use:N \l_@@_tmpa_int}
         {
           \tl_put_right:Nn \l_@@_tmpa_tl {,}
           \tl_put_right:Nv \l_@@_tmpa_tl
           {l_@@_stroke_inline_style_ \int_use:N \l_@@_tmpa_int}
         }
         \tl_if_exist:cT  {l_@@_nib_style_ \int_use:N \l_@@_tmpb_int}
         {
           \tl_put_right:Nn \l_@@_tmpa_tl {,}
           \tl_put_right:Nv \l_@@_tmpa_tl
           {l_@@_nib_style_ \int_use:N \l_@@_tmpb_int}
         }
         \spath_tikz_path:VV \l_@@_tmpa_tl \l_@@_tmp_patha_tl
       }
       \pgfsys@endscope
       \group_end:
     }

     \bool_if:NT \l_@@_annotate_bool
     {
       \seq_get_right:NN #2 \l_@@_tmpa_tl
       \spath_finalpoint:NV \l_@@_tmpa_tl \l_@@_tmpa_tl
       \spath_translate:NV \l_@@_tmp_path_tl \l_@@_tmpa_tl
       \tikz@scan@one@point
       \pgfutil@firstofone
       \pgfkeysvalueof{/tikz/annotation~shift}

       \spath_translate:Nnn \l_@@_tmp_path_tl {\pgf@x} {\pgf@y}

       \pgfkeysgetvalue{/tikz/annotation~style}{\l_@@_tmpa_tl}
       \spath_tikz_path:VV \l_@@_tmpa_tl \l_@@_tmp_path_tl

       \spath_finalpoint:NV \l_@@_tmpa_tl \l_@@_tmp_path_tl

       \exp_last_unbraced:NV \pgfqpoint \l_@@_tmpa_tl
       \begin{scope}[reset~ cm]
       \node[
         every~annotation~node/.try,
         tl~use:c = {l_@@_annotation_style_ \int_use:N \l_@@_tmpa_int _tl}
       ] at (\pgf@x,\pgf@y) {\int_use:N \l_@@_tmpa_int};
       \end{scope}
     }
   }
 }
}
\cs_generate_variant:Nn \cal_path_create:NN {Nc}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\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 \g_@@_path_component_int
 \cal_orig_moveto:n {#1}
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\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}[internal]{\cal_at_least_three:Nn}
% 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_protected_nopar:Npn \cal_at_least_three:Nn #1#2
{
 \spath_reallength:Nn \l_@@_tmpa_int {#2}
 \tl_clear:N \l_@@_tmpb_tl
 \tl_set:Nn \l_@@_tmpb_tl {#2}
 \int_compare:nTF {\l_@@_tmpa_int = 1}
 {
   \spath_split_at:Nn \l_@@_tmpb_tl {2/3}
   \spath_split_at:Nn \l_@@_tmpb_tl {1/2}
 }
 {
   \int_compare:nT {\l_@@_tmpa_int = 2}
   {
     \spath_split_at:Nn \l_@@_tmpb_tl {1.5}
     \spath_split_at:Nn \l_@@_tmpb_tl {.5}
   }
 }
 \tl_set_eq:NN #1 \l_@@_tmpb_tl
}
\cs_generate_variant:Nn \cal_at_least_three:Nn {NV}
\cs_new_protected_nopar:Npn \cal_at_least_three:N #1
{
 \cal_at_least_three:NV #1#1
}
\cs_generate_variant:Nn \cal_at_least_three:N {c}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\cal_maybe_taper:N}
% Possibly tapers the path, depending on the booleans.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \cal_maybe_taper:N #1
{
 \tl_set_eq:NN \l_@@_tmpa_tl #1

 \bool_if:NT \l_@@_taper_start_bool
 {

   \dim_set:Nn \l_@@_tmpa_dim {\tl_item:Nn \l_@@_tmpa_tl {2}}
   \dim_set:Nn \l_@@_tmpb_dim {\tl_item:Nn \l_@@_tmpa_tl {3}}
   \tl_set:Nx \l_@@_tmpb_tl {\tl_item:Nn \l_@@_tmpa_tl {4}}

   \token_case_meaning:NnF \l_@@_tmpb_tl
   {
     \c_spath_lineto_tl
     {

       \bool_set_true:N \l_@@_taperable_bool
       \dim_set:Nn \l_@@_tmpg_dim {\tl_item:Nn \l_@@_tmpa_tl {5}}
       \dim_set:Nn \l_@@_tmph_dim {\tl_item:Nn \l_@@_tmpa_tl {6}}
       \dim_set:Nn \l_@@_tmpc_dim {(2\l_@@_tmpa_dim + \l_@@_tmpg_dim)/3}
       \dim_set:Nn \l_@@_tmpd_dim {(2\l_@@_tmpb_dim + \l_@@_tmph_dim)/3}
       \dim_set:Nn \l_@@_tmpe_dim {(\l_@@_tmpa_dim + 2\l_@@_tmpg_dim)/3}
       \dim_set:Nn \l_@@_tmpf_dim {(\l_@@_tmpb_dim + 2\l_@@_tmph_dim)/3}
       \prg_replicate:nn {4}
       {
         \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
       }
       \tl_put_left:NV \l_@@_tmpa_tl \c_spath_moveto_tl
     }
     \c_spath_curvetoa_tl
     {
       \bool_set_true:N \l_@@_taperable_bool
       \dim_set:Nn \l_@@_tmpc_dim {\tl_item:Nn \l_@@_tmpa_tl {5}}
       \dim_set:Nn \l_@@_tmpd_dim {\tl_item:Nn \l_@@_tmpa_tl {6}}
       \dim_set:Nn \l_@@_tmpe_dim {\tl_item:Nn \l_@@_tmpa_tl {8}}
       \dim_set:Nn \l_@@_tmpf_dim {\tl_item:Nn \l_@@_tmpa_tl {9}}
       \dim_set:Nn \l_@@_tmpg_dim {\tl_item:Nn \l_@@_tmpa_tl {11}}
       \dim_set:Nn \l_@@_tmph_dim {\tl_item:Nn \l_@@_tmpa_tl {12}}
       \prg_replicate:nn {10}
       {
         \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
       }
       \tl_put_left:NV \l_@@_tmpa_tl \c_spath_moveto_tl
     }
   }
   {
     \bool_set_false:N \l_@@_taperable_bool
   }

   \bool_if:NT \l_@@_taperable_bool
   {
     \@@_taper_aux:
   }

 }

 \bool_if:NT \l_@@_taper_end_bool
 {

   \dim_set:Nn \l_@@_tmpa_dim {\tl_item:Nn \l_@@_tmpa_tl {-2}}
   \dim_set:Nn \l_@@_tmpb_dim {\tl_item:Nn \l_@@_tmpa_tl {-1}}
   \tl_set:Nx \l_@@_tmpb_tl {\tl_item:Nn \l_@@_tmpa_tl {-3}}

   \token_case_meaning:NnF \l_@@_tmpb_tl
   {
     \c_spath_lineto_tl
     {

       \bool_set_true:N \l_@@_taperable_bool
       \dim_set:Nn \l_@@_tmpg_dim {\tl_item:Nn \l_@@_tmpa_tl {-5}}
       \dim_set:Nn \l_@@_tmph_dim {\tl_item:Nn \l_@@_tmpa_tl {-4}}
       \dim_set:Nn \l_@@_tmpc_dim {(2\l_@@_tmpa_dim + \l_@@_tmpg_dim)/3}
       \dim_set:Nn \l_@@_tmpd_dim {(2\l_@@_tmpb_dim + \l_@@_tmph_dim)/3}
       \dim_set:Nn \l_@@_tmpe_dim {(\l_@@_tmpa_dim + 2\l_@@_tmpg_dim)/3}
       \dim_set:Nn \l_@@_tmpf_dim {(\l_@@_tmpb_dim + 2\l_@@_tmph_dim)/3}
       \tl_reverse:N \l_@@_tmpa_tl
       \prg_replicate:nn {3}
       {
         \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
       }
       \tl_reverse:N \l_@@_tmpa_tl
     }
     \c_spath_curveto_tl
     {
       \bool_set_true:N \l_@@_taperable_bool
       \dim_set:Nn \l_@@_tmpc_dim {\tl_item:Nn \l_@@_tmpa_tl {-5}}
       \dim_set:Nn \l_@@_tmpd_dim {\tl_item:Nn \l_@@_tmpa_tl {-4}}
       \dim_set:Nn \l_@@_tmpe_dim {\tl_item:Nn \l_@@_tmpa_tl {-8}}
       \dim_set:Nn \l_@@_tmpf_dim {\tl_item:Nn \l_@@_tmpa_tl {-7}}
       \dim_set:Nn \l_@@_tmpg_dim {\tl_item:Nn \l_@@_tmpa_tl {-11}}
       \dim_set:Nn \l_@@_tmph_dim {\tl_item:Nn \l_@@_tmpa_tl {-10}}
       \tl_reverse:N \l_@@_tmpa_tl
       \prg_replicate:nn {9}
       {
         \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
       }
       \tl_reverse:N \l_@@_tmpa_tl
     }
   }
   {
     \bool_set_false:N \l_@@_taperable_bool
   }

   \bool_if:NT \l_@@_taperable_bool
   {
     \@@_taper_aux:
   }

 }

 \pgfsyssoftpath@setcurrentpath\l_@@_tmpa_tl
 \pgfsetstrokecolor{\pgfkeysvalueof{/tikz/pen~colour}}
 \pgfusepath{stroke}

}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_taper_aux:}
% Auxiliary macro to avoid unnecessary code duplication.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_taper_aux:
{
 \tl_clear:N \l_@@_tmpb_tl
 \tl_put_right:NV \l_@@_tmpb_tl \c_spath_moveto_tl

 \fp_set:Nn \l_@@_tmpa_fp
 {
   \l_@@_tmpd_dim - \l_@@_tmpb_dim
 }
 \fp_set:Nn \l_@@_tmpb_fp
 {
   \l_@@_tmpa_dim - \l_@@_tmpc_dim
 }
 \fp_set:Nn \l_@@_tmpe_fp
 {
   (\l_@@_tmpa_fp^2 + \l_@@_tmpb_fp^2)^.5
 }

 \fp_set:Nn \l_@@_tmpa_fp
 {
   .5*\l_@@_taper_width_dim
   *
   \l_@@_tmpa_fp / \l_@@_tmpe_fp
 }
 \fp_set:Nn \l_@@_tmpb_fp
 {
   .5*\l_@@_taper_width_dim
   *
   \l_@@_tmpb_fp / \l_@@_tmpe_fp
 }

 \fp_set:Nn \l_@@_tmpc_fp
 {
   \l_@@_tmph_dim - \l_@@_tmpf_dim
 }
 \fp_set:Nn \l_@@_tmpd_fp
 {
   \l_@@_tmpe_dim - \l_@@_tmpg_dim
 }
 \fp_set:Nn \l_@@_tmpe_fp
 {
   (\l_@@_tmpc_fp^2 + \l_@@_tmpd_fp^2)^.5
 }

 \fp_set:Nn \l_@@_tmpc_fp
 {
   .5*\l_@@_line_width_dim
   *
   \l_@@_tmpc_fp / \l_@@_tmpe_fp
 }
 \fp_set:Nn \l_@@_tmpd_fp
 {
   .5*\l_@@_line_width_dim
   *
   \l_@@_tmpd_fp / \l_@@_tmpe_fp
 }

 \tl_put_right:Nx \l_@@_tmpb_tl
 {
   {\dim_eval:n { \fp_to_dim:N \l_@@_tmpa_fp + \l_@@_tmpa_dim}}
   {\dim_eval:n { \fp_to_dim:N \l_@@_tmpb_fp + \l_@@_tmpb_dim}}
 }

 \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curvetoa_tl

 \tl_put_right:Nx \l_@@_tmpb_tl
 {
   {\dim_eval:n { \fp_to_dim:N \l_@@_tmpa_fp + \l_@@_tmpc_dim}}
   {\dim_eval:n { \fp_to_dim:N \l_@@_tmpb_fp + \l_@@_tmpd_dim}}
 }

 \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curvetob_tl

 \tl_put_right:Nx \l_@@_tmpb_tl
 {
   {\dim_eval:n { \fp_to_dim:N \l_@@_tmpc_fp + \l_@@_tmpe_dim}}
   {\dim_eval:n { \fp_to_dim:N \l_@@_tmpd_fp + \l_@@_tmpf_dim}}
 }

 \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curveto_tl

 \tl_put_right:Nx \l_@@_tmpb_tl
 {
   {\dim_eval:n { \fp_to_dim:N \l_@@_tmpc_fp + \l_@@_tmpg_dim}}
   {\dim_eval:n { \fp_to_dim:N \l_@@_tmpd_fp + \l_@@_tmph_dim}}
 }

 \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curvetoa_tl

 \tl_put_right:Nx \l_@@_tmpb_tl
 {
   {
     \dim_eval:n
     {
       \fp_to_dim:N \l_@@_tmpc_fp + \l_@@_tmpg_dim
       - \fp_to_dim:n{ 1.32 * \l_@@_tmpd_fp
       }
     }
   }
   {
     \dim_eval:n
     {
       \fp_to_dim:N \l_@@_tmpd_fp + \l_@@_tmph_dim
       + \fp_to_dim:n {1.32* \l_@@_tmpc_fp
       }
     }
   }
 }

 \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curvetob_tl

 \tl_put_right:Nx \l_@@_tmpb_tl
 {
   {
     \dim_eval:n
     {
       -\fp_to_dim:N \l_@@_tmpc_fp + \l_@@_tmpg_dim
       - \fp_to_dim:n {1.32 * \l_@@_tmpd_fp
       }
     }
   }
   {
     \dim_eval:n
     {
       -\fp_to_dim:N \l_@@_tmpd_fp + \l_@@_tmph_dim
       + \fp_to_dim:n {1.32 * \l_@@_tmpc_fp
       }
     }
   }
 }

 \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curveto_tl

 \tl_put_right:Nx \l_@@_tmpb_tl
 {
   {\dim_eval:n { -\fp_to_dim:N \l_@@_tmpc_fp + \l_@@_tmpg_dim}}
   {\dim_eval:n { -\fp_to_dim:N \l_@@_tmpd_fp + \l_@@_tmph_dim}}
 }

 \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curvetoa_tl

 \tl_put_right:Nx \l_@@_tmpb_tl
 {
   {\dim_eval:n { -\fp_to_dim:N \l_@@_tmpc_fp + \l_@@_tmpe_dim}}
   {\dim_eval:n { -\fp_to_dim:N \l_@@_tmpd_fp + \l_@@_tmpf_dim}}
 }

 \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curvetob_tl

 \tl_put_right:Nx \l_@@_tmpb_tl
 {
   {\dim_eval:n { -\fp_to_dim:N \l_@@_tmpa_fp + \l_@@_tmpc_dim}}
   {\dim_eval:n { -\fp_to_dim:N \l_@@_tmpb_fp + \l_@@_tmpd_dim}}
 }

 \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curveto_tl

 \tl_put_right:Nx \l_@@_tmpb_tl
 {
   {\dim_eval:n { -\fp_to_dim:N \l_@@_tmpa_fp + \l_@@_tmpa_dim}}
   {\dim_eval:n { -\fp_to_dim:N \l_@@_tmpb_fp + \l_@@_tmpb_dim}}
 }

 \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curvetoa_tl

 \tl_put_right:Nx \l_@@_tmpb_tl
 {
   {
     \dim_eval:n
     {
       -\fp_to_dim:N \l_@@_tmpa_fp + \l_@@_tmpa_dim
       + \fp_to_dim:n{ 1.32 * \l_@@_tmpb_fp}
     }
   }
   {
     \dim_eval:n
     {
       -\fp_to_dim:N \l_@@_tmpb_fp + \l_@@_tmpb_dim
       - \fp_to_dim:n {1.32* \l_@@_tmpa_fp}
     }
   }
 }

 \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curvetob_tl

 \tl_put_right:Nx \l_@@_tmpb_tl
 {
   {
     \dim_eval:n
     {
       \fp_to_dim:N \l_@@_tmpa_fp + \l_@@_tmpa_dim
       + \fp_to_dim:n {1.32 * \l_@@_tmpb_fp}
     }
   }
   {
     \dim_eval:n
     {
       \fp_to_dim:N \l_@@_tmpb_fp + \l_@@_tmpb_dim
       - \fp_to_dim:n {1.32 * \l_@@_tmpa_fp}
     }
   }
 }

 \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curveto_tl

 \tl_put_right:Nx \l_@@_tmpb_tl
 {
   {\dim_eval:n { \fp_to_dim:N \l_@@_tmpa_fp + \l_@@_tmpa_dim}}
   {\dim_eval:n { \fp_to_dim:N \l_@@_tmpb_fp + \l_@@_tmpb_dim}}
 }

 \pgfsyssoftpath@setcurrentpath\l_@@_tmpb_tl
 \pgfsetfillcolor{\pgfkeysvalueof{/tikz/pen~colour}}
 \pgfusepath{fill}
}
%    \end{macrocode}
% \end{macro}
%
% Defines a copperplate pen.
%    \begin{macrocode}
\tl_set:Nn \l_@@_tmpa_tl {\pgfsyssoftpath@movetotoken{0pt}{0pt}}
\spath_components_to_seq:NV \l_@@_tmpa_seq \l_@@_tmpa_tl
\seq_gclear_new:N \g_@@_pen_copperplate_seq
\seq_gset_eq:NN \g_@@_pen_copperplate_seq \l_@@_tmpa_seq
%    \end{macrocode}
%
% \begin{macro}[internal]{\CopperplatePath}
% This is used in the decorations section to convert a path to a copperplate path.
%    \begin{macrocode}
\DeclareDocumentCommand \CopperplatePath { m }
{
 \spath_components_to_seq:NV \l_@@_tmpa_seq #1
 \cal_path_create:NN \l_@@_tmpa_seq \g_@@_pen_copperplate_seq
}
%    \end{macrocode}
% \end{macro}
%
%    \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%
   \CopperplatePath{\cal@tmp@path}%
 }%
 \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%
   \CopperplatePath{\cal@tmp@path}%
 }%
 \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%
   \CopperplatePath{\cal@tmp@path}%
 }%
 \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}
%
%    \begin{macrocode}
%<@@=knot>
%    \end{macrocode}
%
% \subsection{Initialisation}
%
% We load the \Verb+spath3+ library and the \Verb+intersections+ TikZ library.
% Then we get going.
%    \begin{macrocode}
\RequirePackage{spath3}
\usetikzlibrary{intersections,spath3}

\ExplSyntaxOn

\tl_new:N \l_@@_tmpa_tl
\tl_new:N \l_@@_tmpb_tl
\tl_new:N \l_@@_tmpc_tl
\tl_new:N \l_@@_tmpd_tl
\tl_new:N \l_@@_tmpg_tl
\tl_new:N \l_@@_redraws_tl
\tl_new:N \l_@@_clip_width_tl
\tl_new:N \l_@@_name_tl
\tl_new:N \l_@@_node_tl
\tl_new:N \l_@@_aux_tl
\tl_new:N \l_@@_auxa_tl
\tl_new:N \l_@@_prefix_tl

\seq_new:N \l_@@_segments_seq

\int_new:N \l_@@_tmpa_int
\int_new:N \l_@@_strands_int
\int_new:N \g_@@_intersections_int
\int_new:N \g_@@_filaments_int
\int_new:N \l_@@_component_start_int

\fp_new:N \l_@@_tmpa_fp
\fp_new:N \l_@@_tmpb_fp

\dim_new:N \l_@@_tmpa_dim
\dim_new:N \l_@@_tmpb_dim
\dim_new:N \l_@@_tolerance_dim
\dim_new:N \l_@@_redraw_tolerance_dim
\dim_new:N \l_@@_clip_bg_radius_dim
\dim_new:N \l_@@_clip_draw_radius_dim

\bool_new:N \l_@@_draft_bool
\bool_new:N \l_@@_ignore_ends_bool
\bool_new:N \l_@@_self_intersections_bool
\bool_new:N \l_@@_splits_bool
\bool_new:N \l_@@_super_draft_bool

\bool_new:N \l_@@_prepend_prev_bool
\bool_new:N \l_@@_append_next_bool
\bool_new:N \l_@@_skip_bool
\bool_new:N \l_@@_save_bool
\bool_new:N \l_@@_debugging_bool

\seq_new:N \g_@@_nodes_seq

\bool_set_true:N \l_@@_ignore_ends_bool
%    \end{macrocode}
%
% Configuration is via TikZ keys and styles.
%    \begin{macrocode}
\tikzset{
 spath/prefix/knot/.style={
   spath/set~ prefix=knot strand,
 },
 spath/suffix/knot/.style={
   spath/set~ suffix={},
 },
 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_@@_gap_tl \pgflinewidth}}
     }
   }
 },
 knot~ gap/.store~ in=\l_@@_gap_tl,
 knot~ gap=3,
 knot~ diagram/.is~family,
 knot~ diagram/.unknown/.code={
   \tl_set_eq:NN \l_@@_tmpa_tl \pgfkeyscurrentname
   \pgfkeysalso{
     /tikz/\l_@@_tmpa_tl=#1
   }
 },
 background~ colour/.code={%
   \colorlet{knotbg}{#1}%
 },
 background~ color/.code={%
   \colorlet{knotbg}{#1}%
 },
 background~ colour=white,
 knot~ diagram,
 name/.store~ in=\l_@@_name_tl,
 name={knot},
 save~ intersections/.is~ choice,
 save~ intersections/.default=true,
 save~ intersections/true/.code={
   \bool_set_true:N \l_@@_save_bool
 },
 save~ intersections/false/.code={
   \bool_set_false:N \l_@@_save_bool
 },
 every~ strand/.style={draw},
 ignore~ endpoint~ intersections/.code={
   \tl_if_eq:nnTF {#1} {true}
   {
     \bool_set_true:N \l_@@_ignore_ends_bool
   }
   {
     \bool_set_false:N \l_@@_ignore_ends_bool
   }
 },
 ignore~ endpoint~ intersections/.default=true,
 consider~ self~ intersections/.is~choice,
 consider~ self~ intersections/true/.code={
   \bool_set_true:N \l_@@_self_intersections_bool
   \bool_set_true:N \l_@@_splits_bool
 },
 consider~ self~ intersections/false/.code={
   \bool_set_false:N \l_@@_self_intersections_bool
   \bool_set_false:N \l_@@_splits_bool
 },
 consider~ self~ intersections/no~ splits/.code={
   \bool_set_true:N \l_@@_self_intersections_bool
   \bool_set_false:N \l_@@_splits_bool
 },
 consider~ self~ intersections/.default={true},
 clip~ radius/.code={
   \dim_set:Nn \l_@@_clip_bg_radius_dim {#1}
   \dim_set:Nn \l_@@_clip_draw_radius_dim {#1+2pt}
 },
 clip~ draw~ radius/.code={
   \dim_set:Nn \l_@@_clip_draw_radius_dim {#1}
 },
 clip~ background~ radius/.code={
   \dim_set:Nn \l_@@_clip_bg_radius_dim {#1}
 },
 clip~ radius=10pt,
 end~ tolerance/.code={
   \dim_set:Nn \l_@@_tolerance_dim {#1}
 },
 end~ tolerance=14pt,
 clip/.style={
   clip
 },
 background~ clip/.style={
   clip
 },
 clip~ width/.code={
   \tl_set:Nn \l_@@_clip_width_tl {#1}
 },
 clip~ width=3,
 flip~ crossing/.code={%
   \tl_clear_new:c {l_@@_crossing_#1}
   \tl_set:cn {l_@@_crossing_#1} {x}
 },
 ignore~ crossing/.code={%
   \tl_clear_new:c {l_@@_ignore_crossing_#1}
   \tl_set:cn {l_@@_ignore_crossing_#1} {x}
 },
 draft~ mode/.is~ choice,
 draft~ mode/off/.code={%
   \bool_set_false:N \l_@@_draft_bool
   \bool_set_false:N \l_@@_super_draft_bool
 },
 draft~ mode/crossings/.code={%
   \bool_set_true:N \l_@@_draft_bool
   \bool_set_false:N \l_@@_super_draft_bool
 },
 draft~ mode/strands/.code={%
   \bool_set_true:N \l_@@_draft_bool
   \bool_set_true:N \l_@@_super_draft_bool
 },
 debug/.is~ choice,
 debug/true/.code={
   \bool_set_true:N \l_@@_debugging_bool
 },
 debug/false/.code={
   \bool_set_false:N \l_@@_debugging_bool
 },
 debug/.default=true,
 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}
%
% \begin{macro}[internal]{\knot_debug:n}
% Debugging
%    \begin{macrocode}
\cs_new_nopar:Npn \knot_debug:n #1
{
 \bool_if:NT \l_@@_debugging_bool
 {
   \iow_term:n {===Knot~ debug: #1===}
 }
}

\cs_new_nopar:Npn \knot_show_strand:n #1
{
 \bool_if:NT \l_@@_debugging_bool
 {
   \iow_term:n {===Knot~ debug: #1===}
   \spath_show:v {knot #1}
 }
}

\cs_generate_variant:Nn \knot_debug:n {x}
%    \end{macrocode}
% \end{macro}
%
% 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
{
 \knot_debug:n {knot~ apply~ style}
 \tl_if_exist:NT #1 {
   \exp_args:NV \tikzset #1
 }
}
\cs_generate_variant:Nn \knot_apply_style:N {c}
%    \end{macrocode}
%
%\begin{macro}[internal]{\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}[internal]{\strand}
% This is how the user specifies a strand of the knot.
%    \begin{macrocode}
\NewDocumentCommand \strand { O{} }
{
 \int_incr:N \l_@@_strands_int
 \tl_clear_new:c {l_@@_options_strand \int_use:N \l_@@_strands_int}
 \tl_set:cn {l_@@_options_strand \int_use:N \l_@@_strands_int} {#1}
 \path[#1,spath/set~ name=knot,spath/save=\int_use:N \l_@@_strands_int]
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{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}[internal]{\knot_initialise:n}
% Set up some stuff before loading in the strands.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \knot_initialise:n #1
{
 \knot_debug:n {knot~ initialise}
 \tikzset{knot~ diagram/.cd,every~ knot~ diagram/.try,#1}
 \int_zero:N \l_@@_strands_int
 \tl_clear:N \l_@@_redraws_tl
 \seq_gclear:N \g_@@_nodes_seq
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\knot_render:}
% This is the code that starts the work of rendering the knot.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \knot_render:
{
 \knot_debug:n {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}
% Set the dimension for deciding when to include neighbouring strands
%    \begin{macrocode}
 \dim_set:Nn \l_@@_redraw_tolerance_dim {\fp_to_dim:n
   {
     sqrt(2) * max(\l_@@_clip_bg_radius_dim, \l_@@_clip_draw_radius_dim)
   }
 }
%    \end{macrocode}
% Loop through the strands drawing each one for the first time.
%    \begin{macrocode}
 \int_step_function:nnnN {1} {1} {\l_@@_strands_int} \knot_draw_strand:n
%    \end{macrocode}
% In super draft mode we don't do anything else.
%    \begin{macrocode}
 \bool_if:NF \l_@@_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_@@_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_@@_self_intersections_bool
   {
     \knot_split_strands:
     \int_set_eq:NN \l_@@_tmpa_int \g_@@_filaments_int
     \tl_set:Nn \l_@@_prefix_tl {filament}
   }
   {
     \int_set_eq:NN \l_@@_tmpa_int \l_@@_strands_int
     \tl_set:Nn \l_@@_prefix_tl {strand}
   }
%    \end{macrocode}
% Initialise the intersection count.
%    \begin{macrocode}
   \int_gzero:N \g_@@_intersections_int
%    \end{macrocode}
% If in draft mode we label the intersections, otherwise we just stick a coordinate at each one.
%    \begin{macrocode}
   \tl_clear:N \l_@@_node_tl
   \bool_if:NT \l_@@_draft_bool
   {
     \tl_set:Nn \l_@@_node_tl {
       \exp_not:N \node[coordinate,
         pin={[
             node~ contents={\int_use:N \g_@@_intersections_int},
             knot~ diagram/draft/crossing~ label,
             knot~ diagram/draft/crossing~
             \int_use:N \g_@@_intersections_int \c_space_tl label/.try
           ]
           }]
     }
   }
%    \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_@@_tmpa_int - 1} \l_@@_tmpa_tl
   {
     \int_step_variable:nnnNn
     {\tl_use:N \l_@@_tmpa_tl + 1}
     {1}
     {\l_@@_tmpa_int} \l_@@_tmpb_tl
     {
       \knot_intersections:VV \l_@@_tmpa_tl \l_@@_tmpb_tl
     }
   }
%    \end{macrocode}
% If any redraws were requested, do them here.
%    \begin{macrocode}
   \tl_use:N \l_@@_redraws_tl
%    \end{macrocode}
% Draw the crossing nodes
%    \begin{macrocode}
   \seq_use:Nn \g_@@_nodes_seq {}
 }
%    \end{macrocode}
% Close the scope
%    \begin{macrocode}
 \endpgfscope
 \knot_debug:x {knot~rendered,
   ~found~\int_use:N  \g_@@_intersections_int \c_space_tl~intersections}
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\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
{
 \knot_debug:n {knot~ draw~ strand~ #1}
 \pgfscope
 \group_begin:
 \spath_bake_round:c {knot strand #1}
 \tl_set:Nn \l_@@_tmpa_tl {knot~ diagram/every~ strand/.try,}
 \tl_put_right:Nv \l_@@_tmpa_tl {l_@@_options_strand #1}
 \tl_put_right:Nn \l_@@_tmpa_tl
 {
   ,
   knot~ diagram/only~ when~ rendering/.try,
   only~ when~ rendering/.try,
 }
 \knot_show_strand:n {strand #1}
 \spath_tikz_path:Vv \l_@@_tmpa_tl {knot strand #1}
 \group_end:
 \endpgfscope
}
\cs_generate_variant:Nn \tl_put_right:Nn {Nv}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\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
{
 \knot_debug:n {knot~ draw~ labels}
 \bool_if:NT \l_@@_draft_bool
 {
   \spath_finalpoint:Nv \l_@@_tmpb_tl {knot strand #1}
   \dim_set:Nn \l_@@_tmpa_dim {\tl_item:Nn \l_@@_tmpb_tl {1}}
   \dim_set:Nn \l_@@_tmpb_dim {\tl_item:Nn \l_@@_tmpb_tl {2}}
   \node[
     knot~ diagram/draft/strand~label
   ] at (\l_@@_tmpa_dim,\l_@@_tmpb_dim) {#1};
   \spath_initialpoint:Nv \l_@@_tmpb_tl {knot strand #1}
   \dim_set:Nn \l_@@_tmpa_dim {\tl_item:Nn \l_@@_tmpb_tl {1}}
   \dim_set:Nn \l_@@_tmpb_dim {\tl_item:Nn \l_@@_tmpb_tl {2}}
   \node[
     knot~ diagram/draft/strand~label
   ] at (\l_@@_tmpa_dim,\l_@@_tmpb_dim) {#1};
 }
 \bool_if:nT {
   \l_@@_self_intersections_bool
   &&
   \l_@@_splits_bool
 }
 {
   \tl_clear:N \l_@@_tmpa_tl
   \spath_remove_empty_components:c {knot strand #1}
   \spath_initialpoint:Nv \l_@@_tmpa_tl {knot strand #1}
   \tl_put_left:NV \l_@@_tmpa_tl \c_spath_moveto_tl
   \spath_segments_to_seq:Nv \l_@@_segments_seq {knot strand #1}
   \seq_map_function:NN \l_@@_segments_seq \knot_split_self_intersects:N
   \tl_set_eq:cN {knot strand #1} \l_@@_tmpa_tl
 }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\knot_split_self_intersects:N}
% 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:N #1
{
 \knot_debug:n {knot~ split~ self~ intersects}
 \tl_set:Nx \l_@@_tmpc_tl {\tl_item:nn {#1} {4}}
 \token_case_meaning:NnF \l_@@_tmpc_tl
 {
   \c_spath_curvetoa_tl
   {
     \fp_set:Nn \l_@@_tmpa_fp
     {
       (\tl_item:nn {#1} {3} - 3 * \tl_item:nn {#1} {6}
       + 3 * \tl_item:nn {#1} {9} - \tl_item:nn {#1} {12})
       *
       (3 * \tl_item:nn {#1} {8} - 3 * \tl_item:nn {#1} {11})
       -
       (\tl_item:nn {#1} {2} - 3 * \tl_item:nn {#1} {5}
       + 3 * \tl_item:nn {#1} {8} - \tl_item:nn {#1} {11})
       *
       (3 * \tl_item:nn {#1} {9} - 3 * \tl_item:nn {#1} {12})
     }
     \fp_set:Nn \l_@@_tmpb_fp
     {
       (\tl_item:nn {#1} {2} - 3 * \tl_item:nn {#1} {5}
       + 3 * \tl_item:nn {#1} {8} - \tl_item:nn {#1} {11})
       *
       (3 * \tl_item:nn {#1} {6} - 6 * \tl_item:nn {#1} {9}
       + 3 * \tl_item:nn {#1} {12})
       -
       (\tl_item:nn {#1} {3} - 3 * \tl_item:nn {#1} {6}
       + 3 * \tl_item:nn {#1} {9} - \tl_item:nn {#1} {12})
       *
       (3 * \tl_item:nn {#1} {5} - 6 * \tl_item:nn {#1} {8}
       + 3 * \tl_item:nn {#1} {11})
     }
     \fp_compare:nTF
     {
       \l_@@_tmpb_fp != 0
     }
     {
       \fp_set:Nn \l_@@_tmpa_fp {.5 * \l_@@_tmpa_fp / \l_@@_tmpb_fp}
       \bool_if:nTF
       {
         \fp_compare_p:n {0 < \l_@@_tmpa_fp}
         &&
         \fp_compare_p:n {\l_@@_tmpa_fp < 1}
       }
       {
         \spath_split_curve:NNnV
         \l_@@_tmpc_tl
         \l_@@_tmpd_tl
         {#1}
         \l_@@_tmpa_fp
         \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl}
         \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl}
         \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl}
         \tl_set:Nx \l_@@_tmpd_tl {\tl_tail:N \l_@@_tmpd_tl}
         \tl_set:Nx \l_@@_tmpd_tl {\tl_tail:N \l_@@_tmpd_tl}
         \tl_set:Nx \l_@@_tmpd_tl {\tl_tail:N \l_@@_tmpd_tl}
         \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpc_tl
         \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpd_tl
       }
       {
         \tl_set:Nn \l_@@_tmpc_tl {#1}
         \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl}
         \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl}
         \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl}
         \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpc_tl
       }
     }
     {
       \tl_set:Nn \l_@@_tmpc_tl {#1}
       \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl}
       \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl}
       \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl}
       \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpc_tl
     }
   }
   \c_spath_lineto_tl
   {
     \tl_set:Nn \l_@@_tmpc_tl {#1}
     \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl}
     \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl}
     \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl}
     \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpc_tl
   }
 }
 {
   \tl_put_right:Nn \l_@@_tmpa_tl {#1}
 }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\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
{
 \knot_debug:x {knot~ intersections~ between~
   \l_@@_prefix_tl \c_space_tl #1~ and~ #2}
 \group_begin:
 \tl_set_eq:NN \l_@@_tmpa_tl \l_@@_prefix_tl
 \tl_put_right:Nn \l_@@_tmpa_tl {#1}
 \tl_set_eq:NN \l_@@_tmpb_tl \l_@@_prefix_tl
 \tl_put_right:Nn \l_@@_tmpb_tl {#2}
 \tl_set_eq:Nc \l_@@_tmpc_tl {knot \tl_use:N \l_@@_tmpa_tl}
 \tl_set_eq:Nc \l_@@_tmpd_tl {knot \tl_use:N \l_@@_tmpb_tl}

 \bool_if:nTF {
   \l_@@_save_bool
   &&
   \tl_if_exist_p:c {
     knot~ intersections~
     \tl_use:N \l_@@_name_tl -
     \tl_use:N \l_@@_tmpa_tl -
     \tl_use:N \l_@@_tmpb_tl
   }
 }
 {
   \tl_use:c
   {
     knot~ intersections~ \tl_use:N \l_@@_name_tl -
     \tl_use:N \l_@@_tmpa_tl -
     \tl_use:N \l_@@_tmpb_tl
   }
 }
 {
   \pgfintersectionofpaths{\pgfsetpath\l_@@_tmpc_tl}{\pgfsetpath\l_@@_tmpd_tl}

 }

 \knot_debug:x {found~\pgfintersectionsolutions\c_space_tl~ intersections}
 \int_compare:nT {\pgfintersectionsolutions > 0}
 {
   \int_step_function:nnnN
   {1}
   {1}
   {\pgfintersectionsolutions}
   \knot_do_intersection:n
 }

 \knot_save_intersections:VV \l_@@_tmpa_tl \l_@@_tmpb_tl
 \group_end:
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\knot_save_intersections:nn}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \knot_save_intersections:nn #1#2
{
 \knot_debug:n {knot~ save~ intersections}
 \bool_if:NT \l_@@_save_bool
 {
   \tl_clear:N \l_@@_aux_tl
   \tl_put_right:Nn \l_@@_aux_tl
   {
     \def\pgfintersectionsolutions
   }
   \tl_put_right:Nx \l_@@_aux_tl
   {
     {\int_eval:n {\pgfintersectionsolutions}}
   }
   \int_compare:nT {\pgfintersectionsolutions > 0}
   {
     \int_step_inline:nnnn {1} {1} {\pgfintersectionsolutions}
     {
       \pgfpointintersectionsolution{##1}
       \dim_set:Nn \l_@@_tmpa_dim {\pgf@x}
       \dim_set:Nn \l_@@_tmpb_dim {\pgf@y}
       \tl_put_right:Nn \l_@@_aux_tl
       {
         \expandafter\def\csname pgfpoint@intersect@solution@##1\endcsname
       }
       \tl_put_right:Nx \l_@@_aux_tl
       {
         {
           \exp_not:N \pgf@x
           =
           \dim_use:N \l_@@_tmpa_dim
           \exp_not:N \relax
           \exp_not:N \pgf@y
           =
           \dim_use:N \l_@@_tmpb_dim
           \exp_not:N \relax
         }
       }
     }
     \tl_set:Nn \l_@@_auxa_tl {\expandafter \gdef \csname knot~ intersections~}
     \tl_put_right:Nx \l_@@_auxa_tl {\tl_use:N \l_@@_name_tl - #1 - #2}
     \tl_put_right:Nn \l_@@_auxa_tl {\endcsname}
     \tl_put_right:Nx \l_@@_auxa_tl {{\tl_to_str:N \l_@@_aux_tl}}
     \protected@write\@auxout{}{\tl_to_str:N \l_@@_auxa_tl}
   }
 }
}
\cs_generate_variant:Nn \knot_save_intersections:nn {VV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\knot_do_intersection:n}
% This handles a specific intersection.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \knot_do_intersection:n #1
{
 \knot_debug:n {knot~ do~ intersection~ #1}
%    \end{macrocode}
% Get the intersection coordinates.
%    \begin{macrocode}
 \pgfpointintersectionsolution{#1}
 \dim_set:Nn \l_@@_tmpa_dim {\pgf@x}
 \dim_set:Nn \l_@@_tmpb_dim {\pgf@y}
 \knot_debug:x {intersection~at~
   (\dim_use:N \l_@@_tmpa_dim,\dim_use:N \l_@@_tmpb_dim)}
%    \end{macrocode}
% If we're dealing with filaments, we can get false positives from the end points.
%    \begin{macrocode}
 \bool_set_false:N \l_@@_skip_bool
 \bool_if:NT \l_@@_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_@@_tmpc_tl {knot previous}
   \tl_put_right:NV \l_@@_tmpc_tl \l_@@_tmpa_tl
   \tl_set:Nv \l_@@_tmpc_tl \l_@@_tmpc_tl
   \tl_if_eq:NNT \l_@@_tmpc_tl \l_@@_tmpb_tl
   {
     \knot_test_endpoint:NVnT \l_@@_tolerance_dim \l_@@_tmpb_tl {final point}
     {
       \bool_set_true:N \l_@@_skip_bool
     }
   }

   \tl_set:Nn \l_@@_tmpc_tl {knot previous}
   \tl_put_right:NV \l_@@_tmpc_tl \l_@@_tmpb_tl
   \tl_set:Nv \l_@@_tmpc_tl \l_@@_tmpc_tl
   \tl_if_eq:NNT \l_@@_tmpc_tl \l_@@_tmpa_tl
   {
     \knot_test_endpoint:NVnT \l_@@_tolerance_dim \l_@@_tmpa_tl {final point}
     {
       \bool_set_true:N \l_@@_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_@@_ignore_ends_bool
 {
   \knot_test_endpoint:NVnT \l_@@_tolerance_dim \l_@@_tmpa_tl {initial point}
   {
     \bool_set_true:N \l_@@_skip_bool
   }
   \knot_test_endpoint:NVnT \l_@@_tolerance_dim \l_@@_tmpa_tl {final point}
   {
     \bool_set_true:N \l_@@_skip_bool
   }
   \knot_test_endpoint:NVnT \l_@@_tolerance_dim \l_@@_tmpb_tl {initial point}
   {
     \bool_set_true:N \l_@@_skip_bool
   }
   \knot_test_endpoint:NVnT \l_@@_tolerance_dim \l_@@_tmpb_tl {final point}
   {
     \bool_set_true:N \l_@@_skip_bool
   }
 }
%    \end{macrocode}
% Assuming that we passed all the above tests, we render the crossing.
%    \begin{macrocode}
 \bool_if:NF \l_@@_skip_bool
 {

   \int_gincr:N \g_@@_intersections_int
   \knot_debug:x {Processing~intersection~\int_use:N \g_@@_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_@@_ignore_crossing_ \int_use:N
       \g_@@_intersections_int}
     &&
     ! \tl_if_empty_p:c {l_@@_ignore_crossing_ \int_use:N
       \g_@@_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_@@_crossing_ \int_use:N
         \g_@@_intersections_int}
       &&
       ! \tl_if_empty_p:c {l_@@_crossing_ \int_use:N
         \g_@@_intersections_int}
     }
     {
       \tl_set_eq:NN \l_@@_tmpg_tl \l_@@_tmpb_tl
     }
     {
       \tl_set_eq:NN \l_@@_tmpg_tl \l_@@_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_@@_self_intersections_bool
     {
       \knot_test_endpoint:NVnT
       \l_@@_redraw_tolerance_dim \l_@@_tmpg_tl {initial point}
       {
         \bool_set_true:N \l_@@_prepend_prev_bool
       }
       {
         \bool_set_false:N \l_@@_prepend_prev_bool
       }
       \knot_test_endpoint:NVnT
       \l_@@_redraw_tolerance_dim \l_@@_tmpg_tl {final point}
       {
         \bool_set_true:N \l_@@_append_next_bool
       }
       {
         \bool_set_false:N \l_@@_append_next_bool
       }
%    \end{macrocode}
% If either of those tests succeeded, do the appending or prepending.
%    \begin{macrocode}
       \bool_if:nT
       {
         \l_@@_prepend_prev_bool || \l_@@_append_next_bool
       }
       {
         \tl_clear_new:c {knot \tl_use:N \l_@@_prefix_tl -1}
         \tl_set_eq:cc
         {knot \tl_use:N \l_@@_prefix_tl -1}
         {knot \tl_use:N \l_@@_tmpg_tl}

         \tl_clear_new:c {l_@@_options_ \tl_use:N \l_@@_prefix_tl -1}
         \tl_set_eq:cc
         {l_@@_options_ \tl_use:N \l_@@_prefix_tl -1}
         {l_@@_options_ \tl_use:N \l_@@_tmpg_tl}

         \bool_if:nT
         {
           \l_@@_prepend_prev_bool
           &&
           \tl_if_exist_p:c {knot previous \tl_use:N \l_@@_tmpg_tl}
           &&
           !\tl_if_empty_p:c {knot previous \tl_use:N \l_@@_tmpg_tl}
         }
         {
           \knot_debug:x {Prepending~
             \tl_use:c {knot previous \tl_use:N \l_@@_tmpg_tl}}
           \spath_prepend_no_move:cv
           {knot \tl_use:N \l_@@_prefix_tl -1}
           {knot \tl_use:c {knot previous \tl_use:N \l_@@_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_@@_splits_bool
             &&
             \tl_if_exist_p:c {knot previous
               \tl_use:c {knot previous \tl_use:N \l_@@_tmpg_tl}
             }
             &&
             !\tl_if_empty_p:c {knot previous
               \tl_use:c {knot previous \tl_use:N \l_@@_tmpg_tl}
             }
           }
           {
             \knot_test_endpoint:NvnT
             \l_@@_redraw_tolerance_dim
             {knot previous \tl_use:N \l_@@_tmpg_tl}
             {initial point}
             {
               \knot_debug:x {Prepending~
                 \tl_use:c {knot previous
                   \tl_use:c {knot previous \tl_use:N \l_@@_tmpg_tl}
                 }
               }
               \spath_prepend_no_move:cv
               {knot \tl_use:N \l_@@_prefix_tl -1}
               {knot \tl_use:c
                 {knot previous \tl_use:c
                   {knot previous \tl_use:N \l_@@_tmpg_tl}
                 }
               }
               \tl_set_eq:Nc \l_@@_tmpa_tl
               {knot \tl_use:N \l_@@_prefix_tl -1}
             }
           }
         }

%    \end{macrocode}
% Now the same for appending.
%    \begin{macrocode}
         \bool_if:nT
         {
           \l_@@_append_next_bool
           &&
           \tl_if_exist_p:c {knot next \tl_use:N \l_@@_tmpg_tl}
           &&
           !\tl_if_empty_p:c {knot next \tl_use:N \l_@@_tmpg_tl}
         }
         {
           \knot_debug:x {Appending~
             \tl_use:c {knot next \tl_use:N \l_@@_tmpg_tl}}
           \spath_append_no_move:cv
           {knot \tl_use:N \l_@@_prefix_tl -1}
           {knot \tl_use:c {knot next \tl_use:N \l_@@_tmpg_tl}}
           \bool_if:nT
           {
             \l_@@_splits_bool
             &&
             \tl_if_exist_p:c {knot next \tl_use:c { knot next \tl_use:N
               \l_@@_tmpg_tl}}
             &&
             !\tl_if_empty_p:c {knot next
               \tl_use:c { knot next \tl_use:N \l_@@_tmpg_tl}
             }
           }
           {
             \knot_debug:x {Testing~ whether~ to~ append~
               {knot next \tl_use:c { knot next \tl_use:N \l_@@_tmpg_tl}}
             }
             \knot_test_endpoint:NvnT
             \l_@@_redraw_tolerance_dim
             {knot next \tl_use:N \l_@@_tmpg_tl}
             {final point}
             {
               \knot_debug:x {Appending~
                 {knot next \tl_use:c { knot next \tl_use:N \l_@@_tmpg_tl}}
               }
               \spath_append_no_move:cv
               {knot \tl_use:N \l_@@_prefix_tl -1}
               {knot \tl_use:c
                 {knot next \tl_use:c
                   {knot next \tl_use:N \l_@@_tmpg_tl}
                 }
               }
             }
           }
         }
         \tl_set:Nn \l_@@_tmpg_tl {\tl_use:N \l_@@_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 \g_@@_intersections_int/.try
     }
     \knot_draw_crossing:VVV \l_@@_tmpg_tl \l_@@_tmpa_dim \l_@@_tmpb_dim
     \coordinate
     (\l_@@_name_tl \c_space_tl \int_use:N \g_@@_intersections_int)
     at (\dim_use:N \l_@@_tmpa_dim, \dim_use:N \l_@@_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 possibly stick a coordinate with a label at the crossing.
%    \begin{macrocode}
   \tl_if_empty:NF \l_@@_node_tl
   {
     \seq_gpush:Nx
     \g_@@_nodes_seq
     {
       \l_@@_node_tl
       at
       (\dim_use:N \l_@@_tmpa_dim, \dim_use:N \l_@@_tmpb_dim) {};
     }
   }
 }
}

\cs_generate_variant:Nn \knot_intersections:nn {VV}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\knot_test_endpoint:N}
% Test whether the point is near the intersection point.
%    \begin{macrocode}
\prg_new_conditional:Npnn \knot_test_endpoint:NN #1#2 {p,T,F,TF}
{
 \dim_compare:nTF
 {
   \dim_abs:n { \l_@@_tmpa_dim - \tl_item:Nn #2 {1}}
   +
   \dim_abs:n { \l_@@_tmpb_dim - \tl_item:Nn #2 {2}}
   <
   #1
 }
 {
   \prg_return_true:
 }
 {
   \prg_return_false:
 }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\knot_test_endpoint:nn}
% Wrapper around the above.
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn \knot_test_endpoint:Nnn #1#2#3 {T,F,TF}
{
 \use:c {spath_#3:Nv} \l_@@_tmpd_tl {knot #2}
 \knot_test_endpoint:NNTF #1 \l_@@_tmpd_tl
 {
   \prg_return_true:
 }
 {
   \prg_return_false:
 }
}

\cs_generate_variant:Nn \knot_test_endpoint:NnnT {NVnT,NvnT}
\cs_generate_variant:Nn \knot_test_endpoint:NnnF {NVnF,NvnF}
\cs_generate_variant:Nn \knot_test_endpoint:NnnTF {NVnTF,NvnTF}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\knot_draw_crossing:nnn}
% This is the code that actually renders a crossing.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \knot_draw_crossing:nnn #1#2#3
{
 \knot_debug:n {knot~ draw~ crossing}
 \group_begin:
 \pgfscope
 \path[knot~ diagram/background~ clip] (#2, #3)
 circle[radius=\l_@@_clip_bg_radius_dim];

 \tl_set:Nn \l_@@_tmpa_tl {knot~ diagram/every~ strand/.try,}
 \tl_if_exist:cT {l_@@_options_ #1}
 {
 \tl_put_right:Nv \l_@@_tmpa_tl {l_@@_options_ #1}
 }
 \tl_put_right:Nn \l_@@_tmpa_tl
 {
   ,knotbg
   ,line~ width= \tl_use:N \l_@@_clip_width_tl * \pgflinewidth
 }
 \spath_tikz_path:Vv \l_@@_tmpa_tl {knot #1}

 \endpgfscope

 \pgfscope
 \path[knot~ diagram/clip] (#2, #3)
 circle[radius=\l_@@_clip_draw_radius_dim];

 \tl_set:Nn \l_@@_tmpa_tl {knot~ diagram/every~ strand/.try,}
 \tl_if_exist:cT {l_@@_options_ #1}
 {
 \tl_put_right:Nv \l_@@_tmpa_tl {l_@@_options_ #1}
 }
 \tl_put_right:Nn \l_@@_tmpa_tl
 {
   ,knot~ diagram/only~ when~ rendering/.try
   ,only~ when~ rendering/.try
 }
 \spath_tikz_path:Vv \l_@@_tmpa_tl {knot #1}

 \endpgfscope
 \group_end:
}

\cs_generate_variant:Nn \knot_draw_crossing:nnn {nVV, VVV}

\cs_new_protected_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}[internal]{\knot_split_strands:}
% This, and the following macros, are for splitting strands into filaments.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \knot_split_strands:
{
 \knot_debug:n {knot~ split~ strands}
 \int_gzero:N \g_@@_filaments_int
 \int_step_function:nnnN {1} {1} {\l_@@_strands_int} \knot_split_strand:n
 \int_step_function:nnnN {1} {1} {\g_@@_filaments_int} \knot_compute_nexts:n
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\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
{
 \knot_debug:n {knot~ compute~ nexts}
 \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}[internal]{\knot_split_strand:n}
% Sets up the split for a single strand.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \knot_split_strand:n #1
{
 \knot_debug:n {knot~ split~ strand}
 \int_set_eq:NN \l_@@_component_start_int \g_@@_filaments_int
 \int_incr:N \l_@@_component_start_int
 \tl_set_eq:Nc \l_@@_tmpa_tl {l_@@_options_strand #1}
 \spath_remove_empty_components:c {knot strand #1}
 \spath_segments_to_seq:Nv \l_@@_segments_seq {knot strand #1}
 \seq_map_function:NN \l_@@_segments_seq \knot_save_filament:N
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\knot_save_filament:N}
% Saves a filament as a new \Verb+spath+ object.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \knot_save_filament:N #1
{
 \knot_debug:n {knot~ save~ filament}
 \tl_set:Nx \l_@@_tmpb_tl {\tl_item:nn {#1} {4}}
 \token_case_meaning:NnF \l_@@_tmpb_tl
 {
   \c_spath_moveto_tl
   {
     \int_compare:nT {\l_@@_component_start_int < \g_@@_filaments_int}
     {
       \int_set_eq:NN \l_@@_component_start_int \g_@@_filaments_int
     }
   }
   \c_spath_lineto_tl
   {
     \int_gincr:N \g_@@_filaments_int
     \tl_clear_new:c {knot filament \int_use:N \g_@@_filaments_int}
     \tl_set:cn {knot filament \int_use:N \g_@@_filaments_int} {#1}

     \tl_clear_new:c {l_@@_options_filament \int_use:N \g_@@_filaments_int}
     \tl_set_eq:cN {l_@@_options_filament \int_use:N \g_@@_filaments_int}
     \l_@@_tmpa_tl

     \tl_clear_new:c {knot previous filament \int_use:N \g_@@_filaments_int}
     \int_compare:nF {\l_@@_component_start_int == \g_@@_filaments_int}
     {
       \tl_set:cx {knot previous filament \int_use:N \g_@@_filaments_int}
       {filament \int_eval:n {\g_@@_filaments_int - 1}}
     }
   }
   \c_spath_curvetoa_tl
   {
     \int_gincr:N \g_@@_filaments_int
     \tl_clear_new:c {knot filament \int_use:N \g_@@_filaments_int}
     \tl_set:cn {knot filament \int_use:N \g_@@_filaments_int} {#1}
     \tl_clear_new:c {l_@@_options_filament \int_use:N \g_@@_filaments_int}
     \tl_set_eq:cN {l_@@_options_filament \int_use:N \g_@@_filaments_int}
     \l_@@_tmpa_tl

     \tl_clear_new:c {knot previous filament \int_use:N \g_@@_filaments_int}
     \int_compare:nF {\l_@@_component_start_int == \g_@@_filaments_int}
     {
       \tl_set:cx
       {knot previous filament \int_use:N \g_@@_filaments_int}
       {filament \int_eval:n {\g_@@_filaments_int - 1}}
     }
   }
   \c_spath_closepath_tl
   {
     \int_gincr:N \g_@@_filaments_int
     \tl_clear_new:c {knot filament \int_use:N \g_@@_filaments_int}
     \tl_clear:N \l_@@_tmpa_tl
     \tl_put_right:Nx
     {
       \tl_item:nn {#1} {1}\tl_item:nn {#1} {2}\tl_item:nn {#1} {3}
     }
     \tl_put_right:NV \l_@@_tmpa_tl \c_spath_lineto_tl
     \tl_put_right:Nx {\tl_item:nn {#1} {5}\tl_item:nn {#1} {6}}

     \tl_set:cV {knot filament \int_use:N \g_@@_filaments_int} \l_@@_tmpa_tl
     \tl_set_eq:cN {l_@@_options_filament \int_use:N \g_@@_filaments_int}
     \l_@@_tmpa_tl
     \tl_clear_new:c {knot previous filament \int_use:N \g_@@_filaments_int}
     \int_compare:nF {\l_@@_component_start_int == \g_@@_filaments_int}
     {
       \tl_set:cx
       {knot previous filament \int_use:N \g_@@_filaments_int}
       {filament \int_eval:n {\g_@@_filaments_int - 1}}
     }
     \tl_set:cx
     {knot previous filament \int_use:N \l_@@_component_start_int}
     {filament \int_use:N \g_@@_filaments_int}
   }
 }
 {
 }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\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_@@_redraws_tl {\knot_draw_crossing:nn}
 \tl_put_right:Nx \l_@@_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}[internal]{\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}[internal]{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}[internal]{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}[internal]{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}[internal]{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}[internal]{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