summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/spath3/spath3_code.dtx
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2021-01-23 22:17:41 +0000
committerKarl Berry <karl@freefriends.org>2021-01-23 22:17:41 +0000
commit3fc4de03f2da103a96f5d0cc757300f8c7e311f1 (patch)
tree69271c9383f64bb9905fa1e00910cf2b18c96119 /Master/texmf-dist/source/latex/spath3/spath3_code.dtx
parent03a1a8f6b693dc58a1840c8703af5c5399dc6117 (diff)
spath3 (23jan21)
git-svn-id: svn://tug.org/texlive/trunk@57504 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/latex/spath3/spath3_code.dtx')
-rw-r--r--Master/texmf-dist/source/latex/spath3/spath3_code.dtx6719
1 files changed, 6719 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/spath3/spath3_code.dtx b/Master/texmf-dist/source/latex/spath3/spath3_code.dtx
new file mode 100644
index 00000000000..c5c584c4f81
--- /dev/null
+++ b/Master/texmf-dist/source/latex/spath3/spath3_code.dtx
@@ -0,0 +1,6719 @@
+% \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-2019 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.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{6848}
+%
+% \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.}
+%
+% \DoNotIndex{\newcommand,\newenvironment}
+%
+% \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/01/19} {2.0} {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.
+% \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}
+%
+% We need a slew of temporary 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
+
+\seq_new:N \l_@@_tmpa_seq
+\seq_new:N \l_@@_tmpb_seq
+
+\tl_new:N \g_@@_output_tl
+\int_new:N \g_@@_output_int
+\seq_new:N \g_@@_output_seq
+
+\dim_new:N \l_@@_tmpa_dim
+\dim_new:N \l_@@_tmpb_dim
+\dim_new:N \l_@@_move_x_dim
+\dim_new:N \l_@@_move_y_dim
+\fp_new:N \l_@@_tmpa_fp
+\fp_new:N \l_@@_tmpb_fp
+\int_new:N \l_@@_tmpa_int
+
+\bool_new:N \g_@@_output_bool
+\bool_new:N \l_@@_closed_bool
+% \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}
+%
+% Groups and iterations don't mix well and I haven't got a good scheme for protecting local calculations when iterating, so we do our best with iteration-specific variables.
+% \begin{macrocode}
+\tl_new:N \l_@@_itera_tl
+\tl_new:N \l_@@_iterb_tl
+\tl_new:N \l_@@_iterc_tl
+\tl_new:N \l_@@_iterd_tl
+\tl_new:N \l_@@_iterp_tl
+\dim_new:N \l_@@_itera_dim
+\dim_new:N \l_@@_iterb_dim
+\seq_new:N \l_@@_iter_seq
+% \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}{
+% \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_@@_itera_tl {#1}
+ \tl_clear:N \l_@@_iterb_tl
+ \seq_clear:N \l_@@_iter_seq
+ \dim_zero:N \l_@@_itera_dim
+ \dim_zero:N \l_@@_iterb_dim
+
+ \bool_until_do:nn {
+ \tl_if_empty_p:N \l_@@_itera_tl
+ }
+ {
+ \tl_set:Nx \l_@@_iterc_tl {\tl_head:N \l_@@_itera_tl}
+ \tl_set:Nx \l_@@_itera_tl {\tl_tail:N \l_@@_itera_tl}
+ \tl_case:NnF \l_@@_iterc_tl
+ {
+ \c_spath_moveto_tl
+ {
+ \tl_set_eq:NN \l_@@_iterb_tl \c_spath_moveto_tl
+ \tl_put_right:Nx \l_@@_iterb_tl {{\tl_head:N \l_@@_itera_tl}}
+ \dim_set:Nn \l_@@_itera_dim {\tl_head:N \l_@@_itera_tl}
+ \tl_set:Nx \l_@@_itera_tl {\tl_tail:N \l_@@_itera_tl}
+
+ \tl_put_right:Nx \l_@@_iterb_tl {{\tl_head:N \l_@@_itera_tl}}
+ \dim_set:Nn \l_@@_iterb_dim {\tl_head:N \l_@@_itera_tl}
+ \tl_set:Nx \l_@@_itera_tl {\tl_tail:N \l_@@_itera_tl}
+
+ \tl_set:Nx \l_@@_iterd_tl {\tl_head:N \l_@@_itera_tl}
+ \tl_if_eq:NNF \l_@@_iterd_tl \c_spath_moveto_tl
+ {
+ \tl_clear:N \l_@@_iterb_tl
+ }
+
+ }
+
+ \c_spath_lineto_tl
+ {
+ \tl_set_eq:NN \l_@@_iterb_tl \c_spath_moveto_tl
+ \tl_put_right:Nx \l_@@_iterb_tl
+ {
+ {\dim_use:N \l_@@_itera_dim}
+ {\dim_use:N \l_@@_iterb_dim}
+ }
+ \tl_put_right:NV \l_@@_iterb_tl \c_spath_lineto_tl
+
+ \tl_put_right:Nx \l_@@_iterb_tl {{\tl_head:N \l_@@_itera_tl}}
+ \dim_set:Nn \l_@@_itera_dim {\tl_head:N \l_@@_itera_tl}
+ \tl_set:Nx \l_@@_itera_tl {\tl_tail:N \l_@@_itera_tl}
+
+ \tl_put_right:Nx \l_@@_iterb_tl {{\tl_head:N \l_@@_itera_tl}}
+ \dim_set:Nn \l_@@_iterb_dim {\tl_head:N \l_@@_itera_tl}
+ \tl_set:Nx \l_@@_itera_tl {\tl_tail:N \l_@@_itera_tl}
+
+ }
+
+ \c_spath_curvetoa_tl
+ {
+ \tl_set_eq:NN \l_@@_iterb_tl \c_spath_moveto_tl
+ \tl_put_right:Nx \l_@@_iterb_tl
+ {
+ {\dim_use:N \l_@@_itera_dim}
+ {\dim_use:N \l_@@_iterb_dim}
+ }
+ \tl_put_right:NV \l_@@_iterb_tl \c_spath_curvetoa_tl
+
+ \prg_replicate:nn {2} {
+ \tl_put_right:Nx \l_@@_iterb_tl {{\tl_head:N \l_@@_itera_tl}}
+ \tl_set:Nx \l_@@_itera_tl {\tl_tail:N \l_@@_itera_tl}
+ \tl_put_right:Nx \l_@@_iterb_tl {{\tl_head:N \l_@@_itera_tl}}
+ \tl_set:Nx \l_@@_itera_tl {\tl_tail:N \l_@@_itera_tl}
+ \tl_put_right:Nx \l_@@_iterb_tl {\tl_head:N \l_@@_itera_tl}
+ \tl_set:Nx \l_@@_itera_tl {\tl_tail:N \l_@@_itera_tl}
+ }
+
+ \tl_put_right:Nx \l_@@_iterb_tl {{\tl_head:N \l_@@_itera_tl}}
+ \dim_set:Nn \l_@@_itera_dim {\tl_head:N \l_@@_itera_tl}
+ \tl_set:Nx \l_@@_itera_tl {\tl_tail:N \l_@@_itera_tl}
+
+ \tl_put_right:Nx \l_@@_iterb_tl {{\tl_head:N \l_@@_itera_tl}}
+ \dim_set:Nn \l_@@_iterb_dim {\tl_head:N \l_@@_itera_tl}
+ \tl_set:Nx \l_@@_itera_tl {\tl_tail:N \l_@@_itera_tl}
+
+ }
+
+ \c_spath_closepath_tl
+ {
+ \tl_set_eq:NN \l_@@_iterb_tl \c_spath_moveto_tl
+ \tl_put_right:Nx \l_@@_iterb_tl
+ {
+ {\dim_use:N \l_@@_itera_dim}
+ {\dim_use:N \l_@@_iterb_dim}
+ }
+ \tl_put_right:NV \l_@@_iterb_tl \c_spath_lineto_tl
+
+ \tl_put_right:Nx \l_@@_iterb_tl {{\tl_head:N \l_@@_itera_tl}}
+ \dim_set:Nn \l_@@_itera_dim {\tl_head:N \l_@@_itera_tl}
+ \tl_set:Nx \l_@@_itera_tl {\tl_tail:N \l_@@_itera_tl}
+
+ \tl_put_right:Nx \l_@@_iterb_tl {{\tl_head:N \l_@@_itera_tl}}
+ \dim_set:Nn \l_@@_iterb_dim {\tl_head:N \l_@@_itera_tl}
+ \tl_set:Nx \l_@@_itera_tl {\tl_tail:N \l_@@_itera_tl}
+
+ }
+
+ }
+ {
+
+ \tl_set_eq:NN \l_@@_iterb_tl \l_@@_iterc_tl
+ \tl_put_right:Nx \l_@@_iterb_tl {{\tl_head:N \l_@@_itera_tl}}
+ \dim_set:Nn \l_@@_itera_dim {\tl_head:N \l_@@_itera_tl}
+ \tl_set:Nx \l_@@_itera_tl {\tl_tail:N \l_@@_itera_tl}
+
+ \tl_put_right:Nx \l_@@_iterb_tl {{\tl_head:N \l_@@_itera_tl}}
+ \dim_set:Nn \l_@@_iterb_dim {\tl_head:N \l_@@_itera_tl}
+ \tl_set:Nx \l_@@_itera_tl {\tl_tail:N \l_@@_itera_tl}
+
+ }
+
+ \tl_if_empty:NF \l_@@_iterb_tl
+ {
+ \seq_put_right:NV \l_@@_iter_seq \l_@@_iterb_tl
+ }
+ \tl_clear:N \l_@@_iterb_tl
+ }
+
+ \seq_gclear:N \g_@@_output_seq
+ \seq_gset_eq:NN \g_@@_output_seq \l_@@_iter_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}{
+% \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_@@_itera_tl {#1}
+ \seq_clear:N \l_@@_iter_seq
+ \tl_set:Nx \l_@@_itera_tl {\tl_tail:N \l_@@_itera_tl}
+ \tl_put_right:NV \l_@@_itera_tl \c_spath_moveto_tl
+ \tl_set_eq:NN \l_@@_iterb_tl \c_spath_moveto_tl
+ \bool_do_until:nn {
+ \tl_if_empty_p:N \l_@@_itera_tl
+ }
+ {
+ \tl_set:Nx \l_@@_iterc_tl {\tl_head:N \l_@@_itera_tl}
+ \tl_if_eq:NNT \l_@@_iterc_tl \c_spath_moveto_tl
+ {
+ \seq_put_right:NV \l_@@_iter_seq \l_@@_iterb_tl
+ \tl_clear:N \l_@@_iterb_tl
+ }
+ \tl_if_single:NTF \l_@@_iterc_tl
+ {
+ \tl_put_right:NV \l_@@_iterb_tl \l_@@_iterc_tl
+ }
+ {
+ \tl_put_right:Nx \l_@@_iterb_tl {{\l_@@_iterc_tl}}
+ }
+ \tl_set:Nx \l_@@_itera_tl {\tl_tail:N \l_@@_itera_tl}
+ }
+
+ \seq_gclear:N \g_@@_output_seq
+ \seq_gset_eq:NN \g_@@_output_seq \l_@@_iter_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}{\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}{
+% \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_@@_tmpc_tl {##1}
+ \tl_case:NnT \l_@@_tmpc_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}{
+% \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}
+\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}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{
+% \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_@@_tmpb_tl
+ \tl_set:Nx \l_@@_tmpb_tl
+ {
+ { \tl_item:nn {#1} {2} }
+ { \tl_item:nn {#1} {3} }
+ }
+ \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_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}{
+% \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}{
+% \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}{
+% \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}{
+% \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}{
+% \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}{
+% \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}{
+% \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}{
+% \spath_translate:Nnnn,
+% \spath_translate:Nnn,
+% \spath_gtranslate:Nnnn,
+% \spath_gtranslate:Nnn
+% }
+% Translates a path.
+% \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_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}{
+% \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}{
+% \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}{
+% \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:NV \l_@@_tmpc_tl \l_@@_tmpa_tl
+ \spath_initialpoint:NV \l_@@_tmpd_tl \l_@@_tmpb_tl
+
+ \dim_set:Nn \l_@@_tmpa_dim
+ {
+ \tl_item:Nn \l_@@_tmpc_tl {1}
+ -
+ \tl_item:Nn \l_@@_tmpd_tl {1}
+ }
+ \dim_set:Nn \l_@@_tmpb_dim
+ {
+ \tl_item:Nn \l_@@_tmpc_tl {2}
+ -
+ \tl_item:Nn \l_@@_tmpd_tl {2}
+ }
+
+ \spath_translate:NVV \l_@@_tmpb_tl \l_@@_tmpa_dim \l_@@_tmpb_dim
+
+ \prg_replicate:nn {3}
+ {
+ \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_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}{
+% \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_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}
+\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}
+\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}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{
+% \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}
+\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}
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}{
+% \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}{
+% \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}{
+% \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}{
+% \spath_close:Nn,
+% \spath_close:N,
+% \spath_gclose:Nn,
+% \spath_gclose:N
+% }
+% Appends a close path to the end of the path.
+% For now, the point is the initial or final point (respectively).
+% To be future proof, it ought to be the point of the adjacent move to.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \@@_close:n #1
+{
+ \group_begin:
+ \tl_set:Nn \l_@@_tmpa_tl {#1}
+ \spath_initialpoint: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_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}{
+% \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}{
+% \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}{\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}{
+% \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_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}{
+% \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{macrocode}
+\int_new:N \l_@@_split_int
+\int_new:N \l_@@_splitat_int
+\fp_new:N \l_@@_split_fp
+\bool_new:N \l_@@_split_bool
+\tl_new:N \l_@@_split_path_tl
+\tl_new:N \l_@@_split_patha_tl
+\tl_new:N \l_@@_split_pathb_tl
+\tl_new:N \l_@@_split_intoa_tl
+\tl_new:N \l_@@_split_intob_tl
+\dim_new:N \l_@@_splitx_dim
+\dim_new:N \l_@@_splity_dim
+% \end{macrocode}
+%
+% \begin{macro}{
+% \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_@@_splitat_int {\fp_to_int:n {floor(#2) + 1}}
+ \fp_set:Nn \l_@@_split_fp {#2 - floor(#2)}
+
+ % Is split point near one end or other of a component?
+ \fp_compare:nT
+ {
+ \l_@@_split_fp < 0.01
+ }
+ {
+ % Near the start, so we'll place it at the start
+ \fp_set:Nn \l_@@_split_fp {0}
+ }
+ \fp_compare:nT
+ {
+ \l_@@_split_fp > 0.99
+ }
+ {
+ % Near the end, so we'll place it at the end
+ \fp_set:Nn \l_@@_split_fp {0}
+ \int_incr:N \l_@@_splitat_int
+ }
+
+ \int_zero:N \l_@@_split_int
+ \bool_set_true:N \l_@@_split_bool
+
+ \tl_set:Nn \l_@@_split_path_tl {#1}
+ \tl_clear:N \l_@@_split_patha_tl
+
+ \dim_zero:N \l_@@_splitx_dim
+ \dim_zero:N \l_@@_splity_dim
+
+ \bool_until_do:nn {
+ \tl_if_empty_p:N \l_@@_split_path_tl
+ ||
+ \int_compare_p:n { \l_@@_splitat_int == \l_@@_split_int }
+ }
+ {
+ \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_split_path_tl}
+ \tl_set:Nx \l_@@_split_path_tl {\tl_tail:N \l_@@_split_path_tl }
+ \tl_case:Nn \l_@@_tmpc_tl
+ {
+ \c_spath_lineto_tl
+ {
+ \int_incr:N \l_@@_split_int
+ }
+ \c_spath_curvetoa_tl
+ {
+ \int_incr:N \l_@@_split_int
+ }
+ }
+ \int_compare:nT { \l_@@_split_int < \l_@@_splitat_int }
+ {
+ \tl_put_right:NV \l_@@_split_patha_tl \l_@@_tmpc_tl
+
+ \tl_put_right:Nx \l_@@_split_patha_tl
+ {{ \tl_head:N \l_@@_split_path_tl }}
+ \dim_set:Nn \l_@@_splitx_dim {\tl_head:N \l_@@_split_path_tl}
+ \tl_set:Nx \l_@@_split_path_tl {\tl_tail:N \l_@@_split_path_tl }
+
+ \tl_put_right:Nx \l_@@_split_patha_tl
+ {{ \tl_head:N \l_@@_split_path_tl }}
+ \dim_set:Nn \l_@@_splity_dim {\tl_head:N \l_@@_split_path_tl}
+ \tl_set:Nx \l_@@_split_path_tl {\tl_tail:N \l_@@_split_path_tl }
+
+ }
+ }
+
+ \tl_clear:N \l_@@_split_pathb_tl
+ \tl_put_right:NV \l_@@_split_pathb_tl \c_spath_moveto_tl
+ \tl_put_right:Nx \l_@@_split_pathb_tl
+ {
+ {\dim_use:N \l_@@_splitx_dim}
+ {\dim_use:N \l_@@_splity_dim}
+ }
+
+ \fp_compare:nTF
+ {
+ \l_@@_split_fp == 0
+ }
+ {
+ \tl_set_eq:NN \l_@@_split_intob_tl \l_@@_split_pathb_tl
+ \tl_if_empty:NF \l_@@_split_path_tl
+ {
+ \tl_put_right:NV \l_@@_split_intob_tl \l_@@_tmpc_tl
+ \tl_put_right:NV \l_@@_split_intob_tl \l_@@_split_path_tl
+ }
+ }
+ {
+
+ \tl_case:Nn \l_@@_tmpc_tl
+ {
+ \c_spath_lineto_tl
+ {
+ \tl_put_right:NV \l_@@_split_pathb_tl \l_@@_tmpc_tl
+ \tl_put_right:Nx \l_@@_split_pathb_tl
+ {{ \tl_head:N \l_@@_split_path_tl }}
+ \tl_set:Nx \l_@@_split_path_tl {\tl_tail:N \l_@@_split_path_tl }
+
+ \tl_put_right:Nx \l_@@_split_pathb_tl
+ {{ \tl_head:N \l_@@_split_path_tl }}
+ \tl_set:Nx \l_@@_split_path_tl {\tl_tail:N \l_@@_split_path_tl }
+
+ \spath_split_line:NNVV
+ \l_@@_split_intoa_tl
+ \l_@@_split_intob_tl
+ \l_@@_split_pathb_tl
+ \l_@@_split_fp
+
+ \prg_replicate:nn {3} {
+ \tl_set:Nx \l_@@_split_intoa_tl {\tl_tail:N \l_@@_split_intoa_tl}
+ }
+
+ \tl_put_right:NV \l_@@_split_patha_tl \l_@@_split_intoa_tl
+ \tl_put_right:NV \l_@@_split_intob_tl \l_@@_split_path_tl
+ }
+ \c_spath_curvetoa_tl
+ {
+ \tl_put_right:NV \l_@@_split_pathb_tl \l_@@_tmpc_tl
+ \tl_put_right:Nx \l_@@_split_pathb_tl
+ {{ \tl_head:N \l_@@_split_path_tl }}
+ \tl_set:Nx \l_@@_split_path_tl {\tl_tail:N \l_@@_split_path_tl }
+
+ \tl_put_right:Nx \l_@@_split_pathb_tl
+ {{ \tl_head:N \l_@@_split_path_tl }}
+ \tl_set:Nx \l_@@_split_path_tl {\tl_tail:N \l_@@_split_path_tl }
+
+ \prg_replicate:nn {2} {
+
+ \tl_put_right:Nx \l_@@_split_pathb_tl
+ { \tl_head:N \l_@@_split_path_tl }
+ \tl_set:Nx \l_@@_split_path_tl {\tl_tail:N \l_@@_split_path_tl }
+
+ \tl_put_right:Nx \l_@@_split_pathb_tl
+ {{ \tl_head:N \l_@@_split_path_tl }}
+ \tl_set:Nx \l_@@_split_path_tl {\tl_tail:N \l_@@_split_path_tl }
+
+ \tl_put_right:Nx \l_@@_split_pathb_tl
+ {{ \tl_head:N \l_@@_split_path_tl }}
+ \tl_set:Nx \l_@@_split_path_tl {\tl_tail:N \l_@@_split_path_tl }
+ }
+
+ \spath_split_curve:NNVV
+ \l_@@_split_intoa_tl
+ \l_@@_split_intob_tl
+ \l_@@_split_pathb_tl \l_@@_split_fp
+
+ \prg_replicate:nn {3} {
+ \tl_set:Nx \l_@@_split_intoa_tl {\tl_tail:N \l_@@_split_intoa_tl}
+ }
+
+ \tl_put_right:NV \l_@@_split_patha_tl \l_@@_split_intoa_tl
+ \tl_put_right:NV \l_@@_split_intob_tl \l_@@_split_path_tl
+ }
+ }
+ }
+
+ \tl_gclear:N \g_@@_output_tl
+ \@@_tl_gput_right_braced:NV \g_@@_output_tl \l_@@_split_patha_tl
+ \@@_tl_gput_right_braced:NV \g_@@_output_tl \l_@@_split_intob_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{macrocode}
+\tl_new:N \l_@@_shorten_fa_tl
+\tl_new:N \l_@@_shorten_path_tl
+\tl_new:N \l_@@_shorten_last_tl
+\tl_new:N \l_@@_shorten_lasta_tl
+\tl_new:N \l_@@_shorten_lastb_tl
+\int_new:N \l_@@_shorten_int
+\fp_new:N \l_@@_shorten_x_fp
+\fp_new:N \l_@@_shorten_y_fp
+\fp_new:N \l_@@_shorten_len_fp
+% \end{macrocode}
+%
+% \begin{macro}{
+% \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_@@_shorten_path_tl {#1}
+ \tl_reverse:N \l_@@_shorten_path_tl
+
+ \tl_set:Nx \l_@@_shorten_fa_tl {\tl_item:Nn \l_@@_shorten_path_tl {3}}
+
+ \tl_clear:N \l_@@_shorten_last_tl
+ \tl_if_eq:NNTF \l_@@_shorten_fa_tl \c_spath_curveto_tl
+ {
+ \int_set:Nn \l_@@_shorten_int {3}
+ }
+ {
+ \int_set:Nn \l_@@_shorten_int {1}
+ }
+
+ \prg_replicate:nn { \l_@@_shorten_int }
+ {
+ \tl_put_right:Nx \l_@@_shorten_last_tl
+ {
+ {\tl_head:N \l_@@_shorten_path_tl}
+ }
+ \tl_set:Nx \l_@@_shorten_path_tl {\tl_tail:N \l_@@_shorten_path_tl}
+ \tl_put_right:Nx \l_@@_shorten_last_tl
+ {
+ {\tl_head:N \l_@@_shorten_path_tl}
+ }
+ \tl_set:Nx \l_@@_shorten_path_tl {\tl_tail:N \l_@@_shorten_path_tl}
+ \tl_put_right:Nx \l_@@_shorten_last_tl
+ {
+ \tl_head:N \l_@@_shorten_path_tl
+ }
+ \tl_set:Nx \l_@@_shorten_path_tl {\tl_tail:N \l_@@_shorten_path_tl}
+ }
+
+ \tl_put_right:Nx \l_@@_shorten_last_tl
+ {
+ {\tl_item:Nn \l_@@_shorten_path_tl {1}}
+ {\tl_item:Nn \l_@@_shorten_path_tl {2}}
+ }
+ \tl_put_right:NV \l_@@_shorten_last_tl \c_spath_moveto_tl
+
+ \tl_reverse:N \l_@@_shorten_path_tl
+
+ \fp_set:Nn \l_@@_shorten_x_fp
+ {
+ \dim_to_fp:n {\tl_item:Nn \l_@@_shorten_last_tl {4}}
+ -
+ \dim_to_fp:n {\tl_item:Nn \l_@@_shorten_last_tl {1}}
+ }
+
+ \fp_set:Nn \l_@@_shorten_y_fp
+ {
+ \dim_to_fp:n {\tl_item:Nn \l_@@_shorten_last_tl {5}}
+ -
+ \dim_to_fp:n {\tl_item:Nn \l_@@_shorten_last_tl {2}}
+ }
+
+ \fp_set:Nn \l_@@_shorten_len_fp
+ {
+ sqrt( \l_@@_shorten_x_fp * \l_@@_shorten_x_fp + \l_@@_shorten_y_fp * \l_@@_shorten_y_fp )
+ }
+
+ \fp_compare:nTF
+ {
+ \l_@@_shorten_len_fp > #2
+ }
+ {
+
+ \fp_set:Nn \l_@@_shorten_len_fp
+ {
+ (\l_@@_shorten_len_fp - #2)/ \l_@@_shorten_len_fp
+ }
+
+ \tl_reverse:N \l_@@_shorten_last_tl
+
+ \tl_if_eq:NNTF \l_@@_shorten_fa_tl \c_spath_curveto_tl
+ {
+ \fp_set:Nn \l_@@_shorten_len_fp
+ {
+ 1 - (1 -\l_@@_shorten_len_fp)/3
+ }
+ \spath_split_curve:NNVV
+ \l_@@_shorten_lasta_tl
+ \l_@@_shorten_lastb_tl
+ \l_@@_shorten_last_tl
+ \l_@@_shorten_len_fp
+ }
+ {
+ \spath_split_line:NNVV
+ \l_@@_shorten_lasta_tl
+ \l_@@_shorten_lastb_tl
+ \l_@@_shorten_last_tl
+ \l_@@_shorten_len_fp
+ }
+
+ \prg_replicate:nn {3}
+ {
+ \tl_set:Nx \l_@@_shorten_lasta_tl {\tl_tail:N \l_@@_shorten_lasta_tl}
+ }
+
+ \tl_put_right:NV \l_@@_shorten_path_tl \l_@@_shorten_lasta_tl
+
+ }
+ {
+
+ \int_compare:nT
+ {
+ \tl_count:N \l_@@_shorten_path_tl > 3
+ }
+ {
+ \dim_set:Nn \l_@@_tmpa_dim {\fp_to_dim:n {#2 - \l_@@_shorten_len_fp } }
+ \spath_shorten_at_end:NV \l_@@_shorten_path_tl \l_@@_tmpa_dim
+ }
+ }
+
+ \tl_gset_eq:NN \g_@@_output_tl \l_@@_shorten_path_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}{
+% \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_@@_shorten_path_tl {#1}
+
+ \tl_set:Nx \l_@@_shorten_fa_tl {\tl_item:Nn \l_@@_shorten_path_tl {4}}
+
+ \tl_clear:N \l_@@_shorten_last_tl
+
+ \tl_if_eq:NNTF \l_@@_shorten_fa_tl \c_spath_curvetoa_tl
+ {
+ \int_set:Nn \l_@@_shorten_int {3}
+ }
+ {
+ \int_set:Nn \l_@@_shorten_int {1}
+ }
+
+ \tl_set_eq:NN \l_@@_shorten_last_tl \c_spath_moveto_tl
+ \tl_set:Nx \l_@@_shorten_path_tl {\tl_tail:N \l_@@_shorten_path_tl }
+
+ \prg_replicate:nn { \l_@@_shorten_int }
+ {
+ \@@_tl_put_right_braced:Nx \l_@@_shorten_last_tl {\tl_item:Nn \l_@@_shorten_path_tl {1}}
+ \@@_tl_put_right_braced:Nx \l_@@_shorten_last_tl {\tl_item:Nn \l_@@_shorten_path_tl {2}}
+ \tl_put_right:Nx \l_@@_shorten_last_tl {\tl_item:Nn \l_@@_shorten_path_tl {3}}
+
+ \prg_replicate:nn {3}
+ {
+ \tl_set:Nx \l_@@_shorten_path_tl {\tl_tail:N \l_@@_shorten_path_tl }
+ }
+ }
+ \@@_tl_put_right_braced:Nx \l_@@_shorten_last_tl {\tl_item:Nn \l_@@_shorten_path_tl {1}}
+ \@@_tl_put_right_braced:Nx \l_@@_shorten_last_tl {\tl_item:Nn \l_@@_shorten_path_tl {2}}
+
+ \fp_set:Nn \l_@@_shorten_x_fp
+ {
+ \dim_to_fp:n {\tl_item:Nn \l_@@_shorten_last_tl {5}}
+ -
+ \dim_to_fp:n {\tl_item:Nn \l_@@_shorten_last_tl {2}}
+ }
+
+ \fp_set:Nn \l_@@_shorten_y_fp
+ {
+ \dim_to_fp:n {\tl_item:Nn \l_@@_shorten_last_tl {6}}
+ -
+ \dim_to_fp:n {\tl_item:Nn \l_@@_shorten_last_tl {3}}
+ }
+
+ \fp_set:Nn \l_@@_shorten_len_fp
+ {
+ sqrt( \l_@@_shorten_x_fp * \l_@@_shorten_x_fp + \l_@@_shorten_y_fp * \l_@@_shorten_y_fp )
+ }
+
+ \fp_compare:nTF
+ {
+ \l_@@_shorten_len_fp > #2
+ }
+ {
+
+ \fp_set:Nn \l_@@_shorten_len_fp
+ {
+ #2/ \l_@@_shorten_len_fp
+ }
+
+ \tl_if_eq:NNTF \l_@@_shorten_fa_tl \c_spath_curvetoa_tl
+ {
+ \fp_set:Nn \l_@@_shorten_len_fp
+ {
+ \l_@@_shorten_len_fp/3
+ }
+ \spath_split_curve:NNVV
+ \l_@@_shorten_lasta_tl
+ \l_@@_shorten_lastb_tl
+ \l_@@_shorten_last_tl
+ \l_@@_shorten_len_fp
+ }
+ {
+ \spath_split_line:NNVV
+ \l_@@_shorten_lasta_tl
+ \l_@@_shorten_lastb_tl
+ \l_@@_shorten_last_tl
+ \l_@@_shorten_len_fp
+ }
+
+ \prg_replicate:nn {2}
+ {
+ \tl_set:Nx \l_@@_shorten_path_tl {\tl_tail:N \l_@@_shorten_path_tl}
+ }
+
+ \tl_put_left:NV \l_@@_shorten_path_tl \l_@@_shorten_lastb_tl
+
+ }
+ {
+
+ \tl_put_left:NV \l_@@_shorten_path_tl \c_spath_moveto_tl
+
+ \int_compare:nT
+ {
+ \tl_count:N \l_@@_shorten_path_tl > 3
+ }
+ {
+ \dim_set:Nn \l_@@_tmpa_dim {\fp_to_dim:n {#2 - \l_@@_shorten_len_fp } }
+ \spath_shorten_at_start:NV \l_@@_shorten_path_tl \l_@@_tmpa_dim
+ }
+ }
+
+ \tl_gset_eq:NN \g_@@_output_tl \l_@@_shorten_path_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}{
+% \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}{
+% \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}{
+% \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{macrocode}
+\tl_new:N \l_@@_split_path_a_tl
+\tl_new:N \l_@@_split_path_b_tl
+\tl_new:N \l_@@_split_path_a_start_tl
+\tl_new:N \l_@@_split_path_b_start_tl
+\tl_new:N \l_@@_split_path_a_end_tl
+\tl_new:N \l_@@_split_path_b_end_tl
+\tl_new:N \l_@@_split_path_a_final_tl
+\tl_new:N \l_@@_split_path_b_final_tl
+
+\tl_new:N \l_@@_split_prev_first_tl
+\tl_new:N \l_@@_split_prev_second_tl
+
+\seq_new:N \l_@@_split_first_seq
+\seq_new:N \l_@@_split_second_seq
+
+\int_new:N \l_@@_split_segment_int
+% \end{macrocode}
+%
+% \begin{macro}{
+% \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
+ }
+}
+\tl_new:N \l_@@_intersecta_tl
+\tl_new:N \l_@@_intersectb_tl
+\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}{
+% \spath_split_at_intersections:NNnn,
+% \spath_split_at_intersections:NN,
+% \spath_gsplit_at_intersections:NNnn,
+% \spath_gsplit_at_intersections:NN
+% }
+% Given two paths, split them at points where they intersect and store them in the macros.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \@@_split_at_intersections:nn #1#2
+{
+ \group_begin:
+
+ % Clear some token lists and sequences
+ \tl_clear:N \l_@@_split_path_a_final_tl
+ \tl_clear:N \l_@@_split_path_b_final_tl
+ \seq_clear:N \l_@@_split_first_seq
+ \seq_clear:N \l_@@_split_second_seq
+
+ \pgfintersectionsortbyfirstpath
+ % Find the intersections of these segments
+ \tl_set:Nn \l_@@_split_path_a_tl {#1}
+ \tl_set:Nn \l_@@_split_path_b_tl {#2}
+ % Remove empty components
+% \spath_path_remove_empty_components:N \l_@@_split_path_a_tl
+% \spath_path_remove_empty_components:N \l_@@_split_path_b_tl
+
+ \spath_intersect:NN \l_@@_split_path_a_tl \l_@@_split_path_b_tl
+
+ % If we get intersections
+ \int_compare:nT {\pgfintersectionsolutions > 0}
+ {
+ % Find the times of the intersections on each path
+ \int_step_inline:nnnn {1} {1} {\pgfintersectionsolutions}
+ {
+ \pgfintersectiongetsolutiontimes{##1}{\l_@@_split_first_tl}{\l_@@_split_second_tl}
+ \seq_put_left:NV \l_@@_split_first_seq \l_@@_split_first_tl
+ }
+ }
+
+ \spath_intersect:NN \l_@@_split_path_b_tl \l_@@_split_path_a_tl
+
+ % If we get intersections
+ \int_compare:nT {\pgfintersectionsolutions > 0}
+ {
+ % Find the times of the intersections on each path
+ \int_step_inline:nnnn {1} {1} {\pgfintersectionsolutions}
+ {
+ \pgfintersectiongetsolutiontimes{##1}{\l_@@_split_first_tl}{\l_@@_split_second_tl}
+ \seq_put_left:NV \l_@@_split_second_seq \l_@@_split_first_tl
+ }
+ }
+
+ \tl_set:Nn \l_@@_split_prev_first_tl {-1}
+
+ \seq_map_inline:Nn \l_@@_split_first_seq
+ {
+ \tl_set:Nn \l_@@_split_first_tl {##1}
+
+ \tl_set_eq:NN \l_@@_tmpa_tl \l_@@_split_first_tl
+ \int_compare:nT
+ {
+ \fp_to_int:n {floor( \l_@@_split_first_tl) }
+ =
+ \fp_to_int:n {floor( \l_@@_split_prev_first_tl) }
+ }
+ {
+ \tl_set:Nx \l_@@_split_first_tl
+ {
+ \fp_eval:n {
+ floor( \l_@@_split_first_tl )
+ +
+ ( \l_@@_split_first_tl - floor( \l_@@_split_first_tl) )
+ /
+ ( \l_@@_split_prev_first_tl - floor( \l_@@_split_prev_first_tl) )
+ }
+ }
+ }
+ \tl_set_eq:NN \l_@@_split_prev_first_tl \l_@@_tmpa_tl
+
+ \spath_split_at:NNVV \l_@@_split_path_a_start_tl \l_@@_split_path_a_end_tl \l_@@_split_path_a_tl \l_@@_split_first_tl
+
+ \tl_put_left:NV \l_@@_split_path_a_final_tl \l_@@_split_path_a_end_tl
+ \tl_set_eq:NN \l_@@_split_path_a_tl \l_@@_split_path_a_start_tl
+
+ }
+
+ \tl_set:Nn \l_@@_split_prev_second_tl {-1}
+
+ \seq_map_inline:Nn \l_@@_split_second_seq
+ {
+ \tl_set:Nn \l_@@_split_second_tl {##1}
+
+ \tl_set_eq:NN \l_@@_tmpa_tl \l_@@_split_second_tl
+ \int_compare:nT
+ {
+ \fp_to_int:n {floor( \l_@@_split_second_tl) }
+ =
+ \fp_to_int:n {floor( \l_@@_split_prev_second_tl) }
+ }
+ {
+ \tl_set:Nx \l_@@_split_second_tl
+ {
+ \fp_eval:n {
+ floor( \l_@@_split_second_tl )
+ +
+ ( \l_@@_split_second_tl - floor( \l_@@_split_second_tl) )
+ /
+ ( \l_@@_split_prev_second_tl - floor( \l_@@_split_prev_second_tl) )
+ }
+ }
+ }
+ \tl_set_eq:NN \l_@@_split_prev_second_tl \l_@@_tmpa_tl
+
+ \spath_split_at:NNVV \l_@@_split_path_b_start_tl \l_@@_split_path_b_end_tl \l_@@_split_path_b_tl\l_@@_split_second_tl
+
+ \tl_put_left:NV \l_@@_split_path_b_final_tl \l_@@_split_path_b_end_tl
+ \tl_set_eq:NN \l_@@_split_path_b_tl \l_@@_split_path_b_start_tl
+
+ }
+
+ \tl_put_left:NV \l_@@_split_path_a_final_tl \l_@@_split_path_a_tl
+ \tl_put_left:NV \l_@@_split_path_b_final_tl \l_@@_split_path_b_tl
+
+ \tl_if_empty:NT \l_@@_split_path_a_final_tl
+ {
+ \tl_set_eq:NN \l_@@_split_path_a_final_tl \l_@@_split_path_a_tl
+ }
+ \tl_if_empty:NT \l_@@_split_path_b_final_tl
+ {
+ \tl_set_eq:NN \l_@@_split_path_b_final_tl \l_@@_split_path_b_tl
+ }
+
+ \spath_remove_empty_components:N \l_@@_split_path_a_final_tl
+ \spath_remove_empty_components:N \l_@@_split_path_b_final_tl
+
+ \tl_gclear:N \g_@@_output_tl
+ \@@_tl_gput_right_braced:NV \g_@@_output_tl \l_@@_split_path_a_final_tl
+ \@@_tl_gput_right_braced:NV \g_@@_output_tl \l_@@_split_path_b_final_tl
+ \group_end:
+}
+\cs_new_protected_nopar:Npn \spath_split_at_intersections:NNnn #1#2#3#4
+{
+ \@@_split_at_intersections: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_intersections:NNnn {NNVV, ccVV, 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_at_intersections: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_intersections:NNnn {NNVV, ccVV, 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}{
+% \spath_split_at_self_intersections:Nn,
+% \spath_split_at_self_intersections:N,
+% \spath_gsplit_at_self_intersections:Nn,
+% \spath_gsplit_at_self_intersections:N,
+% }
+% Given a path, split it at points where it self-intersects and store in the given macro.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \@@_split_at_self_intersections:n #1
+{
+ \group_begin:
+
+ % Copy the path
+ \tl_set:Nn \l_@@_split_path_b_tl {#1}
+
+ % Open the path
+ \spath_open:N \l_@@_split_path_b_tl
+ % Remove empty components
+ \spath_remove_empty_components:N \l_@@_split_path_b_tl
+ % Make a copy for later
+ \tl_set_eq:NN \l_@@_split_path_b_final_tl \l_@@_split_path_b_tl
+
+ % Clear some token lists and sequences
+ \tl_clear:N \l_@@_split_path_a_tl
+ \seq_clear:N \l_@@_split_first_seq
+ \int_zero:N \l_@@_split_segment_int
+
+ \pgfintersectionsortbyfirstpath
+
+ \bool_do_until:nn
+ {
+ \int_compare_p:n
+ {
+ \tl_count:N \l_@@_split_path_b_tl < 4
+ }
+ }
+ {
+ \tl_clear:N \l_@@_split_path_a_tl
+ \tl_put_right:Nx \l_@@_split_path_a_tl
+ {
+ \tl_item:Nn \l_@@_split_path_b_tl {1}
+ {\tl_item:Nn \l_@@_split_path_b_tl {2}}
+ {\tl_item:Nn \l_@@_split_path_b_tl {3}}
+ }
+
+ \tl_set:Nx \l_@@_tmpa_tl { \tl_item:Nn \l_@@_split_path_b_tl {4} }
+
+ \tl_set:Nx \l_@@_split_path_b_tl {\tl_tail:N \l_@@_split_path_b_tl}
+
+ \int_zero:N \l_@@_tmpa_int
+
+ \tl_case:Nn \l_@@_tmpa_tl
+ {
+ \c_spath_moveto_tl
+ {
+ \tl_clear:N \l_@@_split_path_a_tl
+ \tl_set:Nx \l_@@_split_path_b_tl {\tl_tail:N \l_@@_split_path_b_tl}
+ \tl_set:Nx \l_@@_split_path_b_tl {\tl_tail:N \l_@@_split_path_b_tl}
+ \tl_set:Nx \l_@@_split_path_b_tl {\tl_tail:N \l_@@_split_path_b_tl}
+ }
+ \c_spath_lineto_tl
+ {
+ \int_set:Nn \l_@@_tmpa_int {1}
+ }
+ \c_spath_curvetoa_tl
+ {
+ \int_set:Nn \l_@@_tmpa_int {3}
+ }
+ }
+
+ \prg_replicate:nn { \l_@@_tmpa_int }
+ {
+ \tl_set:Nx \l_@@_split_path_b_tl {\tl_tail:N \l_@@_split_path_b_tl}
+ \tl_set:Nx \l_@@_split_path_b_tl {\tl_tail:N \l_@@_split_path_b_tl}
+
+ \tl_put_right:Nx \l_@@_split_path_a_tl
+ {
+ \tl_item:Nn \l_@@_split_path_b_tl {1}
+ {\tl_item:Nn \l_@@_split_path_b_tl {2}}
+ {\tl_item:Nn \l_@@_split_path_b_tl {3}}
+ }
+
+ \tl_set:Nx \l_@@_split_path_b_tl {\tl_tail:N \l_@@_split_path_b_tl}
+ }
+
+ \tl_put_left:NV \l_@@_split_path_b_tl \c_spath_moveto_tl
+
+ \tl_if_empty:NF \l_@@_split_path_a_tl
+ {
+ % Intersect the current segment with the rest of the path
+ \spath_intersect:NN \l_@@_split_path_a_tl \l_@@_split_path_b_tl
+
+
+ % If we get intersections
+ \int_compare:nT {\pgfintersectionsolutions > 0}
+ {
+ % Find the times of the intersections on each path
+ \int_step_inline:nnnn {1} {1} {\pgfintersectionsolutions}
+ {
+ \pgfintersectiongetsolutiontimes{##1}{\l_@@_split_first_tl}{\l_@@_split_second_tl}
+ \fp_compare:nT
+ {
+ \l_@@_split_first_tl < .99
+ }
+ {
+ \tl_set:Nx \l_@@_tmpa_tl {\fp_to_decimal:n {\l_@@_split_first_tl + \l_@@_split_segment_int}}
+ \seq_put_right:NV \l_@@_split_first_seq \l_@@_tmpa_tl
+ }
+ }
+ }
+
+ \spath_intersect:NN \l_@@_split_path_b_tl \l_@@_split_path_a_tl
+
+ % If we get intersections
+ \int_compare:nT {\pgfintersectionsolutions > 0}
+ {
+ % Find the times of the intersections on each path
+ \int_step_inline:nnnn {1} {1} {\pgfintersectionsolutions}
+ {
+ \pgfintersectiongetsolutiontimes{##1}{\l_@@_split_first_tl}{\l_@@_split_second_tl}
+ \fp_compare:nT
+ {
+ \l_@@_split_first_tl > .01
+ }
+ {
+ \tl_set:Nx \l_@@_tmpa_tl {\fp_to_decimal:n {\l_@@_split_first_tl + \l_@@_split_segment_int + 1}}
+ \seq_put_right:NV \l_@@_split_first_seq \l_@@_tmpa_tl
+ }
+ }
+ }
+
+ }
+ % Increment the segment counter
+ \int_incr:N \l_@@_split_segment_int
+ }
+
+ % Sort the sequence by reverse order along the path
+ \seq_sort:Nn \l_@@_split_first_seq
+ {
+ \fp_compare:nNnTF { ##1 } < { ##2 }
+ { \sort_return_swapped: }
+ { \sort_return_same: }
+ }
+
+ % Restore the original copy of the path
+ \tl_set_eq:NN \l_@@_split_path_b_tl \l_@@_split_path_b_final_tl
+
+ % Clear the token lists
+ \tl_clear:N \l_@@_split_path_b_start_tl
+ \tl_clear:N \l_@@_split_path_b_end_tl
+ \tl_clear:N \l_@@_split_path_b_final_tl
+
+ \tl_set:Nn \l_@@_split_prev_first_tl {-1}
+
+ \seq_map_inline:Nn \l_@@_split_first_seq
+ {
+ \tl_set:Nn \l_@@_split_first_tl {##1}
+ \tl_set_eq:NN \l_@@_tmpa_tl \l_@@_split_first_tl
+ \int_compare:nT
+ {
+ \fp_to_int:n {floor( \l_@@_split_first_tl ) }
+ =
+ \fp_to_int:n {floor( \l_@@_split_prev_first_tl) }
+ }
+ {
+ \tl_set:Nx \l_@@_split_first_tl
+ {
+ \fp_eval:n {
+ floor( \l_@@_split_first_tl )
+ +
+ ( \l_@@_split_first_tl - floor( \l_@@_split_first_tl) )
+ /
+ ( \l_@@_split_prev_first_tl - floor( \l_@@_split_prev_first_tl) )
+ }
+ }
+ }
+ \tl_set_eq:NN \l_@@_split_prev_first_tl \l_@@_tmpa_tl
+
+ \spath_split_at:NNVV \l_@@_split_path_b_start_tl \l_@@_split_path_b_end_tl \l_@@_split_path_b_tl \l_@@_split_first_tl
+
+ \tl_put_left:NV \l_@@_split_path_b_final_tl \l_@@_split_path_b_end_tl
+ \tl_set_eq:NN \l_@@_split_path_b_tl \l_@@_split_path_b_start_tl
+
+ }
+
+ \tl_put_left:NV \l_@@_split_path_b_final_tl \l_@@_split_path_b_tl
+
+ \tl_if_empty:NT \l_@@_split_path_b_final_tl
+ {
+ \tl_set_eq:NN \l_@@_split_path_b_final_tl \l_@@_split_path_b_tl
+ }
+
+ \spath_remove_empty_components:N \l_@@_split_path_b_final_tl
+
+ \tl_gclear:N \g_@@_output_tl
+ \tl_gset_eq:NN \g_@@_output_tl \l_@@_split_path_b_final_tl
+ \group_end:
+}
+\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}
+\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}
+\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}{
+% \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}
+\tl_new:N \l_@@_tmpj_tl
+\cs_new_protected_nopar:Npn \@@_join_component:nn #1#2
+{
+ \group_begin:
+ \spath_components_to_seq:Nn \l_@@_tmpa_seq {#1}
+ \int_compare:nT
+ {
+ #2 == 1
+ }
+ {
+ \tl_clear:N \l_@@_tmpj_tl
+ \seq_pop_left:NN \l_@@_tmpa_seq \l_@@_tmpj_tl
+
+ \prg_replicate:nn {3}
+ {
+ \tl_set:Nx \l_@@_tmpj_tl {\tl_tail:N \l_@@_tmpj_tl}
+ }
+
+ \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpj_tl
+ }
+ \bool_if:nT
+ {
+ \int_compare_p:n
+ {
+ #2 > 1
+ }
+ &&
+ \int_compare_p:n
+ {
+ #2 <= \seq_count:N \l_@@_tmpa_seq
+ }
+ }
+ {
+
+ \seq_clear:N \l_@@_tmpb_seq
+ \seq_map_indexed_inline:Nn \l_@@_tmpa_seq
+ {
+ \tl_set:Nn \l_@@_tmpj_tl {##2}
+ \int_compare:nT {##1 = #2}
+ {
+ \prg_replicate:nn {3}
+ {
+ \tl_set:Nx \l_@@_tmpj_tl {\tl_tail:N \l_@@_tmpj_tl}
+ }
+ }
+ \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpj_tl
+ }
+
+ \seq_set_eq:NN \l_@@_tmpa_seq \l_@@_tmpb_seq
+ }
+
+ \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpa_seq {} }
+ \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}{
+% \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}{
+% \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}{
+% \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
+{
+ \spath_convert_path_to_svg:Nn \l_@@_iterp_tl {#2}
+ \iow_open:Nn \g_@@_stream {#1 .svg}
+ \iow_now:Nx \g_@@_stream
+ {
+ \tl_use:N \l_@@_iterp_tl
+ }
+ \iow_close:N \g_@@_stream
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\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 \l_@@_tmpa_tl {##1}
+ {\tl_item:Nn \l_@@_tmpa_tl {##1+1}}
+ {\tl_item:Nn \l_@@_tmpa_tl {##1+2}}
+ }
+ }
+}
+\cs_generate_variant:Nn \spath_show:n {V, v}
+% \end{macrocode}
+% \end{macro}
+
+% \subsubsection{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}{
+% \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}{\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}{
+% \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}{
+% \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}{\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}{\spath_set_tikz_coords:n}
+% Sets the \Verb+\tikz@lastx+ and other coordinates from the soft path.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \spath_set_tikz_coords:n #1
+{
+ \spath_finalpoint:Nn \l_@@_tmpa_tl {#1}
+ \tl_set:Nx \l_@@_tmpa_tl
+ {
+ \exp_not:c {tikz@lastx}=\tl_item:Nn \l_@@_tmpa_tl {1}
+ \exp_not:c {tikz@lasty}=\tl_item:Nn \l_@@_tmpa_tl {2}
+ \exp_not:c {tikz@lastxsaved}=\tl_item:Nn \l_@@_tmpa_tl {1}
+ \exp_not:c {tikz@lastysaved}=\tl_item:Nn \l_@@_tmpa_tl {2}
+ }
+ \tl_use:N \l_@@_tmpa_tl
+}
+\cs_generate_variant:Nn \spath_set_tikz_coords: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}
+%
+% 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}
+%
+% 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
+ }
+ },
+ clone/.code~ 2~ args={
+ \tl_if_exist:cT {\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}
+ }
+ },
+ clone~ global/.code~ 2~ args={
+ \tl_if_exist:cT {\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}
+ }
+ },
+ 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
+ }
+ },
+% \end{macrocode}
+%
+% Saves a soft path to the aux file.
+%
+% \begin{macrocode}
+ save~ to~ aux/.code={
+ \tl_if_exist:cT {\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}
+ }
+ },
+% \end{macrocode}
+%
+% Restores a soft path to the current path.
+%
+% \begin{macrocode}
+ restore/.code={
+ \tl_if_exist:cT {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
+ {
+ \spath_set_current_path:c
+ {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
+ \spath_set_tikz_coords:v
+ {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
+ }
+ },
+% \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:cT {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
+ {
+ \iow_term:n {---~ soft~ path~ #1~ ---}
+ \spath_show:v {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
+ }
+ },
+% \end{macrocode}
+%
+% Appends the named path to the current path with a weld.
+%
+% \begin{macrocode}
+ append/.code={
+ \tl_if_exist:cT {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
+ {
+ \spath_get_current_path:N \l_@@_current_tl
+ \spath_weld:Nv
+ \l_@@_current_tl
+ {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
+ \spath_set_current_path:N \l_@@_current_tl
+ \spath_set_tikz_coords:V \l_@@_current_tl
+ }
+ },
+% \end{macrocode}
+%
+% Joins the second named path to the first.
+%
+% \begin{macrocode}
+ join~ with/.code~ 2~ args={
+ \tl_if_exist:cT {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
+ {
+ \tl_if_exist:cT {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl}
+ {
+ \spath_append: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}
+ }
+ }
+ },
+% \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:cT {\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}
+ }
+ },
+% \end{macrocode}
+%
+% Reverses the named path.
+%
+% \begin{macrocode}
+ reverse/.code={
+ \tl_if_exist:cT {\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}
+ }
+ },
+% \end{macrocode}
+%
+% Appends the reversal of the path to the current path.
+%
+% \begin{macrocode}
+ append~ reverse/.code={
+ \tl_if_exist:cT {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
+ {
+ \spath_reverse:Nv
+ \l_@@_reverse_tl
+ {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
+ \spath_get_current_path:N \l_@@_current_tl
+ \spath_weld:NV \l_@@_current_tl \l_@@_reverse_tl
+ \spath_set_current_path:N \l_@@_current_tl
+ \spath_set_tikz_coords:V \l_@@_current_tl
+ }
+ },
+% \end{macrocode}
+%
+% Inserts the named path into the current path as-is, meaning without transforming or welding it.
+%
+% \begin{macrocode}
+ insert/.code={
+ \tl_if_exist:cT {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
+ {
+ \spath_get_current_path:N \l_@@_current_tl
+ \spath_append:Nv
+ \l_@@_current_tl
+ {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
+ \spath_set_current_path:N \l_@@_current_tl
+ \spath_set_tikz_coords:V \l_@@_current_tl
+ }
+ },
+% \end{macrocode}
+%
+% Inserts the reverse of the named path.
+%
+% \begin{macrocode}
+ insert~ reverse/.code={
+ \tl_if_exist:cT {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
+ {
+ \spath_reverse:Nv
+ \l_@@_reverse_tl
+ {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
+ \spath_get_current_path:N \l_@@_current_tl
+ \spath_append:NV \l_@@_current_tl \l_@@_reverse_tl
+ \spath_set_current_path:N \l_@@_current_tl
+ \spath_set_tikz_coords:V \l_@@_current_tl
+ }
+ },
+% \end{macrocode}
+%
+% These keys shorten the path.
+%
+% \begin{macrocode}
+ shorten~ at~ end/.code~ 2~ args={
+ \tl_if_exist:cT {\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}
+ }
+ },
+ shorten~ at~ start/.code~ 2~ args ={
+ \tl_if_exist:cT {\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}
+ }
+ },
+ shorten~ at~ both~ ends/.code~ 2~ args={
+ \tl_if_exist:cT {\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}
+ }
+ },
+% \end{macrocode}
+%
+% This translates the named path.
+%
+% \begin{macrocode}
+ translate/.code~ n~ args={3}{
+ \tl_if_exist:cT {\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}
+ }
+ },
+% \end{macrocode}
+%
+% Exports the path as an SVG file.
+%
+% \begin{macrocode}
+ export~ to~ svg/.code={
+ \tl_if_exist:cT {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
+ {
+ \spath_export_to_svg:c
+ {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
+ }
+ },
+% \end{macrocode}
+%
+% Transforms the named path using TikZ transformation specifications.
+%
+% \begin{macrocode}
+ transform/.code~ 2~ args={
+ \tl_if_exist:cT {\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
+ }
+ },
+ transform~global/.code~ 2~ args={
+ \tl_if_exist:cT {\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
+ }
+ },
+% \end{macrocode}
+%
+% Splits two paths at their mutual intersections.
+%
+% \begin{macrocode}
+ split~ at~ intersections/.code~ n~ args={2}{
+ \tl_if_exist:cT {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
+ {
+ \tl_if_exist:cT {\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}
+ }
+ }
+ },
+% \end{macrocode}
+%
+% Splits a path at its self-intersections.
+%
+% \begin{macrocode}
+ split~ at~ self~ intersections/.code={
+ \tl_if_exist:cT {\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}
+ }
+ },
+% \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:cT {\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
+ }
+ }
+ },
+% \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:cT {\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:
+ }
+ },
+% \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~ n~ args={3}{
+ \tl_if_exist:cT {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
+ {
+ \group_begin:
+ \seq_gclear:N \g_@@_tmpa_seq
+ \seq_gclear:N \g_@@_tmpb_seq
+ \foreach \l_@@_tmpa_tl in {#3}
+ {
+ \seq_gput_right:NV \g_@@_tmpa_seq \l_@@_tmpa_tl
+ \seq_gput_right:Nx \g_@@_tmpb_seq {\int_eval:n { \l_@@_tmpa_tl + 1 }}
+ }
+ \spath_components_to_seq:Nv
+ \l_@@_tmpa_seq
+ {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}
+ \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 {#2/2}
+ }
+ \seq_if_in:NnT \g_@@_tmpb_seq {##1}
+ {
+ \spath_shorten_at_start:Nn \l_@@_tmpa_tl {#2/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
+ }
+ },
+% \end{macrocode}
+%
+% Join the specified components together, joining each to its previous one.
+%
+% \begin{macrocode}
+ join~ components/.code~ 2~ args={
+ \tl_if_exist:cT {\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}
+ }
+ }
+ },
+% \end{macrocode}
+%
+% Remove all components of the path that don't actually draw anything.
+%
+% \begin{macrocode}
+ remove~ empty~ components/.code={
+ \tl_if_exist:cT {\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}
+ }
+ },
+% \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:cT {\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}
+ }
+ }
+ },
+% \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
+ }
+ {
+ \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
+ }
+ {
+ \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/.cd,
+ split~ at~ self~ intersections=#1,
+ insert~ gaps~ after~ components={#1}{#2}{#3},
+ maybe~ spot~ weld=#1,
+ 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
+ }
+ }
+ {
+ \tl_gset_eq:NN \g_@@_smuggle_tl \pgfpointorigin
+ }
+ \group_end:
+ \tl_use:N \g_@@_smuggle_tl
+}
+
+\ExplSyntaxOff
+% \end{macrocode}
+% \iffalse
+%</tikzspath3>
+% \fi
+%
+% \iffalse
+%<*calligraphy>
+% \fi
+%
+% \section{The Calligraphy Package}
+%
+% \begin{macrocode}
+%<@@=cal>
+% \end{macrocode}
+%
+% \subsection{Initialisation}
+% \begin{macrocode}
+\RequirePackage{spath3}
+\ExplSyntaxOn
+
+\tl_new:N \l_@@_tmpa_tl
+\tl_new:N \l_@@_tmpb_tl
+\tl_new:N \l_@@_tmp_path_tl
+\tl_new:N \l_@@_tmp_rpath_tl
+\tl_new:N \l_@@_tmp_rpathb_tl
+\tl_new:N \l_@@_tmp_patha_tl
+
+\seq_new:N \l_@@_tmpa_seq
+
+\int_new:N \l_@@_tmpa_int
+\int_new:N \l_@@_tmpb_int
+\int_new:N \g_@@_path_component_int
+\int_new:N \g_@@_label_int
+
+\fp_new:N \l_@@_tmpa_fp
+\fp_new:N \l_@@_tmpb_fp
+\fp_new:N \l_@@_tmpc_fp
+\fp_new:N \l_@@_tmpd_fp
+\fp_new:N \l_@@_tmpe_fp
+
+\dim_new:N \l_@@_tmpa_dim
+\dim_new:N \l_@@_tmpb_dim
+\dim_new:N \l_@@_tmpc_dim
+\dim_new:N \l_@@_tmpd_dim
+\dim_new:N \l_@@_tmpe_dim
+\dim_new:N \l_@@_tmpf_dim
+\dim_new:N \l_@@_tmpg_dim
+\dim_new:N \l_@@_tmph_dim
+
+\bool_new:N \l_@@_annotate_bool
+\bool_new:N \l_@@_taper_start_bool
+\bool_new:N \l_@@_taper_end_bool
+\bool_new:N \l_@@_taperable_bool
+
+\dim_new:N \l_@@_taper_width_dim
+\dim_new:N \l_@@_line_width_dim
+
+\bool_set_true:N \l_@@_taper_start_bool
+\bool_set_true:N \l_@@_taper_end_bool
+
+\cs_generate_variant:Nn \tl_put_right:Nn {Nv}
+
+\msg_new:nnn { calligraphy } { undefined pen } { The~ pen~ "#1"~ is~ not~ defined. }
+% \end{macrocode}
+%
+% \subsection{TikZ Keys}
+%
+% The public interface to this package is through TikZ keys and styles.
+% \begin{macrocode}
+\tikzset{
+ define~pen/.code={
+ \tikzset{pen~name=#1}
+ \pgf@relevantforpicturesizefalse
+ \tikz@addmode{
+ \pgfsyssoftpath@getcurrentpath\l_@@_tmpa_tl
+ \spath_components_to_seq:NV \l_@@_tmpa_seq \l_@@_tmpa_tl
+ \seq_gclear_new:c {g_@@_pen_\pgfkeysvalueof{/tikz/pen~name}_seq}
+ \seq_gset_eq:cN {g_@@_pen_\pgfkeysvalueof{/tikz/pen~name}_seq} \l_@@_tmpa_seq
+ \pgfusepath{discard}%
+ }
+ },
+ define~pen/.default={default},
+ use~pen/.code={
+ \tikzset{pen~name=#1}
+ \int_gzero:N \g_@@_path_component_int
+ \cs_set_eq:NN \pgfpathmoveto \cal_moveto:n
+ \tikz@addmode{
+ \pgfsyssoftpath@getcurrentpath\l_@@_tmpa_tl
+ \spath_components_to_seq:NV \l_@@_tmpa_seq \l_@@_tmpa_tl
+ \tl_if_exist:cTF {g_@@_pen_\pgfkeysvalueof{/tikz/pen~name}_seq}
+ {
+ \cal_path_create:Nc \l_@@_tmpa_seq {g_@@_pen_\pgfkeysvalueof{/tikz/pen~name}_seq}
+ }
+ {
+ \msg_warning:nnx { calligraphy } { undefined pen } { \pgfkeysvalueof{/tikz/pen~name} }
+ }
+ }
+ },
+ use~pen/.default={default},
+ pen~name/.initial={default},
+ copperplate/.style={pen~name=copperplate},
+ pen~colour/.initial={black},
+ weight/.is~choice,
+ weight/heavy/.style={
+ line~width=\pgfkeysvalueof{/tikz/heavy~line~width},
+ taper~width=\pgfkeysvalueof{/tikz/light~line~width},
+ },
+ weight/light/.style={
+ line~width=\pgfkeysvalueof{/tikz/light~line~width},
+ taper~width=0pt,
+ },
+ heavy/.style={
+ weight=heavy
+ },
+ light/.style={
+ weight=light
+ },
+ heavy~line~width/.initial=2pt,
+ light~line~width/.initial=1pt,
+ taper/.is~choice,
+ taper/.default=both,
+ taper/none/.style={
+ taper~start=false,
+ taper~end=false,
+ },
+ taper/both/.style={
+ taper~start=true,
+ taper~end=true,
+ },
+ taper/start/.style={
+ taper~start=true,
+ taper~end=false,
+ },
+ taper/end/.style={
+ taper~start=false,
+ taper~end=true,
+ },
+ taper~start/.code={
+ \tl_if_eq:nnTF {#1} {true}
+ {
+ \bool_set_true:N \l_@@_taper_start_bool
+ }
+ {
+ \bool_set_false:N \l_@@_taper_start_bool
+ }
+ },
+ taper~start/.default={true},
+ taper~end/.code={
+ \tl_if_eq:nnTF {#1} {true}
+ {
+ \bool_set_true:N \l_@@_taper_end_bool
+ }
+ {
+ \bool_set_false:N \l_@@_taper_end_bool
+ }
+ },
+ taper~end/.default={true},
+ taper~width/.code={\dim_set:Nn \l_@@_taper_width_dim {#1}},
+ nib~style/.code~2~args={
+ \tl_clear_new:c {l_@@_nib_style_#1}
+ \tl_set:cn {l_@@_nib_style_#1} {#2}
+ },
+ stroke~style/.code~2~args={
+ \tl_clear_new:c {l_@@_stroke_style_#1}
+ \tl_set:cn {l_@@_stroke_style_#1} {#2}
+ },
+ this~stroke~style/.code={
+ \tl_clear_new:c {l_@@_stroke_inline_style_ \int_use:N \g_@@_path_component_int}
+ \tl_set:cn {l_@@_stroke_inline_style_ \int_use:N \g_@@_path_component_int} {#1}
+ },
+ annotate/.style={
+ annotate~if,
+ annotate~reset,
+ annotation~style/.update~value={#1},
+ },
+ annotate~if/.default={true},
+ annotate~if/.code={
+ \tl_if_eq:nnTF {#1} {true}
+ {
+ \bool_set_true:N \l_@@_annotate_bool
+ }
+ {
+ \bool_set_false:N \l_@@_annotate_bool
+ }
+ },
+ annotate~reset/.code={
+ \int_gzero:N \g_@@_label_int
+ },
+ annotation~style/.initial={draw,->},
+ annotation~shift/.initial={(0,1ex)},
+ every~annotation~node/.initial={anchor=south~west},
+ annotation~node~style/.code~2~args={
+ \tl_clear_new:c {l_@@_annotation_style_ #1 _tl}
+ \tl_set:cn {l_@@_annotation_style_ #1 _tl}{#2}
+ },
+ tl~use:N/.code={
+ \exp_args:NV \pgfkeysalso #1
+ },
+ tl~use:c/.code={
+ \tl_if_exist:cT {#1}
+ {
+ \exp_args:Nv \pgfkeysalso {#1}
+ }
+ },
+ /handlers/.update~style/.code={
+ \tl_if_eq:nnF {#1} {\pgfkeysnovalue}
+ {
+ \pgfkeys{\pgfkeyscurrentpath/.code=\pgfkeysalso{#1}}
+ }
+ },
+ /handlers/.update~value/.code={
+ \tl_if_eq:nnF {#1} {\pgfkeysnovalue}
+ {
+ \pgfkeyssetvalue{\pgfkeyscurrentpath}{#1}
+ }
+ },
+}
+% \end{macrocode}
+%
+% Some wrappers around the TikZ keys.
+% \begin{macrocode}
+\NewDocumentCommand \pen { O{} }
+{
+ \path[define~ pen,every~ calligraphy~ pen/.try,#1]
+}
+
+\NewDocumentCommand \definepen { O{} }
+{
+ \tikz \path[define~ pen,every~ calligraphy~ pen/.try,#1]
+}
+
+\NewDocumentCommand \calligraphy { O{} }
+{
+ \path[use~ pen,every~ calligraphy/.try,#1]
+}
+% \end{macrocode}
+%
+% \subsection{The Path Creation}
+%
+% \begin{macro}[internal]{\cal_path_create:NN}
+% This is the main command for creating the calligraphic paths.
+% First argument is the given path
+% Second argument is the pen path
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \cal_path_create:NN #1#2
+{
+ \int_zero:N \l_@@_tmpa_int
+ \seq_map_inline:Nn #1
+ {
+ \int_compare:nT {\tl_count:n {##1} > 3}
+ {
+
+ \int_incr:N \l_@@_tmpa_int
+ \int_zero:N \l_@@_tmpb_int
+
+ \tl_set:Nn \l_@@_tmp_path_tl {##1}
+ \spath_open:N \l_@@_tmp_path_tl
+ \spath_reverse:NV \l_@@_tmp_rpath_tl \l_@@_tmp_path_tl
+
+ \seq_map_inline:Nn #2
+ {
+ \int_incr:N \l_@@_tmpb_int
+ \group_begin:
+ \pgfsys@beginscope
+ \cal_apply_style:c {l_@@_stroke_style_ \int_use:N \l_@@_tmpa_int}
+ \cal_apply_style:c {l_@@_stroke_inline_style_ \int_use:N \l_@@_tmpa_int}
+ \cal_apply_style:c {l_@@_nib_style_ \int_use:N \l_@@_tmpb_int}
+
+ \spath_initialpoint:Nn \l_@@_tmpa_tl {####1}
+ \tl_set_eq:NN \l_@@_tmp_patha_tl \l_@@_tmp_path_tl
+ \spath_translate:NV \l_@@_tmp_patha_tl \l_@@_tmpa_tl
+
+ \int_compare:nTF {\tl_count:n {####1} == 3}
+ {
+ \cal_at_least_three:N \l_@@_tmp_patha_tl
+ \spath_protocol_path:V \l_@@_tmp_patha_tl
+
+ \tikz@options
+ \dim_set:Nn \l_@@_line_width_dim {\pgflinewidth}
+ \cal_maybe_taper:N \l_@@_tmp_patha_tl
+ }
+ {
+ \spath_weld:Nn \l_@@_tmp_patha_tl {####1}
+ \spath_weld:NV \l_@@_tmp_patha_tl \l_@@_tmp_rpath_tl
+ \spath_reverse:Nn \l_@@_tmp_rpathb_tl {####1}
+ \spath_weld:NV \l_@@_tmp_patha_tl \l_@@_tmp_rpathb_tl
+
+ \tl_clear:N \l_@@_tmpa_tl
+ \tl_set:Nn \l_@@_tmpa_tl {fill=\pgfkeysvalueof{/tikz/pen~colour},draw=none}
+ \tl_if_exist:cT {l_@@_stroke_style_ \int_use:N \l_@@_tmpa_int}
+ {
+ \tl_put_right:Nv \l_@@_tmpa_tl {l_@@_stroke_style_ \int_use:N \l_@@_tmpa_int}
+ }
+ \tl_if_exist:cT {l_@@_stroke_inline_style_ \int_use:N \l_@@_tmpa_int}
+ {
+ \tl_put_right:Nn \l_@@_tmpa_tl {,}
+ \tl_put_right:Nv \l_@@_tmpa_tl {l_@@_stroke_inline_style_ \int_use:N \l_@@_tmpa_int}
+ }
+ \tl_if_exist:cT {l_@@_nib_style_ \int_use:N \l_@@_tmpb_int}
+ {
+ \tl_put_right:Nn \l_@@_tmpa_tl {,}
+ \tl_put_right:Nv \l_@@_tmpa_tl {l_@@_nib_style_ \int_use:N \l_@@_tmpb_int}
+ }
+ \spath_tikz_path:VV \l_@@_tmpa_tl \l_@@_tmp_patha_tl
+
+ }
+ \pgfsys@endscope
+ \group_end:
+ }
+
+ \bool_if:NT \l_@@_annotate_bool
+ {
+ \seq_get_right:NN #2 \l_@@_tmpa_tl
+ \spath_finalpoint:NV \l_@@_tmpa_tl \l_@@_tmpa_tl
+ \spath_translate:NV \l_@@_tmp_path_tl \l_@@_tmpa_tl
+ \tikz@scan@one@point\pgfutil@firstofone\pgfkeysvalueof{/tikz/annotation~shift}
+
+ \spath_translate:Nnn \l_@@_tmp_path_tl {\pgf@x} {\pgf@y}
+
+ \pgfkeysgetvalue{/tikz/annotation~style}{\l_@@_tmpa_tl}
+ \spath_tikz_path:VV \l_@@_tmpa_tl \l_@@_tmp_path_tl
+
+ \spath_finalpoint:NV \l_@@_tmpa_tl \l_@@_tmp_path_tl
+
+ \exp_last_unbraced:NV \pgfqpoint \l_@@_tmpa_tl
+ \begin{scope}[reset~ cm]
+ \node[every~annotation~node/.try,tl~use:c = {l_@@_annotation_style_ \int_use:N \l_@@_tmpa_int _tl}] at (\pgf@x,\pgf@y) {\int_use:N \l_@@_tmpa_int};
+ \end{scope}
+ }
+ }
+ }
+}
+\cs_generate_variant:Nn \cal_path_create:NN {Nc}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\cal_moveto:n}
+% When creating the path, we need to keep track of the number of components so that we can apply styles accordingly.
+% \begin{macrocode}
+\cs_new_eq:NN \cal_orig_moveto:n \pgfpathmoveto
+\cs_new_nopar:Npn \cal_moveto:n #1
+{
+ \int_gincr:N \g_@@_path_component_int
+ \cal_orig_moveto:n {#1}
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\cal_apply_style:N}
+% Interface for applying \Verb+\tikzset+ to a token list.
+% \begin{macrocode}
+\cs_new_nopar:Npn \cal_apply_style:N #1
+{
+ \tl_if_exist:NT #1 {
+ \exp_args:NV \tikzset #1
+ }
+}
+\cs_generate_variant:Nn \cal_apply_style:N {c}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\cal_at_least_three:Nn}
+% A tapered path has to have at least three components.
+% This figures out if it is necessary and sets up the splitting.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \cal_at_least_three:Nn #1#2
+{
+ \spath_reallength:Nn \l_@@_tmpa_int {#2}
+ \tl_clear:N \l_@@_tmpb_tl
+ \tl_set:Nn \l_@@_tmpb_tl {#2}
+ \int_compare:nTF {\l_@@_tmpa_int = 1}
+ {
+ \spath_split_at:Nn \l_@@_tmpb_tl {2/3}
+ \spath_split_at:Nn \l_@@_tmpb_tl {1/2}
+ }
+ {
+ \int_compare:nT {\l_@@_tmpa_int = 2}
+ {
+ \spath_split_at:Nn \l_@@_tmpb_tl {1.5}
+ \spath_split_at:Nn \l_@@_tmpb_tl {.5}
+ }
+ }
+ \tl_set_eq:NN #1 \l_@@_tmpb_tl
+}
+\cs_generate_variant:Nn \cal_at_least_three:Nn {NV}
+\cs_new_protected_nopar:Npn \cal_at_least_three:N #1
+{
+ \cal_at_least_three:NV #1#1
+}
+\cs_generate_variant:Nn \cal_at_least_three:N {c}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\cal_maybe_taper:N}
+% Possibly tapers the path, depending on the booleans.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \cal_maybe_taper:N #1
+{
+ \tl_set_eq:NN \l_@@_tmpa_tl #1
+
+ \bool_if:NT \l_@@_taper_start_bool
+ {
+
+ \dim_set:Nn \l_@@_tmpa_dim {\tl_item:Nn \l_@@_tmpa_tl {2}}
+ \dim_set:Nn \l_@@_tmpb_dim {\tl_item:Nn \l_@@_tmpa_tl {3}}
+ \tl_set:Nx \l_@@_tmpb_tl {\tl_item:Nn \l_@@_tmpa_tl {4}}
+
+ \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}{\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_@@_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}
+% 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_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\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:VnT \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:VnT \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:VnT \l_@@_tmpa_tl {initial point}
+ {
+ \bool_set_true:N \l_@@_skip_bool
+ }
+ \knot_test_endpoint:VnT \l_@@_tmpa_tl {final point}
+ {
+ \bool_set_true:N \l_@@_skip_bool
+ }
+ \knot_test_endpoint:VnT \l_@@_tmpb_tl {initial point}
+ {
+ \bool_set_true:N \l_@@_skip_bool
+ }
+ \knot_test_endpoint:VnT \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:VnT \l_@@_tmpg_tl {initial point}
+ {
+ \bool_set_true:N \l_@@_prepend_prev_bool
+ }
+ {
+ \bool_set_false:N \l_@@_prepend_prev_bool
+ }
+ \knot_test_endpoint:VnT \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:vnT {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:vnT {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:N #1 {p,T,F,TF}
+{
+ \dim_compare:nTF
+ {
+ \dim_abs:n { \l_@@_tmpa_dim - \tl_item:Nn #1 {1}}
+ +
+ \dim_abs:n { \l_@@_tmpb_dim - \tl_item:Nn #1 {2}}
+ <
+ \l_@@_tolerance_dim
+ }
+ {
+ \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:nn #1#2 {T,F,TF}
+{
+ \use:c {spath_#2:Nv} \l_@@_tmpd_tl {knot #1}
+ \knot_test_endpoint:NTF \l_@@_tmpd_tl
+ {
+ \prg_return_true:
+ }
+ {
+ \prg_return_false:
+ }
+}
+
+\cs_generate_variant:Nn \knot_test_endpoint:nnT {VnT,vnT}
+\cs_generate_variant:Nn \knot_test_endpoint:nnF {VnF,vnF}
+\cs_generate_variant:Nn \knot_test_endpoint:nnTF {VnTF,vnTF}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[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