% \iffalse meta-comment
%<*internal>
\iffalse
%</internal>
%<*readme>
----------------------------------------------------------------
spath3 --- LaTeX3 functions for manipulating PGF soft paths
E-mail: loopspace@mathforge.org
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: loopspace@mathforge.org
Released under the LaTeX Project Public License v1.3c or later
See http://www.latex-project.org/lppl.txt
----------------------------------------------------------------

\endpreamble
\postamble

Copyright (C) 2011-2021 by Andrew Stacey <loopspace@mathforge.org>

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

\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{8929}
%
% \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.}
%
% \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{loopspace@mathforge.org}}
% \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+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+penrose+
% \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} {2021/02/21} {2.4} {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

\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}
%
% 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}
%    \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}
%
% 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}
    \tl_case: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_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_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}
  \tl_put_right:NV \l_@@_tmpa_tl \c_spath_moveto_tl
  \tl_set_eq:NN \l_@@_tmpb_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_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, 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}
    \tl_case:NnT \l_@@_tmpa_tl
    {
      \c_spath_lineto_tl {}
      \c_spath_curveto_tl {}
      \c_spath_closepath_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}
    \tl_case:Nn \l_@@_tmpa_tl
    {
      \c_spath_moveto_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_@@_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}
    \tl_case: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} }
        }
      }
    }
    \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_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
  \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}

    \tl_case: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_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_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
  \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
  \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_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_@@_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}
  \bool_until_do:nn {
    \tl_if_empty_p:N \l_@@_tmpa_tl
  }
  {
    \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_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_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_@@_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}
  \bool_until_do:nn {
    \tl_if_empty_p:N \l_@@_tmpa_tl
  }
  {
    \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_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_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_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_clear:N #1
      \tl_set: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
  }
}
%    \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_put_right:Nx \l_@@_tmpb_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 + #2}
    \tl_set:Nx \l_@@_tmpa_tl {\tl_tail: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_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}

    \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:NV \l_@@_tmpd_tl {#1}
  \spath_initialpoint:NV \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_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_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.
%    \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_@@_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_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_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.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_open:n #1
{
  \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_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl}

    \tl_case: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_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
}
%    \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_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}
    \fp_compare:nTF
    {
      0 < \l_@@_tmpa_fp && \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}
    \tl_case: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_at:NNnn,
% \spath_split_at:Nnn,
% \spath_split_at:Nn,
% \spath_gsplit_at:NNnn
% \spath_gsplit_at:Nnn,
% \spath_gsplit_at: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).
%    \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}
  \tl_clear:N \l_@@_tmpc_tl

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

  \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 }
    \tl_case:Nn \l_@@_tmpf_tl
    {
      \c_spath_lineto_tl
      {
        \int_incr:N \l_@@_tmpb_int
      }
      \c_spath_curvetoa_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
    }
  }
  {

    \tl_case: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
      }
    }
  }

  \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_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}
%    \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}
}
%    \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}
%
%
% \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
  
      \tl_case: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_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
  
      \tl_case: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_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}

  \spath_reallength:Nn \l_@@_tmpa_int {#1}
  
  % 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
  }

  % Open it
  \spath_open:N \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}
    \tl_case: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
}
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \spath_gget_current_path:N #1
{
  \pgfsyssoftpath@getcurrentpath #1
  \tl_gset_eq:NN #1 #1
}
%    \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}
  \exp_last_unbraced:NV \pgf@protocolsizes\l_@@_tmpa_tl

  \spath_maxbb:Nn \l_@@_tmpa_tl {#1}
  \exp_last_unbraced:NV \pgf@protocolsizes\l_@@_tmpa_tl
}
\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
{
  \path[#1] \pgfextra{
    \spath_set_current_path:n {#2}
    \tl_put_right: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}
    \exp_not:c {pgf@y}=\tl_item:Nn \l_@@_tmpa_tl {2}
  }
  \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}
    \exp_not:c {tikz@lasty}=\exp_not:c {pgf@y}
    \exp_not:c {tikz@lastxsaved}=\exp_not:c {pgf@x}
    \exp_not:c {tikz@lastysaved}=\exp_not:c {pgf@y}
  }
  \tl_use:N \l_@@_tmpa_tl
  \spath_finalmovepoint:Nn \l_@@_tmpa_tl {#1}
  \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}
    \exp_not:c {pgf@y}=\tl_item:Nn \l_@@_tmpa_tl {2}
  }
  \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@lastmovetox}=\exp_not:c {pgf@x}
    \exp_not:c {tikz@lastmovetoy}=\exp_not:c {pgf@y}
  }
  \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
%
% 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_@@_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
\seq_new:N \g_@@_tmpa_seq
\seq_new:N \g_@@_tmpb_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_new:nnnn { spath3 } { empty soft path } { Soft~ path~ #1~ is~ empty}
{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.
% 
%    \begin{macrocode}
\tl_new:N \g_@@_tikzfinish_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:}

\cs_new_protected_nopar:Npn \spath_save_path:Nn #1#2
{
  \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}
%
% 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
\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,
  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}
%
% 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}
%
% 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_save_path:cV
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      \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_gsave_path:cV
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      \l_@@_tmpa_tl
    }
  },
  clone/.code~ 2~ args={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl}
    {
      \tl_clear_new:c
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      \tl_set_eq:cc
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #2 }
    }
  },
  clone~ global/.code~ 2~ args={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl}
    {
      \tl_gclear_new:c
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      \tl_gset_eq:cc
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #2 }
    }
  },
%    \end{macrocode}
%
% Saves a soft path to the aux file.
%
%    \begin{macrocode}
  save~ to~ aux/.code={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \spath_save_to_aux:c
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
%    \end{macrocode}
%
% Exports the path as an SVG file.
%
%    \begin{macrocode}
  export~ to~ svg/.code={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \spath_export_to_svg:nv {#1}
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #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
    \tl_clear:N \l_@@_joinpath_tl
    \tl_clear:N \l_@@_transformation_tl
    \tikzset{
      spath/join/.cd,
      #1
    }

    \tl_if_exist:cTF
    {
      \tl_use:N \l_@@_prefix_tl
      \tl_use:N \l_@@_joinpath_tl
      \tl_use:N \l_@@_suffix_tl
    }
    {
      \tl_if_empty:cT
      {
        \tl_use:N \l_@@_prefix_tl
        \tl_use:N \l_@@_joinpath_tl
        \tl_use:N \l_@@_suffix_tl
      }
      {
        \msg_warning:nnn { spath3 } { empty soft path } { #1 }
      }
      \tl_set_eq:Nc \l_@@_joinpath_tl
      {
        \tl_use:N \l_@@_prefix_tl
        \tl_use:N \l_@@_joinpath_tl
        \tl_use:N \l_@@_suffix_tl
      }
      \spath_get_current_path:N \l_@@_current_tl
      
      \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
      {
        \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}
        }
        \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
    }
    {
      \msg_warning:nnx
      { spath3 }
      { missing soft path }
      {\tl_use:N \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={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \tl_if_empty:cTF
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      {
        \msg_warning:nnn { spath3 } { empty soft path } { #1 }
      }
      {
        \iow_term:n {---~ soft~ path~ #1~ ---}
        \spath_show:v
        {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      }
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #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={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \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
      \tl_clear:N \l_@@_joinpath_tl
      \tl_clear:N \l_@@_transformation_tl
      \tikzset{
        spath/join/.cd,
        #2
      }

      \tl_if_exist:cTF
      {
        \tl_use:N \l_@@_prefix_tl
        \tl_use:N \l_@@_joinpath_tl
        \tl_use:N \l_@@_suffix_tl
      }
      {
        \tl_set_eq:Nc \l_@@_joinpath_tl
        {
          \tl_use:N \l_@@_prefix_tl
          \tl_use:N \l_@@_joinpath_tl
          \tl_use:N \l_@@_suffix_tl
        }
        
        \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
          {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
          \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 {:cV}

        \cs_if_exist:cF {\tl_use:N \l_@@_tmpa_tl}
        {
          \tl_show:N \l_@@_tmpa_tl
        }
        
        \use:c {\tl_use:N \l_@@_tmpa_tl }
        {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
        \l_@@_joinpath_tl
      }
      {
        \msg_warning:nnx
        { spath3 }
        { missing soft path }
        {\tl_use:N \l_@@_joinpath_tl }
      }
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
%    \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={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \spath_spot_weld_components:c
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
  spot~ weld~ globally/.code={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \spath_spot_gweld_components:c
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
%    \end{macrocode}
%
% Reverses the named path.
%
%    \begin{macrocode}
  reverse/.code={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \spath_reverse:c
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
  reverse~ globally/.code={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \spath_reverse:c
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
%    \end{macrocode}
%
% Adjust a path to span between two points.
%  
%    \begin{macrocode}
  span/.code ~n~ args={3}{
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \@@_process_tikz_point:Nn \l_@@_tmpa_tl {#2}
      \@@_process_tikz_point:Nn \l_@@_tmpb_tl {#3}
      \spath_span:cVV
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      \l_@@_tmpa_tl \l_@@_tmpb_tl
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
  span~ global/.code ~n~ args={3}{
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \@@_process_tikz_point:Nn \l_@@_tmpa_tl {#2}
      \@@_process_tikz_point:Nn \l_@@_tmpb_tl {#3}
      \spath_gspan:cVV
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      \l_@@_tmpa_tl \l_@@_tmpb_tl
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
%    \end{macrocode}
% 
% Defines a to path
% 
%    \begin{macrocode}
  to/.style={
    to~path={
      [
        spath/span={#1}{(\tikztostart)}{(\tikztotarget)},
        spath/append~no~move={#1},
      ]
      \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}{
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \tl_if_exist:cTF
      {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl}
      {
        \tl_if_exist:cTF
        {\tl_use:N \l_@@_prefix_tl #3 \tl_use:N \l_@@_suffix_tl}
        {
          \spath_splice_between:cvv
          {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
          {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl}
          {\tl_use:N \l_@@_prefix_tl #3 \tl_use:N \l_@@_suffix_tl}
        }
        {
          \msg_warning:nnn { spath3 } { missing soft path } { #3 }
        }
      }
      {
        \msg_warning:nnn { spath3 } { missing soft path } { #2 }
      }
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
  splice~ global/.code ~n~ args={3}{
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \tl_if_exist:cTF
      {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl}
      {
        \tl_if_exist:cTF
        {\tl_use:N \l_@@_prefix_tl #3 \tl_use:N \l_@@_suffix_tl}
        {
          \spath_gsplice_between:cvv
          {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
          {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl}
          {\tl_use:N \l_@@_prefix_tl #3 \tl_use:N \l_@@_suffix_tl}
        }
        {
          \msg_warning:nnn { spath3 } { missing soft path } { #3 }
        }
      }
      {
        \msg_warning:nnn { spath3 } { missing soft path } { #2 }
      }
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
%    \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_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \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
      }
      \tl_if_exist:cTF
      {
        \tl_use:N \l_@@_prefix_tl
        \tl_use:N \l_@@_tmpc_tl
        \tl_use:N \l_@@_suffix_tl
      }
      {
        \tl_if_empty:NT \l_@@_tmpd_tl
        {
          \spath_spot_weld_components:c
          {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
        }

        \spath_components_to_seq:Nv
        \l_@@_tmpa_seq
        {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}

        \seq_gclear:N \g_@@_tmpa_seq
        
        \tl_if_empty:NTF \l_@@_tmpd_tl
        {
          \int_step_inline:nnnn {1}{1} {\seq_count:N \l_@@_tmpa_seq}
          {
            \seq_gput_right:Nn \g_@@_tmpa_seq {##1}
          }
        }
        {
          \foreach \l_@@_tmpa_tl in \l_@@_tmpd_tl
          {
            \seq_gput_right:NV \g_@@_tmpa_seq \l_@@_tmpa_tl
          }
          \seq_gsort:Nn \g_@@_tmpa_seq
          {
            \int_compare:nNnTF {##1} < {##2}
            { \sort_return_same: }
            { \sort_return_swapped: }
          }
        }

        \seq_pop_left:NN \l_@@_tmpa_seq \l_@@_tmpa_tl
        \seq_gpop_left:NN \g_@@_tmpa_seq \l_@@_tmpb_tl

        \seq_map_indexed_inline:Nn \l_@@_tmpa_seq
        {
          \int_compare:nTF
          {
            ##1 == \l_@@_tmpb_tl
          }
          {
            \seq_gpop_left:NNF \g_@@_tmpa_seq \l_@@_tmpb_tl
            {
              \tl_set:Nn \l_@@_tmpb_tl {-1}
            }
            \spath_splice_between:Nvn \l_@@_tmpa_tl
            {
              \tl_use:N \l_@@_prefix_tl
              \tl_use:N \l_@@_tmpc_tl
              \tl_use:N \l_@@_suffix_tl
            }
            {##2}
          }
          {
            \tl_put_right:Nn \l_@@_tmpa_tl {##2}
          }
        }
        \tl_set_eq:cN
        {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
        \l_@@_tmpa_tl
      }
      {
        \msg_warning:nnx
        { spath3 }
        { missing soft path }
        { \tl_use:N \l_@@_tmpc_tl }
      }
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
  join~ components~ globally~ with/.code~2~args={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \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
      }
      \tl_if_exist:cTF
      {
        \tl_use:N \l_@@_prefix_tl
        \tl_use:N \l_@@_tmpc_tl
        \tl_use:N \l_@@_suffix_tl
      }
      {
        \tl_if_empty:NT \l_@@_tmpd_tl
        {
          \spath_spot_weld_components:c
          {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
        }

        \spath_components_to_seq:Nv
        \l_@@_tmpa_seq
        {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}

        \seq_gclear:N \g_@@_tmpa_seq
        
        \tl_if_empty:NTF \l_@@_tmpd_tl
        {
          \int_step_inline:nnnn {1}{1} {\seq_count:N \l_@@_tmpa_seq}
          {
            \seq_gput_right:Nn \g_@@_tmpa_seq {##1}
          }
        }
        {
          \foreach \l_@@_tmpa_tl in \l_@@_tmpd_tl
          {
            \seq_gput_right:NV \g_@@_tmpa_seq \l_@@_tmpa_tl
          }
          \seq_gsort:Nn \g_@@_tmpa_seq
          {
            \int_compare:nNnTF {##1} < {##2}
            { \sort_return_same: }
            { \sort_return_swapped: }
          }
        }

        \seq_pop_left:NN \l_@@_tmpa_seq \l_@@_tmpa_tl
        \seq_gpop_left:NN \g_@@_tmpa_seq \l_@@_tmpb_tl

        \seq_map_indexed_inline:Nn \l_@@_tmpa_seq
        {
          \int_compare:nTF
          {
            ##1 == \l_@@_tmpb_tl
          }
          {
            \seq_gpop_left:NNF \g_@@_tmpa_seq \l_@@_tmpb_tl
            {
              \tl_set:Nn \l_@@_tmpb_tl {-1}
            }
            \spath_splice_between:Nvn \l_@@_tmpa_tl
            {
              \tl_use:N \l_@@_prefix_tl
              \tl_use:N \l_@@_tmpc_tl
              \tl_use:N \l_@@_suffix_tl
            }
            {##2}
          }
          {
            \tl_put_right:Nn \l_@@_tmpa_tl {##2}
          }
        }
        \tl_gset_eq:cN
        {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
        \l_@@_tmpa_tl
      }
      {
        \msg_warning:nnx
        { spath3 }
        { missing soft path }
        { \tl_use:N \l_@@_tmpc_tl }
      }
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
  join~ components~ upright~ with/.code~2~args={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \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
      }
      \tl_if_exist:cTF
      {
        \tl_use:N \l_@@_prefix_tl
        \tl_use:N \l_@@_tmpc_tl
        \tl_use:N \l_@@_suffix_tl
      }
      {
        \tl_if_empty:NT \l_@@_tmpd_tl
        {
          \spath_spot_weld_components:c
          {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
        }

        \spath_components_to_seq:Nv
        \l_@@_tmpa_seq
        {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}

        \seq_gclear:N \g_@@_tmpa_seq
        
        \tl_if_empty:NTF \l_@@_tmpd_tl
        {
          \int_step_inline:nnnn {1}{1} {\seq_count:N \l_@@_tmpa_seq}
          {
            \seq_gput_right:Nn \g_@@_tmpa_seq {##1}
          }
        }
        {
          \foreach \l_@@_tmpa_tl in \l_@@_tmpd_tl
          {
            \seq_gput_right:NV \g_@@_tmpa_seq \l_@@_tmpa_tl
          }
          \seq_gsort:Nn \g_@@_tmpa_seq
          {
            \int_compare:nNnTF {##1} < {##2}
            { \sort_return_same: }
            { \sort_return_swapped: }
          }
        }

        \seq_pop_left:NN \l_@@_tmpa_seq \l_@@_tmpa_tl
        \seq_gpop_left:NN \g_@@_tmpa_seq \l_@@_tmpb_tl

        \tl_set_eq:Nc \l_@@_tmpc_tl
        {
          \tl_use:N \l_@@_prefix_tl
          \tl_use:N \l_@@_tmpc_tl
          \tl_use:N \l_@@_suffix_tl
        }
        \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_gpop_left:NNF \g_@@_tmpa_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_set_eq:cN
        {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
        \l_@@_tmpa_tl
      }
      {
        \msg_warning:nnx
        { spath3 }
        { missing soft path }
        { \tl_use:N \l_@@_tmpc_tl }
      }
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
  join~ components~ globally~ upright~ with/.code~2~args={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \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
      }
      \tl_if_exist:cTF
      {
        \tl_use:N \l_@@_prefix_tl
        \tl_use:N \l_@@_tmpc_tl
        \tl_use:N \l_@@_suffix_tl
      }
      {
        \tl_if_empty:NT \l_@@_tmpd_tl
        {
          \spath_spot_weld_components:c
          {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
        }

        \spath_components_to_seq:Nv
        \l_@@_tmpa_seq
        {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}

        \seq_gclear:N \g_@@_tmpa_seq
        
        \tl_if_empty:NTF \l_@@_tmpd_tl
        {
          \int_step_inline:nnnn {1}{1} {\seq_count:N \l_@@_tmpa_seq}
          {
            \seq_gput_right:Nn \g_@@_tmpa_seq {##1}
          }
        }
        {
          \foreach \l_@@_tmpa_tl in \l_@@_tmpd_tl
          {
            \seq_gput_right:NV \g_@@_tmpa_seq \l_@@_tmpa_tl
          }
          \seq_gsort:Nn \g_@@_tmpa_seq
          {
            \int_compare:nNnTF {##1} < {##2}
            { \sort_return_same: }
            { \sort_return_swapped: }
          }
        }

        \seq_pop_left:NN \l_@@_tmpa_seq \l_@@_tmpa_tl
        \seq_gpop_left:NN \g_@@_tmpa_seq \l_@@_tmpb_tl

        \tl_set_eq:Nc \l_@@_tmpc_tl
        {
          \tl_use:N \l_@@_prefix_tl
          \tl_use:N \l_@@_tmpc_tl
          \tl_use:N \l_@@_suffix_tl
        }
        \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_gpop_left:NNF \g_@@_tmpa_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:cN
        {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
        \l_@@_tmpa_tl
      }
      {
        \msg_warning:nnx
        { spath3 }
        { missing soft path }
        { \tl_use:N \l_@@_tmpc_tl }
      }
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
%    \end{macrocode}
%
% Close a path.
%
%    \begin{macrocode}
  close/.code={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \spath_close:c
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
  close~ globally/.code={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \spath_gclose:c
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
%    \end{macrocode}
%
% Close a path with another path.
%
%    \begin{macrocode}
  close~ with/.code~ 2~ args={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \tl_if_exist:cTF
      {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl}
      {
        \spath_close_with:cv
        {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
        {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl}
      }
      {
        \msg_warning:nnn { spath3 } { missing soft path } { #2 }
      }
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
  close~ globally~ with/.code~ 2~ args={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \tl_if_exist:cTF
      {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl}
      {
        \spath_gclose_with:cv
        {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
        {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl}
      }
      {
        \msg_warning:nnn { spath3 } { missing soft path } { #2 }
      }
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
%    \end{macrocode}
%
% These keys shorten the path.
%
%    \begin{macrocode}
  shorten~ at~ end/.code~ 2~ args={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \spath_shorten_at_end:cn
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {#2}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
  shorten~ at~ start/.code~ 2~ args ={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \spath_shorten_at_start:cn
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {#2}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
  shorten~ at~ both~ ends/.code~ 2~ args={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \spath_shorten_at_end:cn
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {#2}
      \spath_shorten_at_start:cn
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {#2}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
  shorten~ globally~ at~ end/.code~ 2~ args={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \spath_gshorten_at_end:cn
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {#2}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
  shorten~ globally~ at~ start/.code~ 2~ args ={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \spath_gshorten_at_start:cn
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {#2}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
  shorten~ globally~ at~ both~ ends/.code~ 2~ args={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \spath_shorten_at_end:cn
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {#2}
      \spath_shorten_at_start:cn
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {#2}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
%    \end{macrocode}
%
% This translates the named path.
%
%    \begin{macrocode}
  translate/.code~ n~ args={3}{
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \spath_translate:cnn
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}{#2}{#3}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
  translate~ globally/.code~ n~ args={3}{
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \spath_gtranslate:cnn
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}{#2}{#3}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
%    \end{macrocode}
%
% This normalises the named path.
%
%    \begin{macrocode}
  normalise/.code={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \spath_normalise:c
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
  normalise~ globally/.code={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \spath_gnormalise:c
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
%    \end{macrocode}
%
% Transforms the named path using TikZ transformation specifications.
%
%    \begin{macrocode}
  transform/.code~ 2~ args={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \group_begin:
      \pgftransformreset
      \tikzset{#2}
      \pgfgettransform \l_@@_tmpa_tl
      \tl_gset:Nn \g_@@_smuggle_tl
      {
        \spath_transform:cnnnnnn
        {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      }
      \tl_gput_right:NV \g_@@_smuggle_tl \l_@@_tmpa_tl
      \group_end:
      \tl_use:N \g_@@_smuggle_tl
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
  transform~globally/.code~ 2~ args={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \group_begin:
      \pgftransformreset
      \tikzset{#2}
      \pgfgettransform \l_@@_tmpa_tl
      \tl_gset:Nn \g_@@_smuggle_tl
      {
        \spath_gtransform:cnnnnnn
        {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      }
      \tl_gput_right:NV \g_@@_smuggle_tl \l_@@_tmpa_tl
      \group_end:
      \tl_use:N \g_@@_smuggle_tl
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
%    \end{macrocode}
%
% Splits first path where it intersects with the second.
%
%    \begin{macrocode}
  split~ at~ intersections~ with/.code~ n~ args={2}{
    \tl_if_exist:cTF
    {
      tikz@library@intersections@loaded
    }
    {
      \tl_if_exist:cTF
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      {
        \tl_if_exist:cTF
        {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl}
        {
          \spath_split_path_at_intersections:cv
          {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
          {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl}
        }
        {
          \msg_warning:nnn { spath3 } { missing soft path } { #2 }
        }
      }
      {
        \msg_warning:nnn { spath3 } { missing soft path } { #1 }
      }
    }
    {
      \msg_warning:nn { spath3 } { load intersections }
    }
  },
  split~ globally~ at~ intersections~ with/.code~ n~ args={2}{
    \tl_if_exist:cTF
    {
      tikz@library@intersections@loaded
    }
    {
    \tl_if_exist:cTF
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      {
        \tl_if_exist:cTF
        {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl}
        {
          \spath_gsplit_path_at_intersections:cv
          {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
          {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl}
        }
        {
          \msg_warning:nnn { spath3 } { missing soft path } { #2 }
        }
      }
      {
        \msg_warning:nnn { spath3 } { missing soft path } { #1 }
      }
    }
    {
      \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
    }
    {
      \tl_if_exist:cTF
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      {
        \tl_if_exist:cTF
        {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl}
        {
          \spath_split_at_intersections:cc
          {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
          {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl}
        }
        {
          \msg_warning:nnn { spath3 } { missing soft path } { #2 }
        }
      }
      {
        \msg_warning:nnn { spath3 } { missing soft path } { #1 }
      }
    }
    {
      \msg_warning:nn { spath3 } { load intersections }
    }
  },
  split~ globally~ at~ intersections/.code~ n~ args={2}{
    \tl_if_exist:cTF
    {
      tikz@library@intersections@loaded
    }
    {
      \tl_if_exist:cTF
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      {
        \tl_if_exist:cTF
        {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl}
        {
          \spath_gsplit_at_intersections:cc
          {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
          {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl}
        }
        {
          \msg_warning:nnn { spath3 } { missing soft path } { #2 }
        }
      }
      {
        \msg_warning:nnn { spath3 } { missing soft path } { #1 }
      }
    }
    {
      \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
    }
    {
      \tl_if_exist:cTF
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      {
        \spath_split_at_self_intersections:c
        {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      }
      {
        \msg_warning:nnn { spath3 } { missing soft path } { #1 }
      }
    }
    {
      \msg_warning:nn { spath3 } { load intersections }
    }
  },
  split~ globally~ at~ self~ intersections/.code={
    \tl_if_exist:cTF
    {
      tikz@library@intersections@loaded
    }
    {
      \tl_if_exist:cTF
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      {
        \spath_gsplit_at_self_intersections:c
        {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      }
      {
        \msg_warning:nnn { spath3 } { missing soft path } { #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={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \clist_clear_new:N #2
      \spath_components_to_seq:Nv
      \l_@@_tmpa_seq
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      \seq_map_inline:Nn \l_@@_tmpa_seq
      {
        \tl_new:c
        {
          \tl_use:N \l_@@_prefix_tl
          anonymous_\int_use:N \g_@@_anon_int
          \tl_use:N \l_@@_suffix_tl
        }
        \tl_set:cn
        {
          \tl_use:N \l_@@_prefix_tl
          anonymous_\int_use:N \g_@@_anon_int
          \tl_use:N \l_@@_suffix_tl
        } {##1}
        \clist_put_right:Nx #2 {anonymous_\int_use:N \g_@@_anon_int}
        \int_gincr:N \g_@@_anon_int
      }
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
  get~ components~ of~ globally/.code~ 2~ args={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \clist_gclear_new:N #2
      \spath_components_to_seq:Nv
      \l_@@_tmpa_seq
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      \seq_map_inline:Nn \l_@@_tmpa_seq
      {
        \tl_new:c
        {
          \tl_use:N \l_@@_prefix_tl
          anonymous_\int_use:N \g_@@_anon_int
          \tl_use:N \l_@@_suffix_tl
        }
        \tl_gset:cn
        {
          \tl_use:N \l_@@_prefix_tl
          anonymous_\int_use:N \g_@@_anon_int
          \tl_use:N \l_@@_suffix_tl
        } {##1}
        \clist_gput_right:Nx #2 {anonymous_\int_use:N \g_@@_anon_int}
        \int_gincr:N \g_@@_anon_int
      }
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #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={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \group_begin:
      \spath_components_to_seq:Nv
      \l_@@_tmpa_seq
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      \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:
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #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_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \group_begin:
      \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
      }
      \seq_gclear:N \g_@@_tmpa_seq
      \seq_gclear:N \g_@@_tmpb_seq
      \spath_numberofcomponents:Nv \l_@@_tmpa_int
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}

      \spath_components_to_seq:Nv
      \l_@@_tmpa_seq
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      
      \tl_if_empty:NTF \l_@@_tmpd_tl
      {
        \int_step_inline:nnnn {1}{1} { \l_@@_tmpa_int - 1 }
        {
          \seq_gput_right:Nn \g_@@_tmpa_seq {##1}
          \seq_gput_right:Nx
          \g_@@_tmpb_seq
          {\int_eval:n {##1 + 1}}
        }
      }
      {
        \foreach \l_@@_tmpa_tl in \l_@@_tmpd_tl
        {
          \seq_gput_right:NV \g_@@_tmpa_seq \l_@@_tmpa_tl
          \seq_gput_right:Nx
          \g_@@_tmpb_seq
          {\int_eval:n
            {
              \int_mod:nn { \l_@@_tmpa_tl }{ \l_@@_tmpa_int } + 1
            }
          }
        }
      }

      \seq_clear:N \l_@@_tmpb_seq
      \seq_map_indexed_inline:Nn \l_@@_tmpa_seq
      {
        \tl_set:Nn \l_@@_tmpa_tl {##2}
        \seq_if_in:NnT \g_@@_tmpa_seq {##1}
        {
          \spath_shorten_at_end:Nn \l_@@_tmpa_tl {\l_@@_tmpc_tl/2}
        }
        \seq_if_in:NnT \g_@@_tmpb_seq {##1}
        {
          \spath_shorten_at_start:Nn \l_@@_tmpa_tl {\l_@@_tmpc_tl/2}
        }
        \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpa_tl
      }
      \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpb_seq {} }
      \group_end:
      \tl_set_eq:cN
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      \g_@@_output_tl
      \tl_gclear:N \g_@@_output_tl
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
  insert~ gaps~ globally~ after~ components/.code~ 2~ args={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \group_begin:
      \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
      }
      \seq_gclear:N \g_@@_tmpa_seq
      \seq_gclear:N \g_@@_tmpb_seq
      \spath_numberofcomponents:Nv \l_@@_tmpa_int
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}

      \spath_components_to_seq:Nv
      \l_@@_tmpa_seq
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      
      \tl_if_empty:NTF \l_@@_tmpd_tl
      {
        \int_step_inline:nnnn {1}{1} { \l_@@_tmpa_int - 1 }
        {
          \seq_gput_right:Nn \g_@@_tmpa_seq {##1}
          \seq_gput_right:Nx
          \g_@@_tmpb_seq
          {\int_eval:n {##1 + 1}}
        }
      }
      {
        \foreach \l_@@_tmpa_tl in \l_@@_tmpd_tl
        {
          \seq_gput_right:NV \g_@@_tmpa_seq \l_@@_tmpa_tl
          \seq_gput_right:Nx
          \g_@@_tmpb_seq
          {\int_eval:n
            {
              \int_mod:nn { \l_@@_tmpa_tl }{ \l_@@_tmpa_int } + 1
            }
          }
        }
      }

      \seq_clear:N \l_@@_tmpb_seq
      \seq_map_indexed_inline:Nn \l_@@_tmpa_seq
      {
        \tl_set:Nn \l_@@_tmpa_tl {##2}
        \seq_if_in:NnT \g_@@_tmpa_seq {##1}
        {
          \spath_shorten_at_end:Nn \l_@@_tmpa_tl {\l_@@_tmpc_tl/2}
        }
        \seq_if_in:NnT \g_@@_tmpb_seq {##1}
        {
          \spath_shorten_at_start:Nn \l_@@_tmpa_tl {\l_@@_tmpc_tl/2}
        }
        \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpa_tl
      }
      \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpb_seq {} }
      \group_end:
      \tl_gset_eq:cN
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      \g_@@_output_tl
      \tl_gclear:N \g_@@_output_tl
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
%    \end{macrocode}
%
% Join the specified components together, joining each to its previous one.
%
%    \begin{macrocode}
  join~ components/.code~ 2~ args={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \seq_gclear:N \g_@@_tmpa_seq
      \foreach \l_@@_tmpa_tl in {#2}
      {
        \seq_gput_right:NV \g_@@_tmpa_seq \l_@@_tmpa_tl
      }
      \seq_gsort:Nn \g_@@_tmpa_seq
      {
        \int_compare:nNnTF {##1} > {##2}
        { \sort_return_same: }
        { \sort_return_swapped: }
      }
      \seq_map_inline:Nn \g_@@_tmpa_seq
      {
        \spath_join_component:cn
        {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}{##1}
      }
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
  join~ components~ globally/.code~ 2~ args={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \seq_gclear:N \g_@@_tmpa_seq
      \foreach \l_@@_tmpa_tl in {#2}
      {
        \seq_gput_right:NV \g_@@_tmpa_seq \l_@@_tmpa_tl
      }
      \seq_gsort:Nn \g_@@_tmpa_seq
      {
        \int_compare:nNnTF {##1} > {##2}
        { \sort_return_same: }
        { \sort_return_swapped: }
      }
      \seq_map_inline:Nn \g_@@_tmpa_seq
      {
        \spath_gjoin_component:cn
        {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}{##1}
      }
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
%    \end{macrocode}
%
% Remove all components of the path that don't actually draw anything.
%
%    \begin{macrocode}
  remove~ empty~ components/.code={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \spath_remove_empty_components:c
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
  remove~ empty~ components~ globally/.code={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \spath_gremove_empty_components:c
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
%    \end{macrocode}
%
% Replace all line segments by B\'ezier curves.
%
%    \begin{macrocode}
  replace~ lines/.code={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \spath_replace_lines:c
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
  replace~ lines~ globally/.code={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \spath_greplace_lines:c
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
%    \end{macrocode}
%
%
% Join the specified components together, joining each to its previous one.
%
%    \begin{macrocode}
  remove~ components/.code~ 2~ args={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \seq_gclear:N \g_@@_tmpa_seq
      \foreach \l_@@_tmpa_tl in {#2}
      {
        \seq_gput_right:NV \g_@@_tmpa_seq \l_@@_tmpa_tl
      }
      \seq_gsort:Nn \g_@@_tmpa_seq
      {
        \int_compare:nNnTF {##1} < {##2}
        { \sort_return_same: }
        { \sort_return_swapped: }
      }
      \spath_components_to_seq:Nv
      \l_@@_tmpa_seq
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      \seq_gpop_left:NNF \g_@@_tmpa_seq \l_@@_tmpa_tl
      {
        \tl_clear:N \l_@@_tmpa_tl
      }
      \seq_clear:N \l_@@_tmpb_seq
      \seq_map_indexed_inline:Nn \l_@@_tmpa_seq
      {
        \tl_set:Nn \l_@@_tmpb_tl {##1}
        \tl_if_eq:NNTF \l_@@_tmpb_tl \l_@@_tmpa_tl
        {
          \seq_gpop_left:NNF \g_@@_tmpa_seq \l_@@_tmpa_tl
          {
            \tl_clear:N \l_@@_tmpa_tl
          }
        }
        {
          \seq_put_right:Nn \l_@@_tmpb_seq {##2}
        }
      }
      \tl_set:cx {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      {\seq_use:Nn \l_@@_tmpb_seq {} }
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
  remove~ components~ globally/.code~ 2~ args={
    \tl_if_exist:cTF
    {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
    {
      \seq_gclear:N \g_@@_tmpa_seq
      \foreach \l_@@_tmpa_tl in {#2}
      {
        \seq_gput_right:NV \g_@@_tmpa_seq \l_@@_tmpa_tl
      }
      \seq_gsort:Nn \g_@@_tmpa_seq
      {
        \int_compare:nNnTF {##1} < {##2}
        { \sort_return_same: }
        { \sort_return_swapped: }
      }
      \spath_components_to_seq:Nv
      \l_@@_tmpa_seq
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      \seq_gpop_left:NNF \g_@@_tmpa_seq \l_@@_tmpa_tl
      {
        \tl_clear:N \l_@@_tmpa_tl
      }
      \seq_clear:N \l_@@_tmpb_seq
      \seq_map_indexed_inline:Nn \l_@@_tmpa_seq
      {
        \tl_set:Nn \l_@@_tmpb_tl {##1}
        \tl_if_eq:NNTF \l_@@_tmpb_tl \l_@@_tmpa_tl
        {
          \seq_gpop_left:NNF \g_@@_tmpa_seq \l_@@_tmpa_tl
          {
            \tl_clear:N \l_@@_tmpa_tl
          }
        }
        {
          \seq_put_right:Nn \l_@@_tmpb_seq {##2}
        }
      }
      \tl_gset:cx {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      {\seq_use:Nn \l_@@_tmpb_seq {} }
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
    }
  },
%    \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/.is~ choice,
  draft~ mode/true/.code={
    \bool_set_true:N \l_@@_draft_bool
  },
  draft~ mode/false/.code={
    \bool_set_false:N \l_@@_draft_bool
  },
  maybe~ spot~ weld/.code={
    \bool_if:NF \l_@@_draft_bool
    {
      \tl_if_exist:cTF
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      {
        \spath_spot_weld_components:c
        {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      }
      {
        \msg_warning:nnn { spath3 } { missing soft path } { #1 }
      }
    }
  },
  maybe~ spot~ weld~ globally/.code={
    \bool_if:NF \l_@@_draft_bool
    {
      \tl_if_exist:cTF
      {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      {
        \spath_spot_gweld_components:c
        {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
      }
      {
        \msg_warning:nnn { spath3 } { missing soft path } { #1 }
      }
    }
  },
%    \end{macrocode}
%
% Set the transformation to lie along a path.
%
%    \begin{macrocode}
  transform~ to/.code~ 2~ args={
    \group_begin:
    \tl_if_exist:cTF
    {
      \tl_use:N \l_@@_prefix_tl
      #1
      \tl_use:N \l_@@_suffix_tl
    }
    {
      \spath_reallength:Nv
      \l_@@_tmpa_int
      {
        \tl_use:N \l_@@_prefix_tl
        #1
        \tl_use:N \l_@@_suffix_tl
      }
      
      \tl_set:Nx \l_@@_tmpb_tl
      {\fp_to_decimal:n {(#2) * (\l_@@_tmpa_int)}}
      \spath_transformation_at:NvV \l_@@_tmpc_tl
      {
        \tl_use:N \l_@@_prefix_tl
        #1
        \tl_use:N \l_@@_suffix_tl
      }
      \l_@@_tmpb_tl
      \tl_gset_eq:NN \g_@@_smuggle_tl \l_@@_tmpc_tl
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
      \tl_gset_eq:NN \g_@@_smuggle_tl { {1}{0}{0}{1}{0pt}{0pt} }
    }
    \group_end:
    \exp_last_unbraced:NV \pgfsettransformentries \g_@@_smuggle_tl
    \tl_gclear:N \g_@@_smuggle_tl
  },
%    \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={
    \group_begin:
    \tl_if_exist:cTF
    {
      \tl_use:N \l_@@_prefix_tl
      #1
      \tl_use:N \l_@@_suffix_tl
    }
    {
      \spath_reallength:Nv
      \l_@@_tmpa_int
      {
        \tl_use:N \l_@@_prefix_tl
        #1
        \tl_use:N \l_@@_suffix_tl
      }
      
      \tl_set:Nx \l_@@_tmpb_tl
      {\fp_to_decimal:n {(#2) * (\l_@@_tmpa_int)}}
      \spath_transformation_at:NvV \l_@@_tmpc_tl
      {
        \tl_use:N \l_@@_prefix_tl
        #1
        \tl_use:N \l_@@_suffix_tl
      }
      \l_@@_tmpb_tl
      \tl_gset_eq:NN \g_@@_smuggle_tl \l_@@_tmpc_tl
    }
    {
      \msg_warning:nnn { spath3 } { missing soft path } { #1 }
      \tl_gset_eq:NN \g_@@_smuggle_tl { {1}{0}{0}{1}{0pt}{0pt} }
    }
    \fp_compare:nT { \tl_item:Nn \g_@@_smuggle_tl {4} < 0}
    {
      \tl_gset:Nx \g_@@_smuggle_tl
      {
        { \fp_eval:n { - (\tl_item:Nn \g_@@_smuggle_tl {1})} }
        { \fp_eval:n { - (\tl_item:Nn \g_@@_smuggle_tl {2})} }
        { \fp_eval:n { - (\tl_item:Nn \g_@@_smuggle_tl {3})} }
        { \fp_eval:n { - (\tl_item:Nn \g_@@_smuggle_tl {4})} }
        { \tl_item:Nn \g_@@_smuggle_tl {5} }
        { \tl_item:Nn \g_@@_smuggle_tl {6} }
      }
    }
    \group_end:
    \exp_last_unbraced:NV \pgfsettransformentries \g_@@_smuggle_tl
    \tl_gclear:N \g_@@_smuggle_tl
  },
%    \end{macrocode}
%
% This is a useful set of styles for drawing a knot diagram.
%
%    \begin{macrocode}
  knot/.style~ n~ args={3}{
    spath/split~ at~ self~ intersections=#1,
    spath/remove~ empty~ components=#1,
    spath/insert~ gaps~ after~ components={#1}{#2}{#3},
    spath/maybe~ spot~ weld=#1,
    spath/render~ components=#1
  },
  global~ knot/.style~ n~ args={3}{
    spath/split~ globally~ at~ self~ intersections=#1,
    spath/remove~ empty~ components~ globally=#1,
    spath/insert~ gaps~ globally ~after~ components={#1}{#2}{#3},
    spath/maybe~ spot~ weld~ globally=#1,
    spath/render~ components=#1
  },
}
%    \end{macrocode}
%
% This defines a coordinate system that finds a position on a soft path.
%
%    \begin{macrocode}
\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 {~} }
  \tl_if_exist:cTF
  {
    \tl_use:N \l_@@_prefix_tl
    \tl_use:N \l_@@_tmpa_tl
    \tl_use:N \l_@@_suffix_tl
  }
  {

    \tl_set_eq:Nc
    \l_@@_tmpa_tl
    {
      \tl_use:N \l_@@_prefix_tl
      \tl_use:N \l_@@_tmpa_tl
      \tl_use:N \l_@@_suffix_tl
    }

    \tl_if_empty:NTF \l_@@_tmpa_tl
    {
      \tl_gset_eq:NN \g_@@_smuggle_tl \pgfpointorigin
    }
    {
      \spath_reallength:NV \l_@@_tmpa_int \l_@@_tmpa_tl
      \tl_set:Nx \l_@@_tmpb_tl
      {\fp_to_decimal:n {(\l_@@_tmpb_tl) * (\l_@@_tmpa_int)}}
      \spath_point_at:NVV \l_@@_tmpc_tl \l_@@_tmpa_tl \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_@@_smuggle_tl \l_@@_tmpd_tl
    }
  }
  {
    \msg_warning:nnx
    { spath3 }
    { missing soft path }
    { \tl_use:N \l_@@_tmpa_tl }
    \tl_gset_eq:NN \g_@@_smuggle_tl \pgfpointorigin
  }
  \group_end:
  \use:c {pgf@process}{%
    \tl_use:N \g_@@_smuggle_tl
    \pgftransforminvert
    \use:c {pgf@pos@transform@glob}
  }
}

\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}}

    \tl_case: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}}

    \tl_case: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

\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
  },
  draft/.is~ family,
  draft,
  crossing~ label/.style={
    overlay,
    fill=white,
    fill~ opacity=.5,
    text~ opacity=1,
    text=blue,
    pin~ edge={blue,<-}
  },
  strand~ label/.style={
    overlay,
    circle,
    draw=purple,
    fill=white,
    fill~ opacity=.5,
    text~ opacity=1,
    text=purple,
    inner~ sep=0pt
  },
}
%    \end{macrocode}
%
% Wrapper around \Verb+\tikzset+ for applying keys from a token list, checking for if the given token list exists.
%    \begin{macrocode}
\cs_new_nopar:Npn \knot_apply_style:N #1
{
  \tl_if_exist:NT #1 {
    \exp_args:NV \tikzset #1
  }
}
\cs_generate_variant:Nn \knot_apply_style:N {c}
%    \end{macrocode}
%
%\begin{macro}[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
{
  \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:
{
%    \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
}
%    \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
{
  \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
  }
  \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
{
  \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_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
{
  \tl_set:Nx \l_@@_tmpc_tl {\tl_item:nn {#1} {4}}
  \tl_case: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}
        \fp_compare:nTF
        {
          0 < \l_@@_tmpa_fp && \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
{
  \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}

  }

  \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
{
  \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
{
%    \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}
%    \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
%    \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}
          }
          {
            \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:N \l_@@_tmpg_tl}
              &&
              !\tl_if_empty_p: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}
              {
                \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 previous \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 previous \tl_use:N
                \l_@@_tmpg_tl}
              &&
              !\tl_if_empty_p: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}
              {final point}
              {
                \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
{
  \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:
{
  \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
{
  \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
{
  \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_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
{
  \tl_set:Nx \l_@@_tmpb_tl {\tl_item:nn {#1} {4}}
  \tl_case: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
