% \iffalse meta-comment %<*internal> \iffalse % %<*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. % %<*internal> \fi \def\nameofplainTeX{plain} \ifx\fmtname\nameofplainTeX\else \expandafter\begingroup \fi % %<*install> \input l3docstrip.tex \keepsilent \askforoverwritefalse \preamble ---------------------------------------------------------------- spath3 --- Functions for manipulating PGF soft paths E-mail: loopspace@mathforge.org Released under the LaTeX Project Public License v1.3c or later See http://www.latex-project.org/lppl.txt ---------------------------------------------------------------- \endpreamble \postamble Copyright (C) 2011-2021 by Andrew Stacey This work may be distributed and/or modified under the conditions of the LaTeX Project Public License (LPPL), either version 1.3c of this license or (at your option) any later version. The latest version of this license is in the file: http://www.latex-project.org/lppl.txt This work is "maintained" (as per LPPL maintenance status) by Andrew Stacey. This work consists of the files spath3_code.dtx calligraphy_doc.tex knots_doc.tex spath3.tex and the derived files spath3.ins, spath3_code.pdf, spath3.sty, tikzlibrarycalligraphy.code.tex tikzlibraryknots.code.tex tikzlibraryspath3.code.tex calligraphy.pdf knots.pdf spath3.pdf README \endpostamble \usedir{tex/latex/spath3} \generate{ \file{spath3.sty}{\from{\jobname.dtx}{spath3}} } \generate{ \file{tikzlibrarycalligraphy.code.tex}{\from{\jobname.dtx}{calligraphy}} } \generate{ \file{tikzlibraryknots.code.tex}{\from{\jobname.dtx}{knots}} } \generate{ \file{tikzlibraryspath3.code.tex}{\from{\jobname.dtx}{tikzspath3}} } % %\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 % %<*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} % % \fi % % \CheckSum{8404} % % \CharacterTable % {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z % Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z % Digits \0\1\2\3\4\5\6\7\8\9 % Exclamation \! Double quote \" Hash (number) \# % Dollar \$ Percent \% Ampersand \& % Acute accent \' Left paren \( Right paren \) % Asterisk \* Plus \+ Comma \, % Minus \- Point \. Solidus \/ % Colon \: Semicolon \; Less than \< % Equals \= Greater than \> Question mark \? % Commercial at \@ Left bracket \[ Backslash \\ % Right bracket \] Circumflex \^ Underscore \_ % Grave accent \` Left brace \{ Vertical bar \| % Right brace \} Tilde \~} % % % \changes{1.0}{2011/05/03}{Converted to DTX file} % \changes{1.1}{2016/02/19}{Fixes due to changes in LaTeX3} % \changes{1.2}{2019/02/12}{More fixes due to changes in LaTeX3} % \changes{1.4}{2020/12/18}{A fair amount of code reimplementation and reorganisation, together with defining TikZ keys to make functions available for use.} % \changes{2.0}{2021/01/19}{Refactored the code to remove the OO approach and make it functional, introduced the spath3 TikZ library to provide a user interface.} % \changes{2.2}{2021/02/05}{Bugfixes and improvements, mainly with regard to the intersection and splitting routines.} % % \DoNotIndex{\newcommand,\newenvironment} % % \pdfstringdefDisableCommands{% % \def\\{}% % \def\url#1{<#1>}% % } % % \providecommand*{\url}{\texttt} % \GetFileInfo{spath3.sty} % \title{The \textsf{spath3} package: code} % \author{Andrew Stacey \\ \url{loopspace@mathforge.org}} % \date{\fileversion~from \filedate} % % % \maketitle % % % \section{Introduction} % % The \Verb+spath3+ package is intended as a library for manipulating PGF's \emph{soft paths}. % In between defining a path and using it, PGF stores a path as a \emph{soft path} where all the defining structure has been resolved into the basic operations but these have not yet been written to the output file. % They can therefore still be manipulated by \TeX, and as they have a very rigid form (and limited vocabulary), they are relatively easy to modify. % This package provides some methods for working with these paths. % It was originally not really intended for use by end users but as a foundation on which other packages can be built. % However, over the years I've found myself using it at ever higher levels and so a set of interfaces has been designed using TikZ keys. % % It also provides the engine that drives a few other packages, such as the \Verb+calligraphy+, \Verb+knot+, and \Verb+penrose+ packages. % The first two of these are subpackages of this one. % The \Verb+calligraphy+ package simulates a calligraphic pen stroking a path. % The \Verb+knots+ package can be used to draw knot (and similar) diagrams. % % For usage, see the documentation of the following packages (\Verb+texdoc +): % % \begin{itemize} % \item \Verb+calligraphy+ % \item \Verb+knots+ % \item \Verb+penrose+ % \item \Verb+spath3+ (\emph{this} document is the code, there's another which focusses on usage) % \end{itemize} % % \section{Technical Details} % % The format of a soft path is a sequence of triples of the form \Verb+\macro {dimension}{dimension}+. % The macro is one of a short list, the dimensions are coordinates in points. % There are certain further restrictions, particularly that every path must begin with a \Verb+move to+, and B\'ezier curves consist of three triples. % % In the original implementation, I wrapped this token list in a \Verb+prop+ to store useful information along with the path. % Over time, this additional structure has proved a little unwieldy and I've pared it back to working primarily with the original soft path as a token list. % % A frequent use of this package is to break a path into pieces and do something with each of those pieces. % To that end, there are various words that I use to describe the levels of the structure of a path. % % At the top level is the path itself. % At the bottom level are the triples of the form \Verb+\macro{dim}{dim}+, as described above. % In between these are the \emph{segments} and \emph{components}. % % A \emph{segment} is a minimal drawing piece. % Thus it might be a straight line or a B\'ezier curve. % When a path is broken into segments then each segment is a complete path so it isn't simply a selection of triples from the original path. % % A \emph{component} is a minimal connected section of the path. % So every component starts with a move command and continues until the next move command. % For ease of implementation (and to enable a copperplate pen in the calligraphy package!), an isolated move is considered as a component. % Thus the following path consists of three components: % % \begin{Verbatim} % \path (0,0) -- (1,0) (2,0) (3,0) to[out=0,in=90] (4,0); % \end{Verbatim} % % \StopEventually{} % % \section{Implementation} % % \iffalse %<*spath3> % \fi % \subsection{Initialisation} % % \begin{macrocode} %<@@=spath> % \end{macrocode} % % Load the \LaTeX3 foundation and register us as a \LaTeX3\ package. % \begin{macrocode} \NeedsTeXFormat{LaTeX2e} \RequirePackage{expl3} \RequirePackage{pgf} \ProvidesExplPackage {spath3} {2021/02/05} {2.2} {Functions for manipulating PGF soft paths} \RequirePackage{xparse} % \end{macrocode} % % Utilities copied from \url{https://github.com/loopspace/LaTeX3-Utilities} for adding something in braces to a token list. % I find I use this quite a lot in my packages. % \begin{macrocode} \cs_new_protected:Nn \@@_tl_put_right_braced:Nn { \tl_put_right:Nn #1 { { #2 } } } \cs_generate_variant:Nn \@@_tl_put_right_braced:Nn { NV, cV, cv, Nx, cx } \cs_new_protected:Nn \@@_tl_gput_right_braced:Nn { \tl_gput_right:Nn #1 { { #2 } } } \cs_generate_variant:Nn \@@_tl_gput_right_braced:Nn { NV, cV, cv, Nx, cx } \cs_new_protected:Nn \@@_tl_put_left_braced:Nn { \tl_put_left:Nn #1 { { #2 } } } \cs_generate_variant:Nn \@@_tl_put_left_braced:Nn { NV, cV, cv, Nx, cx } \cs_new_protected:Nn \@@_tl_gput_left_braced:Nn { \tl_gput_left:Nn #1 { { #2 } } } \cs_generate_variant:Nn \@@_tl_gput_left_braced:Nn { NV, cV, cv, Nx, cx } % \end{macrocode} % % % I had to think a bit about how to get \TeX\ to work the way I wanted. % I'm really defining \emph{functions} but \TeX\ doesn't really have that concept, even with all the amazing \LaTeX3 stuff. % The main issue I had was with scoping and return values. % By default, \TeX\ functions aren't scoped -- they work on the same level as the calling functions. % To protect the internals from being overwritten, each core function works inside a group. % But then I have to work to get the answer out of it. % So each of my core functions finishes by storing its return value in an appropriate \Verb+output+ variable. % The core functions are then wrapped in a more user friendly interface that will take that output and assign it to a variable. % This also means that I can deal with local and global versions without duplicating code. % % \begin{macrocode} \tl_new:N \g_@@_output_tl \int_new:N \g_@@_output_int \seq_new:N \g_@@_output_seq \bool_new:N \g_@@_output_bool % \end{macrocode} % % To avoid creating vast numbers of variables, we provide ourselves with a few that we reuse frequently. % For that reason, most of them don't have very exciting names. % % These are general purpose variables. % \begin{macrocode} \tl_new:N \l_@@_tmpa_tl \tl_new:N \l_@@_tmpb_tl \tl_new:N \l_@@_tmpc_tl \tl_new:N \l_@@_tmpd_tl \tl_new:N \l_@@_tmpe_tl \tl_new:N \l_@@_tmpf_tl \tl_new:N \l_@@_tmpg_tl \tl_new:N \l_@@_tmph_tl \tl_new:N \l_@@_tmpi_tl \seq_new:N \l_@@_tmpa_seq \seq_new:N \l_@@_tmpb_seq \seq_new:N \l_@@_tmpc_seq \dim_new:N \l_@@_tmpa_dim \dim_new:N \l_@@_tmpb_dim \fp_new:N \l_@@_tmpa_fp \fp_new:N \l_@@_tmpb_fp \fp_new:N \l_@@_tmpc_fp \int_new:N \l_@@_tmpa_int \int_new:N \l_@@_tmpb_int \bool_new:N \l_@@_tmpa_bool % \end{macrocode} % % Whenever I need more than two \Verb+dim+ variables it is because I need to remember the position of a move. % % \begin{macrocode} \dim_new:N \l_@@_move_x_dim \dim_new:N \l_@@_move_y_dim % \end{macrocode} % % Closed paths often need special handling. % When it's needed, this will say whether the path is closed or not. % % \begin{macrocode} \bool_new:N \l_@@_closed_bool % \end{macrocode} % % The intersection routine can't happen inside a group so we need two token lists to hold the paths that we'll intersect. % % \begin{macrocode} \tl_new:N \l_@@_intersecta_tl \tl_new:N \l_@@_intersectb_tl % \end{macrocode} % % We need to be able to compare against the macros that can occur in a soft path so these token lists contain them. % These are global constants so that they can be used in other packages. % \begin{macrocode} \tl_const:Nn \c_spath_moveto_tl {\pgfsyssoftpath@movetotoken} \tl_const:Nn \c_spath_lineto_tl {\pgfsyssoftpath@linetotoken} \tl_const:Nn \c_spath_curveto_tl {\pgfsyssoftpath@curvetotoken} \tl_const:Nn \c_spath_curvetoa_tl {\pgfsyssoftpath@curvetosupportatoken} \tl_const:Nn \c_spath_curvetob_tl {\pgfsyssoftpath@curvetosupportbtoken} \tl_const:Nn \c_spath_closepath_tl {\pgfsyssoftpath@closepathtoken} % \end{macrocode} % % We will want to be able to use anonymous spaths internally, so we create a global counter that we can use to refer to them. % \begin{macrocode} \int_new:N \g_@@_anon_int \int_gzero:N \g_@@_anon_int % \end{macrocode} % % And some error messages % \begin{macrocode} \msg_new:nnn { spath3 } { unknown path construction } { The~ path~ construction~ element~ #1~ is~ not~ currently~ supported.} % \end{macrocode} % % % \subsection{Functional Implementation} % % In the functional approach, we start with a token list containing a soft path and do something to it (either calculate some information or manipulate it in some fashion). % We then store that information, or the manipulated path, in an appropriate macro. % The macro to store it in is the first argument. % These functions occur in two versions, the one with the \texttt{g} makes the assignment global. % % \begin{macro}[internal]{ % \spath_segments_to_seq:Nn, % \spath_segments_gto_seq:Nn %} % Splits a soft path into \emph{segments}, storing the result in a sequence. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_segments_to_seq:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_clear:N \l_@@_tmpb_tl \seq_clear:N \l_@@_tmpa_seq \dim_zero:N \l_@@_tmpa_dim \dim_zero:N \l_@@_tmpb_dim \bool_until_do:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_case:NnF \l_@@_tmpc_tl { \c_spath_moveto_tl { \tl_set_eq:NN \l_@@_tmpb_tl \c_spath_moveto_tl \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}} \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}} \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpd_tl {\tl_head:N \l_@@_tmpa_tl} \tl_if_eq:NNF \l_@@_tmpd_tl \c_spath_moveto_tl { \tl_clear:N \l_@@_tmpb_tl } } \c_spath_lineto_tl { \tl_set_eq:NN \l_@@_tmpb_tl \c_spath_moveto_tl \tl_put_right:Nx \l_@@_tmpb_tl { {\dim_use:N \l_@@_tmpa_dim} {\dim_use:N \l_@@_tmpb_dim} } \tl_put_right:NV \l_@@_tmpb_tl \c_spath_lineto_tl \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}} \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}} \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \c_spath_curvetoa_tl { \tl_set_eq:NN \l_@@_tmpb_tl \c_spath_moveto_tl \tl_put_right:Nx \l_@@_tmpb_tl { {\dim_use:N \l_@@_tmpa_dim} {\dim_use:N \l_@@_tmpb_dim} } \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curvetoa_tl \prg_replicate:nn {2} { \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpb_tl {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}} \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}} \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \c_spath_closepath_tl { \tl_set_eq:NN \l_@@_tmpb_tl \c_spath_moveto_tl \tl_put_right:Nx \l_@@_tmpb_tl { {\dim_use:N \l_@@_tmpa_dim} {\dim_use:N \l_@@_tmpb_dim} } \tl_put_right:NV \l_@@_tmpb_tl \c_spath_lineto_tl \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}} \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}} \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } } { \tl_set_eq:NN \l_@@_tmpb_tl \l_@@_tmpc_tl \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}} \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}} \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \tl_if_empty:NF \l_@@_tmpb_tl { \seq_put_right:NV \l_@@_tmpa_seq \l_@@_tmpb_tl } \tl_clear:N \l_@@_tmpb_tl } \seq_gclear:N \g_@@_output_seq \seq_gset_eq:NN \g_@@_output_seq \l_@@_tmpa_seq \group_end: } \cs_new_protected_nopar:Npn \spath_segments_to_seq:Nn #1#2 { \@@_segments_to_seq:n {#2} \seq_clear_new:N #1 \seq_set_eq:NN #1 \g_@@_output_seq \seq_gclear:N \g_@@_output_seq } \cs_generate_variant:Nn \spath_segments_to_seq:Nn {NV, cn, cV, Nv, cv} \cs_new_protected_nopar:Npn \spath_segments_gto_seq:Nn #1#2 { \@@_segments_to_seq:n {#2} \seq_clear_new:N #1 \seq_gset_eq:NN #1 \g_@@_output_seq \seq_gclear:N \g_@@_output_seq } \cs_generate_variant:Nn \spath_segments_gto_seq:Nn {NV, cn, cV, Nv, cv} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_components_to_seq:Nn, % \spath_components_gto_seq:Nn, % \spath_components_to_clist:Nn, % \spath_components_gto_clist:Nn, % } % Splits a soft path into \emph{components}, storing the result in a sequence or a clist. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_components_to_seq:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \seq_clear:N \l_@@_tmpa_seq \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:NV \l_@@_tmpa_tl \c_spath_moveto_tl \tl_set_eq:NN \l_@@_tmpb_tl \c_spath_moveto_tl \bool_do_until:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl} \tl_if_eq:NNT \l_@@_tmpc_tl \c_spath_moveto_tl { \seq_put_right:NV \l_@@_tmpa_seq \l_@@_tmpb_tl \tl_clear:N \l_@@_tmpb_tl } \tl_if_single:NTF \l_@@_tmpc_tl { \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpc_tl } { \tl_put_right:Nx \l_@@_tmpb_tl {{\l_@@_tmpc_tl}} } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \seq_gclear:N \g_@@_output_seq \seq_gset_eq:NN \g_@@_output_seq \l_@@_tmpa_seq \group_end: } \cs_new_protected_nopar:Npn \spath_components_to_seq:Nn #1#2 { \@@_components_to_seq:n {#2} \seq_clear_new:N #1 \seq_set_eq:NN #1 \g_@@_output_seq \seq_gclear:N \g_@@_output_seq } \cs_generate_variant:Nn \spath_components_to_seq:Nn {NV, cn, cV, cv, Nv} \cs_new_protected_nopar:Npn \spath_components_gto_seq:Nn #1#2 { \@@_components_to_seq:n {#2} \seq_clear_new:N #1 \seq_gset_eq:NN #1 \g_@@_output_seq \seq_gclear:N \g_@@_output_seq } \cs_generate_variant:Nn \spath_components_gto_seq:Nn {NV, cn, cV, cv, Nv} \cs_new_protected_nopar:Npn \spath_components_to_clist:Nn #1#2 { \@@_components_to_seq:n {#2} \clist_clear_new:N #1 \clist_set_from_seq:NN #1 \g_@@_output_seq \seq_gclear:N \g_@@_output_seq } \cs_generate_variant:Nn \spath_components_to_clist:Nn {NV, cn, cV, cv, Nv} \cs_new_protected_nopar:Npn \spath_components_gto_clist:Nn #1#2 { \@@_components_to_seq:n {#2} \clist_clear_new:N #1 \clist_gset_from_seq:NN #1 \g_@@_output_seq \seq_gclear:N \g_@@_output_seq } \cs_generate_variant:Nn \spath_components_gto_clist:Nn {NV, cn, cV, cv, Nv} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{\spath_length:n} % Counts the number of triples in the path. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_length:n #1 { \int_eval:n {\tl_count:n {#1} / 3} } \cs_generate_variant:Nn \spath_length:n {V} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_reallength:Nn, % \spath_greallength:Nn % } % The real length of a path is the number of triples that actually draw something (that is, the number of lines, curves, and closepaths). % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_reallength:n #1 { \group_begin: \int_set:Nn \l_@@_tmpa_int {0} \tl_map_inline:nn {#1} { \tl_set:Nn \l_@@_tmpa_tl {##1} \tl_case:NnT \l_@@_tmpa_tl { \c_spath_lineto_tl {} \c_spath_curveto_tl {} \c_spath_closepath_tl {} } { \int_incr:N \l_@@_tmpa_int } } \int_gzero:N \g_@@_output_int \int_gset_eq:NN \g_@@_output_int \l_@@_tmpa_int \group_end: } \cs_new_protected_nopar:Npn \spath_reallength:Nn #1#2 { \@@_reallength:n {#2} \int_set_eq:NN #1 \g_@@_output_int \int_gzero:N \g_@@_output_int } \cs_generate_variant:Nn \spath_reallength:Nn {NV, cn, cV, Nv, cv} \cs_new_protected_nopar:Npn \spath_greallength:Nn #1#2 { \@@_reallength:n {#2} \int_gset_eq:NN #1 \g_@@_output_int \int_gzero:N \g_@@_output_int } \cs_generate_variant:Nn \spath_greallength:Nn {NV, cn, cV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_numberofcomponents:Nn, % \spath_gnumberofcomponents:Nn % } % A component is a continuous segment of the path, separated by moves. % Successive moves are not collapsed, and zero length moves count. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_numberofcomponents:n #1 { \group_begin: \int_set:Nn \l_@@_tmpa_int {0} \tl_map_inline:nn {#1} { \tl_set:Nn \l_@@_tmpa_tl {##1} \tl_case:Nn \l_@@_tmpa_tl { \c_spath_moveto_tl { \int_incr:N \l_@@_tmpa_int } } } \int_gzero:N \g_@@_output_int \int_gset_eq:NN \g_@@_output_int \l_@@_tmpa_int \group_end: } \cs_new_protected_nopar:Npn \spath_numberofcomponents:Nn #1#2 { \@@_numberofcomponents:n {#2} \int_set_eq:NN #1 \g_@@_output_int \int_gzero:N \g_@@_output_int } \cs_generate_variant:Nn \spath_numberofcomponents:Nn {NV, cn, cV, Nv} \cs_new_protected_nopar:Npn \spath_gnumberofcomponents:Nn #1#2 { \@@_numberofcomponents:n {#2} \int_gset_eq:NN #1 \g_@@_output_int \int_gzero:N \g_@@_output_int } \cs_generate_variant:Nn \spath_gnumberofcomponents:Nn {NV, cn, cV, Nv} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_initialpoint:Nn, % \spath_ginitialpoint:Nn % } % The starting point of the path. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_initialpoint:n #1 { \group_begin: \tl_clear:N \l_@@_tmpa_tl \tl_set:Nx \l_@@_tmpa_tl { { \tl_item:nn {#1} {2} } { \tl_item:nn {#1} {3} } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_new_protected_nopar:Npn \spath_initialpoint:Nn #1#2 { \@@_initialpoint:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_initialpoint:Nn {NV, cn, cV, Nv} \cs_new_protected_nopar:Npn \spath_ginitialpoint:Nn #1#2 { \@@_initialpoint:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_ginitialpoint:Nn {NV, cn, cV, Nv} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_finalpoint:Nn, % \spath_gfinalpoint:Nn, % } % The final point of the path. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_finalpoint:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_reverse:N \l_@@_tmpa_tl \tl_clear:N \l_@@_tmpb_tl \tl_set:Nx \l_@@_tmpb_tl { { \tl_item:Nn \l_@@_tmpa_tl {2} } { \tl_item:Nn \l_@@_tmpa_tl {1} } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_finalpoint:Nn #1#2 { \@@_finalpoint:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_finalpoint:Nn {NV, cn, cV, Nv} \cs_new_protected_nopar:Npn \spath_gfinalpoint:Nn #1#2 { \@@_finalpoint:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gfinalpoint:Nn {NV, cn, cV, Nv} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_finalmovepoint:Nn, % \spath_gfinalmovepoint:Nn % } % Get the last move on the path. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_finalmovepoint:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpc_tl { {0pt} {0pt} } \tl_set:Nn \l_@@_tmpa_tl {#1} \bool_do_until:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_set:Nx \l_@@_tmpb_tl {\tl_head:N \l_@@_tmpa_tl} \tl_case:Nn \l_@@_tmpb_tl { \c_spath_moveto_tl { \tl_set:Nx \l_@@_tmpc_tl { { \tl_item:Nn \l_@@_tmpa_tl {2} } { \tl_item:Nn \l_@@_tmpa_tl {3} } } } } \prg_replicate:nn {3} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpc_tl \group_end: } \cs_new_protected_nopar:Npn \spath_finalmovepoint:Nn #1#2 { \@@_finalmovepoint:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_finalmovepoint:Nn {NV, cn, cV} \cs_new_protected_nopar:Npn \spath_gfinalmovepoint:Nn #1#2 { \@@_finalmovepoint:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gfinalmovepoint:Nn {NV, cn, cV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_reverse:Nn, % \spath_greverse:Nn % } % This computes the reverse of the path. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_reverse:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_clear:N \l_@@_tmpb_tl \tl_clear:N \l_@@_tmpd_tl \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_left:Nx \l_@@_tmpd_tl { {\dim_use:N \l_@@_tmpa_dim} {\dim_use:N \l_@@_tmpb_dim} } \bool_set_false:N \l_@@_closed_bool \bool_until_do:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl} \tl_case:NnTF \l_@@_tmpc_tl { \c_spath_moveto_tl { \bool_if:NT \l_@@_closed_bool { \tl_put_right:NV \l_@@_tmpd_tl \c_spath_closepath_tl \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpd_tl} \tl_put_right:Nx \l_@@_tmpd_tl { { \tl_head:N \l_@@_tmpd_tl } { \tl_head:N \l_@@_tmpe_tl } } } \bool_set_false:N \l_@@_closed_bool \tl_put_left:NV \l_@@_tmpd_tl \c_spath_moveto_tl \tl_put_left:NV \l_@@_tmpb_tl \l_@@_tmpd_tl \tl_clear:N \l_@@_tmpd_tl } \c_spath_lineto_tl { \tl_put_left:NV \l_@@_tmpd_tl \c_spath_lineto_tl } \c_spath_curveto_tl { \tl_put_left:NV \l_@@_tmpd_tl \c_spath_curvetoa_tl } \c_spath_curvetoa_tl { \tl_put_left:NV \l_@@_tmpd_tl \c_spath_curveto_tl } \c_spath_curvetob_tl { \tl_put_left:NV \l_@@_tmpd_tl \c_spath_curvetob_tl } } { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_left:Nx \l_@@_tmpd_tl { {\dim_use:N \l_@@_tmpa_dim} {\dim_use:N \l_@@_tmpb_dim} } } { \tl_if_eq:NNTF \l_@@_tmpc_tl \c_spath_closepath_tl { \bool_set_true:N \l_@@_closed_bool } { \msg_warning:nnx { spath3 } { unknown path construction } {\l_@@_tmpc_tl } } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } } \bool_if:NT \l_@@_closed_bool { \tl_put_right:NV \l_@@_tmpd_tl \c_spath_closepath_tl \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpd_tl} \tl_put_right:Nx \l_@@_tmpd_tl { { \tl_head:N \l_@@_tmpd_tl } { \tl_head:N \l_@@_tmpe_tl } } } \bool_set_false:N \l_@@_closed_bool \tl_put_left:NV \l_@@_tmpd_tl \c_spath_moveto_tl \tl_put_left:NV \l_@@_tmpb_tl \l_@@_tmpd_tl \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_reverse:Nn #1#2 { \@@_reverse:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_reverse:Nn {NV, cn, cV, Nv} \cs_new_protected_nopar:Npn \spath_reverse:N #1 { \spath_reverse:NV #1#1 } \cs_generate_variant:Nn \spath_reverse:N {c} \cs_new_protected_nopar:Npn \spath_greverse:Nn #1#2 { \@@_reverse:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_greverse:Nn {NV, cn, cV, Nv} \cs_new_protected_nopar:Npn \spath_greverse:N #1 { \spath_greverse:NV #1#1 } \cs_generate_variant:Nn \spath_greverse:N {c} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_initialaction:Nn, % \spath_ginitialaction:Nn, % } % This is the first thing that the path does (after the initial move). % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_initialaction:n #1 { \group_begin: \tl_clear:N \l_@@_tmpa_tl \int_compare:nT { \tl_count:n {#1} > 3 } { \tl_set:Nx \l_@@_tmpa_tl { \tl_item:Nn {#1} {4} } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_new_protected_nopar:Npn \spath_initialaction:Nn #1#2 { \@@_initialaction:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_initialaction:Nn {NV} \cs_new_protected_nopar:Npn \spath_ginitialaction:Nn #1#2 { \@@_initialaction:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_ginitialaction:Nn {NV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_finalaction:Nn, % \spath_gfinalaction:Nn % } % This is the last thing that the path does. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_finalaction:n #1 { \group_begin: \tl_clear:N \l_@@_tmpb_tl \int_compare:nT { \tl_count:n {#1} > 3 } { \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_reverse:N \l_@@_tmpa_tl \tl_set:Nx \l_@@_tmpb_tl { \tl_item:Nn \l_@@_tmpa_tl {3} } \tl_if_eq:NNT \l_@@_tmpb_tl \c_spath_curvetoa_tl { \tl_set_eq:NN \l_@@_tmpb_tl \c_spath_curveto_tl } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_finalaction:Nn #1#2 { \@@_finalaction:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_finalaction:Nn {NV} \cs_new_protected_nopar:Npn \spath_gfinalaction:Nn #1#2 { \@@_finalaction:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gfinalaction:Nn {NV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_minbb:Nn, % \spath_gminbb:Nn % } % This computes the minimum (bottom left) of the bounding box of the path. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_minbb:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \bool_until_do:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpa_dim {\dim_min:nn {\tl_head:N \l_@@_tmpa_tl} {\l_@@_tmpa_dim}} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpb_dim {\dim_min:nn {\tl_head:N \l_@@_tmpa_tl} {\l_@@_tmpb_dim}} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \tl_clear:N \l_@@_tmpb_tl \tl_put_right:Nx \l_@@_tmpb_tl { {\dim_use:N \l_@@_tmpa_dim} {\dim_use:N \l_@@_tmpb_dim} } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_minbb:Nn #1#2 { \@@_minbb:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_minbb:Nn {NV, cn, cV} \cs_new_protected_nopar:Npn \spath_gminbb:Nn #1#2 { \@@_minbb:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gminbb:Nn {NV, cn, cV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_maxbb:Nn, % \spath_gmaxbb:Nn, % } % This computes the maximum (top right) of the bounding box of the path. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_maxbb:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \bool_until_do:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpa_dim {\dim_max:nn {\tl_head:N \l_@@_tmpa_tl} {\l_@@_tmpa_dim}} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpb_dim {\dim_max:nn {\tl_head:N \l_@@_tmpa_tl} {\l_@@_tmpb_dim}} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \tl_clear:N \l_@@_tmpb_tl \tl_put_right:Nx \l_@@_tmpb_tl { {\dim_use:N \l_@@_tmpa_dim} {\dim_use:N \l_@@_tmpb_dim} } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_maxbb:Nn #1#2 { \@@_maxbb:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_maxbb:Nn {NV, cn, cV} \cs_new_protected_nopar:Npn \spath_gmaxbb:Nn #1#2 { \@@_maxbb:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gmaxbb:Nn {NV, cn, cV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_save_to_aux:Nn, % \spath_save_to_aux:N % } % This saves a soft path to the auxfile. % The first argument is the macro that will be assigned to the soft path when the aux file is read back in. % \begin{macrocode} \int_set:Nn \l_@@_tmpa_int {\char_value_catcode:n {`@}} \char_set_catcode_letter:N @ \cs_new_protected_nopar:Npn \spath_save_to_aux:Nn #1#2 { \tl_if_empty:nF {#2} { \tl_clear:N \l_@@_tmpa_tl \tl_put_right:Nn \l_@@_tmpa_tl { \ExplSyntaxOn \tl_clear:N #1 \tl_set:Nn #1 {#2} \ExplSyntaxOff } \protected@write\@auxout{}{ \tl_to_str:N \l_@@_tmpa_tl } } } \char_set_catcode:nn {`@} {\l_@@_tmpa_int} \cs_generate_variant:Nn \spath_save_to_aux:Nn {cn, cV, NV} \cs_new_protected_nopar:Npn \spath_save_to_aux:N #1 { \tl_if_exist:NT #1 { \spath_save_to_aux:NV #1#1 } } % \end{macrocode} % \end{macro} % % % \subsection{Path Manipulation} % % These functions all manipulate a soft path. % They come with a variety of different argument specifications. % As a general rule, the first argument is the macro in which to store the modified path, the second is the path to manipulate, and the rest are the information about what to do. % There is always a variant in which the path is specified by a macro and restored back in that same macro. % % % \begin{macro}[internal]{ % \spath_translate:Nnnn, % \spath_translate:Nnn, % \spath_gtranslate:Nnnn, % \spath_gtranslate:Nnn % } % Translates a path by an amount. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_translate:nnn #1#2#3 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_clear:N \l_@@_tmpb_tl \bool_until_do:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_put_right:Nx \l_@@_tmpb_tl {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl + #2} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl + #3} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpb_tl { {\dim_use:N \l_@@_tmpa_dim} {\dim_use:N \l_@@_tmpb_dim} } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_generate_variant:Nn \@@_translate:nnn {nVV} \cs_new_protected_nopar:Npn \spath_translate:Nnnn #1#2#3#4 { \@@_translate:nnn {#2}{#3}{#4} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_translate:Nnnn {NVxx, NVVV, NVnn} \cs_new_protected_nopar:Npn \spath_translate:Nnn #1#2#3 { \spath_translate:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \spath_translate:Nnn {NVV, cnn, cVV} \cs_new_protected_nopar:Npn \spath_gtranslate:Nnnn #1#2#3#4 { \@@_translate:nnn {#2}{#3}{#4} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gtranslate:Nnnn {NVxx, NVVV, NVnn} \cs_new_protected_nopar:Npn \spath_gtranslate:Nnn #1#2#3 { \spath_gtranslate:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \spath_gtranslate:Nnn {NVV, cnn, cVV} % \end{macrocode} % % This variant allows for passing the coordinates as a single braced group as it strips off the outer braces of the second argument. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_translate:Nn #1#2 { \spath_translate:Nnn #1 #2 } \cs_generate_variant:Nn \spath_translate:Nn {NV} \cs_new_protected_nopar:Npn \spath_gtranslate:Nn #1#2 { \spath_gtranslate:Nnn #1 #2 } \cs_generate_variant:Nn \spath_gtranslate:Nn {NV} % \end{macrocode} % \end{macro} % % % % \begin{macro}[internal]{ % \spath_translate_to:Nnnn, % \spath_translate_to:Nnn, % \spath_gtranslate_to:Nnnn, % \spath_gtranslate_to:Nnn % } % Translates a path so that it starts at a point. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_translate_to:nnn #1#2#3 { \group_begin: \spath_initialpoint:Nn \l_@@_tmpa_tl {#1} \dim_set:Nn \l_@@_tmpa_dim { #2 - \tl_item:Nn \l_@@_tmpa_tl {1} } \dim_set:Nn \l_@@_tmpb_dim { #3 - \tl_item:Nn \l_@@_tmpa_tl {2} } \@@_translate:nVV {#1} \l_@@_tmpa_dim \l_@@_tmpb_dim \group_end: } \cs_new_protected_nopar:Npn \spath_translate_to:Nnnn #1#2#3#4 { \@@_translate_to:nnn {#2}{#3}{#4} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_translate_to:Nnnn {NVxx, NVVV, NVnn} \cs_new_protected_nopar:Npn \spath_translate_to:Nnn #1#2#3 { \spath_translate_to:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \spath_translate_to:Nnn {NVV, cnn, cVV} \cs_new_protected_nopar:Npn \spath_gtranslate_to:Nnnn #1#2#3#4 { \@@_translate_to:nnn {#2}{#3}{#4} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gtranslate_to:Nnnn {NVxx, NVVV, NVnn} \cs_new_protected_nopar:Npn \spath_gtranslate_to:Nnn #1#2#3 { \spath_gtranslate_to:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \spath_gtranslate_to:Nnn {NVV, cnn, cVV} % \end{macrocode} % % This variant allows for passing the coordinates as a single braced group as it strips off the outer braces of the second argument. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_translate_to:Nn #1#2 { \spath_translate_to:Nnn #1 #2 } \cs_generate_variant:Nn \spath_translate_to:Nn {NV} \cs_new_protected_nopar:Npn \spath_gtranslate_to:Nn #1#2 { \spath_gtranslate_to:Nnn #1 #2 } \cs_generate_variant:Nn \spath_gtranslate_to:Nn {NV} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \spath_scale:Nnnn, % \spath_scale:Nnn, % \spath_gscale:Nnnn, % \spath_gscale:Nnn % } % Scale a path. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_scale:nnn #1#2#3 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_clear:N \l_@@_tmpb_tl \bool_until_do:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_put_right:Nx \l_@@_tmpb_tl {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \fp_set:Nn \l_@@_tmpa_fp {\tl_head:N \l_@@_tmpa_tl * #2} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \fp_set:Nn \l_@@_tmpb_fp {\tl_head:N \l_@@_tmpa_tl * #3} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpb_tl { {\fp_to_dim:N \l_@@_tmpa_fp} {\fp_to_dim:N \l_@@_tmpb_fp} } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_scale:Nnnn #1#2#3#4 { \@@_scale:nnn {#2}{#3}{#4} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_scale:Nnnn {NVnn, Nnxx} \cs_new_protected_nopar:Npn \spath_scale:Nnn #1#2#3 { \spath_scale:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \spath_scale:Nnn {cnn, cVV, NVV} \cs_new_protected_nopar:Npn \spath_gscale:Nnnn #1#2#3#4 { \@@_scale:nnn {#2}{#3}{#4} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gscale:Nnnn {NVnn, Nnxx} \cs_new_protected_nopar:Npn \spath_gscale:Nnn #1#2#3 { \spath_gscale:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \spath_gscale:Nnn {cnn, cVV, NVV} % \end{macrocode} % % This variant allows for passing the coordinates as a single braced group as it strips off the outer braces of the second argument. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_scale:Nn #1#2 { \spath_scale:Nnn #1 #2 } \cs_generate_variant:Nn \spath_scale:Nn {NV} \cs_new_protected_nopar:Npn \spath_gscale:Nn #1#2 { \spath_gscale:Nnn #1 #2 } \cs_generate_variant:Nn \spath_gscale:Nn {NV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_transform:Nnnnnnnn, % \spath_transform:Nnnnnnn, % \spath_gtransform:Nnnnnnnn, % \spath_gtransform:Nnnnnnn, % } % Applies an affine (matrix and vector) transformation to path. % The matrix is specified in rows first. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_transform:nnnnnnn #1#2#3#4#5#6#7 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_clear:N \l_@@_tmpb_tl \bool_until_do:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_put_right:Nx \l_@@_tmpb_tl {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpd_tl {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \fp_set:Nn \l_@@_tmpa_fp {\l_@@_tmpc_tl * #2 + \l_@@_tmpd_tl * #4 + #6} \fp_set:Nn \l_@@_tmpb_fp {\l_@@_tmpc_tl * #3 + \l_@@_tmpd_tl * #5 + #7} \tl_put_right:Nx \l_@@_tmpb_tl { {\fp_to_dim:N \l_@@_tmpa_fp} {\fp_to_dim:N \l_@@_tmpb_fp} } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_transform:Nnnnnnnn #1#2#3#4#5#6#7#8 { \@@_transform:nnnnnnn {#2}{#3}{#4}{#5}{#6}{#7}{#8} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_transform:Nnnnnnnn {NVnnnnnn, Nnxxxxxx, cnnnnnnn} \cs_new_protected_nopar:Npn \spath_transform:Nnnnnnn #1#2#3#4#5#6#7 { \spath_transform:NVnnnnnn #1#1{#2}{#3}{#4}{#5}{#6}{#7} } \cs_generate_variant:Nn \spath_transform:Nnnnnnn {cnnnnnn} \cs_new_protected_nopar:Npn \spath_transform:Nnn #1#2#3 { \spath_transform:Nnnnnnnn #1{#2}#3 } \cs_generate_variant:Nn \spath_transform:Nnn {cnn, cVn, NVn, NnV} \cs_new_protected_nopar:Npn \spath_transform:Nn #1#2 { \spath_transform:NVnnnnnn #1#1#2 } \cs_generate_variant:Nn \spath_transform:Nn {cn, cV, NV} \cs_new_protected_nopar:Npn \spath_gtransform:Nnnnnnnn #1#2#3#4#5#6#7#8 { \@@_transform:nnnnnnn {#2}{#3}{#4}{#5}{#6}{#7}{#8} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gtransform:Nnnnnnnn {NVnnnnnn, Nnxxxxxx, cnnnnnnn} \cs_new_protected_nopar:Npn \spath_gtransform:Nnnnnnn #1#2#3#4#5#6#7 { \spath_gtransform:NVnnnnnn #1#1{#2}{#3}{#4}{#5}{#6}{#7} } \cs_generate_variant:Nn \spath_gtransform:Nnnnnnn {cnnnnnn} \cs_new_protected_nopar:Npn \spath_gtransform:Nnn #1#2#3 { \spath_gtransform:Nnnnnnnn #1{#2}#3 } \cs_generate_variant:Nn \spath_gtransform:Nnn {cnn, cVn, NVn, NnV} \cs_new_protected_nopar:Npn \spath_gtransform:Nn #1#2 { \spath_gtransform:NVnnnnnn #1#1#2 } \cs_generate_variant:Nn \spath_gtransform:Nn {cn, cV, NV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_span:Nnnn, % \spath_span:Nnn, % \spath_gspan:Nnnn, % \spath_gspan:Nnn, % \spath_normalise:Nn, % \spath_normalise:N, % \spath_gnormalise:Nn, % \spath_gnormalise:N % } % The \Verb+span+ functions transform a path to start and end at specified points. % The \Verb+normalise+ functions transform it to start at the origin and end at \((1\mathrm{pt},0\mathrm{pt})\). % % If the path starts and ends at the same point then it is translated to the specified point (or origin) but not otherwise changed. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_span:nnn #1#2#3 { \group_begin: \spath_initialpoint:Nn \l_@@_tmpa_tl {#1} \spath_finalpoint:Nn \l_@@_tmpb_tl {#1} \fp_set:Nn \l_@@_tmpa_fp { (\tl_item:Nn \l_@@_tmpb_tl {1}) - (\tl_item:Nn \l_@@_tmpa_tl {1}) } \fp_set:Nn \l_@@_tmpb_fp { (\tl_item:Nn \l_@@_tmpb_tl {2}) - (\tl_item:Nn \l_@@_tmpa_tl {2}) } \fp_set:Nn \l_@@_tmpc_fp { (\l_@@_tmpa_fp) * (\l_@@_tmpa_fp) + (\l_@@_tmpb_fp * \l_@@_tmpb_fp) } \fp_compare:nTF { \l_@@_tmpc_fp < 0.001 } { \spath_translate_to:Nnnn \l_@@_tmpd_tl {#1} #2 } { \fp_set:Nn \l_@@_tmpa_fp { ( ((\tl_item:nn {#3} {1}) - (\tl_item:nn {#2} {1})) * ((\tl_item:Nn \l_@@_tmpb_tl {1}) - (\tl_item:Nn \l_@@_tmpa_tl {1})) + ((\tl_item:nn {#3} {2}) - (\tl_item:nn {#2} {2})) * ((\tl_item:Nn \l_@@_tmpb_tl {2}) - (\tl_item:Nn \l_@@_tmpa_tl {2})) ) / \l_@@_tmpc_fp } \fp_set:Nn \l_@@_tmpb_fp { ( ((\tl_item:nn {#3} {2}) - (\tl_item:nn {#2} {2})) * ((\tl_item:Nn \l_@@_tmpb_tl {1}) - (\tl_item:Nn \l_@@_tmpa_tl {1})) - ((\tl_item:nn {#3} {1}) - (\tl_item:nn {#2} {1})) * ((\tl_item:Nn \l_@@_tmpb_tl {2}) - (\tl_item:Nn \l_@@_tmpa_tl {2})) ) / \l_@@_tmpc_fp } \tl_set:Nx \l_@@_tmpc_tl { { \fp_to_decimal:N \l_@@_tmpa_fp } { \fp_to_decimal:N \l_@@_tmpb_fp } { \fp_eval:n { - \l_@@_tmpb_fp } } { \fp_to_decimal:N \l_@@_tmpa_fp } { \fp_to_dim:n { \tl_item:nn {#2} {1} - \l_@@_tmpa_fp * (\tl_item:Nn \l_@@_tmpa_tl {1}) + \l_@@_tmpb_fp * (\tl_item:Nn \l_@@_tmpa_tl {2}) } } { \fp_to_dim:n { \tl_item:nn {#2} {2} - \l_@@_tmpb_fp * (\tl_item:Nn \l_@@_tmpa_tl {1}) - \l_@@_tmpa_fp * (\tl_item:Nn \l_@@_tmpa_tl {2}) } } } \spath_transform:NnV \l_@@_tmpd_tl {#1} \l_@@_tmpc_tl } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpd_tl \group_end: } \cs_new_protected_nopar:Npn \spath_span:Nnnn #1#2#3#4 { \@@_span:nnn {#2}{#3}{#4} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_span:Nnnn {NVnn, NVVV, NnVV} \cs_new_protected_nopar:Npn \spath_span:Nnn #1#2#3 { \spath_span:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \spath_span:Nnn {NVV, cnn, cvv, cVV} \cs_new_protected_nopar:Npn \spath_gspan:Nnnn #1#2#3#4 { \@@_span:nnn {#2}{#3}{#4} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gspan:Nnnn {NVnn, NVVV} \cs_new_protected_nopar:Npn \spath_gspan:Nnn #1#2#3 { \spath_gspan:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \spath_gspan:Nnn {NVV, cnn, cvv, cVV} \cs_new_protected_nopar:Npn \@@_normalise:n #1 { \@@_span:nnn {#1}{{0pt}{0pt}}{{1pt}{0pt}} } \cs_new_protected_nopar:Npn \spath_normalise:Nn #1#2 { \@@_normalise:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_normalise:Nn {cn,NV, cV, cv} \cs_new_protected_nopar:Npn \spath_normalise:N #1 { \spath_normalise:NV #1#1 } \cs_generate_variant:Nn \spath_normalise:N {c} \cs_new_protected_nopar:Npn \spath_gnormalise:Nn #1#2 { \@@_normalise:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gnormalise:Nn {cn,NV, cV, cv} \cs_new_protected_nopar:Npn \spath_gnormalise:N #1 { \spath_gnormalise:NV #1#1 } \cs_generate_variant:Nn \spath_gnormalise:N {c} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \spath_splice_between:Nnnn, % \spath_splice_between:Nnn, % \spath_gsplice_between:Nnnn, % \spath_gsplice_between:Nnn % } % This takes three paths and returns a single path in which the middle one is adjusted (and welded) so that it joins the first path to the third. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_splice_between:nnn #1#2#3 { \group_begin: \spath_finalpoint:NV \l_@@_tmpd_tl {#1} \spath_initialpoint:NV \l_@@_tmpe_tl {#3} \spath_span:NnVV \l_@@_tmpb_tl {#2} \l_@@_tmpd_tl \l_@@_tmpe_tl \spath_append_no_move:NnV \l_@@_tmpa_tl {#1} \l_@@_tmpb_tl \spath_append_no_move:Nn \l_@@_tmpa_tl {#3} \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_new_protected_nopar:Npn \spath_splice_between:Nnnn #1#2#3#4 { \@@_splice_between:nnn {#2}{#3}{#4} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_splice_between:Nnnn {NVnn, NVVV} \cs_new_protected_nopar:Npn \spath_splice_between:Nnn #1#2#3 { \spath_splice_between:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \spath_splice_between:Nnn {NVV, cnn, cvv, Nvn} \cs_new_protected_nopar:Npn \spath_gsplice_between:Nnnn #1#2#3#4 { \@@_splice_between:nnn {#2}{#3}{#4} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplice_between:Nnnn {NVnn, NVVV} \cs_new_protected_nopar:Npn \spath_gsplice_between:Nnn #1#2#3 { \spath_gsplice_between:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \spath_gsplice_between:Nnn {NVV, cnn, cvv} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_close_with:Nn, % \spath_gclose_with:Nn % } % Closes the first path by splicing in the second. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_close_with:nn #1#2 { \group_begin: \spath_finalmovepoint:Nn \l_@@_tmpa_tl {#1} \spath_finalpoint:Nn \l_@@_tmpb_tl {#1} \dim_compare:nTF { \dim_abs:n { \tl_item:Nn \l_@@_tmpa_tl {1} - \tl_item:Nn \l_@@_tmpb_tl {1} } + \dim_abs:n { \tl_item:Nn \l_@@_tmpa_tl {2} - \tl_item:Nn \l_@@_tmpb_tl {2} } < 0.01pt } { \@@_close:n {#1} } { \spath_span:NnVV \l_@@_tmpc_tl {#2} \l_@@_tmpb_tl \l_@@_tmpa_tl \spath_append_no_move:NnV \l_@@_tmpd_tl {#1} \l_@@_tmpc_tl \@@_close:V \l_@@_tmpd_tl } \group_end: } \cs_new_protected_nopar:Npn \spath_close_with:Nnn #1#2#3 { \@@_close_with:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_close_with:Nnn {cnn, cVV, cvv, NVn} \cs_new_protected_nopar:Npn \spath_close_with:Nn #1#2 { \spath_close_with:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_close_with:Nn {cn, cV, cv, NV} \cs_new_protected_nopar:Npn \spath_gclose_with:Nnn #1#2#3 { \@@_close_with:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gclose_with:Nnn {cnn, cVV, cvv, NVn} \cs_new_protected_nopar:Npn \spath_gclose_with:Nn #1#2 { \spath_gclose_with:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gclose_with:Nn {cn, cV, cv, NV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_weld:Nnn, % \spath_weld:Nn, % \spath_gweld:Nnn, % \spath_gweld:Nn % } % This welds one path to another, moving the second so that its initial point coincides with the first's final point. % It is called a \emph{weld} because the initial move of the second path is removed. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_weld:nn #1#2 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_set:Nn \l_@@_tmpb_tl {#2} \spath_finalpoint:Nn \l_@@_tmpc_tl {#1} \spath_translate_to:NV \l_@@_tmpb_tl \l_@@_tmpc_tl \@@_append_no_move:VV \l_@@_tmpa_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_weld:Nnn #1#2#3 { \@@_weld:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_weld:Nnn {NVV,NVn} \cs_new_protected_nopar:Npn \spath_weld:Nn #1#2 { \spath_weld:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_weld:Nn {NV, Nv, cV, cv} \cs_new_protected_nopar:Npn \spath_gweld:Nnn #1#2#3 { \@@_weld:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gweld:Nnn {NVV, NVn} \cs_new_protected_nopar:Npn \spath_gweld:Nn #1#2 { \spath_gweld:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gweld:Nn {NV, Nv, cV, cv} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \spath_append_no_move:Nnn, % \spath_append_no_move:Nn, % \spath_gappend_no_move:Nnn, % \spath_gappend_no_move:Nn, % } % Append the path from the second \Verb+spath+ to the first, removing % the adjoining move. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_append_no_move:nn #1#2 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_set:Nn \l_@@_tmpb_tl {#2} \tl_set:Nx \l_@@_tmpb_tl {\tl_tail:N \l_@@_tmpb_tl} \tl_set:Nx \l_@@_tmpb_tl {\tl_tail:N \l_@@_tmpb_tl} \tl_set:Nx \l_@@_tmpb_tl {\tl_tail:N \l_@@_tmpb_tl} \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpb_tl \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_generate_variant:Nn \@@_append_no_move:nn {VV} \cs_new_protected_nopar:Npn \spath_append_no_move:Nnn #1#2#3 { \@@_append_no_move:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_append_no_move:Nnn {NVV, NVn, NnV} \cs_new_protected_nopar:Npn \spath_append_no_move:Nn #1#2 { \spath_append_no_move:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_append_no_move:Nn {NV, cv, Nv} \cs_new_protected_nopar:Npn \spath_gappend_no_move:Nnn #1#2#3 { \@@_append_no_move:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gappend_no_move:Nnn {NVV, NVn} \cs_new_protected_nopar:Npn \spath_gappend_no_move:Nn #1#2 { \spath_gappend_no_move:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gappend_no_move:Nn {NV, cv, Nv} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_append:Nnn, % \spath_append:Nn, % \spath_gappend:Nnn, % \spath_gappend:Nn, % } % Prepend the path from the second \Verb+spath+ to the first. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_append:Nnn #1#2#3 { \tl_set:Nn #1 {#2} \tl_put_right:Nn #1 {#3} } \cs_generate_variant:Nn \spath_append:Nnn {NVV, NVn} \cs_new_protected_nopar:Npn \spath_append:Nn #1#2 { \spath_append:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_append:Nn {NV, Nv, cv} \cs_new_protected_nopar:Npn \spath_gappend:Nnn #1#2#3 { \tl_gset:Nn #1 {#2} \tl_gput_right:Nn #1 {#3} } \cs_generate_variant:Nn \spath_gappend:Nnn {NVV, NVn} \cs_new_protected_nopar:Npn \spath_gappend:Nn #1#2 { \spath_gappend:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gappend:Nn {NV, Nv, cv} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \spath_prepend_no_move:Nnn, % \spath_prepend_no_move:Nn, % \spath_gprepend_no_move:Nnn, % \spath_gprepend_no_move:Nn, % } % Prepend the path from the second \Verb+spath+ to the first, removing % the adjoining move. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_prepend_no_move:Nnn #1#2#3 { \spath_append_no_move:Nnn #1{#3}{#2} } \cs_generate_variant:Nn \spath_prepend_no_move:Nnn {NVV, NVn} \cs_new_protected_nopar:Npn \spath_prepend_no_move:Nn #1#2 { \spath_prepend_no_move:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_prepend_no_move:Nn {NV, cv} \cs_new_protected_nopar:Npn \spath_gprepend_no_move:Nnn #1#2#3 { \spath_gappend_no_move:Nnn #1{#3}{#2} } \cs_generate_variant:Nn \spath_gprepend_no_move:Nnn {NVV, NVn} \cs_new_protected_nopar:Npn \spath_gprepend_no_move:Nn #1#2 { \spath_gprepend_no_move:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gprepend_no_move:Nn {NV, cv} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_prepend:Nnn, % \spath_prepend:Nn, % \spath_gprepend:Nnn, % \spath_gprepend:Nn % } % Prepend the path from the second \Verb+spath+ to the first. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_prepend:Nnn #1#2#3 { \spath_append:Nnn #1{#3}{#2} } \cs_generate_variant:Nn \spath_prepend:Nnn {NVV, NVn} \cs_new_protected_nopar:Npn \spath_prepend:Nn #1#2 { \spath_prepend:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_prepend:Nn {NV} \cs_new_protected_nopar:Npn \spath_gprepend:Nnn #1#2#3 { \spath_gappend:Nnn #1{#3}{#2} } \cs_generate_variant:Nn \spath_gprepend:Nnn {NVV, NVn} \cs_new_protected_nopar:Npn \spath_gprepend:Nn #1#2 { \spath_gprepend:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gprepend:Nn {NV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_bake_round:Nn, % \spath_bake_round:N, % \spath_gbake_round:Nn, % \spath_gbake_round:N % } % % The corner rounding routine is applied quite late in the process of building a soft path so this ensures that it is done. % % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_bake_round:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \pgf@@@@processround \l_@@_tmpa_tl\l_@@_tmpb_tl \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_bake_round:Nn #1#2 { \@@_bake_round:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_bake_round:Nn {NV} \cs_new_protected_nopar:Npn \spath_bake_round:N #1 { \spath_bake_round:NV #1#1 } \cs_generate_variant:Nn \spath_bake_round:N {c} \cs_new_protected_nopar:Npn \spath_gbake_round:Nn #1#2 { \@@_bake_round:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gbake_round:Nn {NV} \cs_new_protected_nopar:Npn \spath_gbake_round:N #1 { \spath_gbake_round:NV #1#1 } \cs_generate_variant:Nn \spath_gbake_round:N {c} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_close:Nn, % \spath_close:N, % \spath_gclose:Nn, % \spath_gclose:N % } % Appends a close path to the end of the path. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_close:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \spath_finalmovepoint:NV \l_@@_tmpb_tl \l_@@_tmpa_tl \tl_put_right:NV \l_@@_tmpa_tl \c_spath_closepath_tl \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpb_tl \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_generate_variant:Nn \@@_close:n {V} \cs_new_protected_nopar:Npn \spath_close:Nn #1#2 { \@@_close:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_close:Nn {NV} \cs_new_protected_nopar:Npn \spath_close:N #1 { \spath_close:NV #1#1 } \cs_generate_variant:Nn \spath_close:N {c} \cs_new_protected_nopar:Npn \spath_gclose:Nn #1#2 { \@@_close:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gclose:Nn {NV} \cs_new_protected_nopar:Npn \spath_gclose:N #1 { \spath_gclose:NV #1#1 } \cs_generate_variant:Nn \spath_gclose:N {c} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \spath_open:Nn, % \spath_open:N, % \spath_gopen:Nn, % \spath_gopen:N % } % Removes all close paths from the path, replacing them by \Verb+lineto+ if they move any distance. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_open:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_clear:N \l_@@_tmpb_tl \bool_until_do:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_case:NnF \l_@@_tmpc_tl { \c_spath_closepath_tl { \bool_if:nF { \dim_compare_p:n { \l_@@_move_x_dim == \l_@@_tmpa_dim } && \dim_compare_p:n { \l_@@_move_y_dim == \l_@@_tmpb_dim } } { \tl_put_right:NV \l_@@_tmpb_tl \c_spath_lineto_tl \tl_put_right:Nx \l_@@_tmpb_tl { { \dim_use:N \l_@@_move_x_dim } { \dim_use:N \l_@@_move_y_dim } } } \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \c_spath_moveto_tl { \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpc_tl \dim_set:Nn \l_@@_move_x_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_move_y_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpb_tl { { \dim_use:N \l_@@_move_x_dim } { \dim_use:N \l_@@_move_y_dim } } \dim_set_eq:NN \l_@@_tmpa_dim \l_@@_move_x_dim \dim_set_eq:NN \l_@@_tmpb_dim \l_@@_move_y_dim } } { \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpc_tl \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpb_tl { { \dim_use:N \l_@@_tmpa_dim } { \dim_use:N \l_@@_tmpb_dim } } } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_open:Nn #1#2 { \@@_open:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_open:Nn {NV} \cs_new_protected_nopar:Npn \spath_open:N #1 { \spath_open:NV #1#1 } \cs_new_protected_nopar:Npn \spath_gopen:Nn #1#2 { \@@_open:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gopen:Nn {NV} \cs_new_protected_nopar:Npn \spath_gopen:N #1 { \spath_gopen:NV #1#1 } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_remove_empty_components:Nn, % \spath_remove_empty_components:N, % \spath_gremove_empty_components:Nn, % \spath_gremove_empty_components:N % } % Remove any component that is simply a moveto. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_remove_empty_components:n #1 { \group_begin: \spath_components_to_seq:Nn \l_@@_tmpa_seq {#1} \tl_clear:N \l_@@_tmpa_tl \seq_map_inline:Nn \l_@@_tmpa_seq { \int_compare:nF { \tl_count:n {##1} == 3 } { \tl_put_right:Nn \l_@@_tmpa_tl {##1} } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_new_protected_nopar:Npn \spath_remove_empty_components:Nn #1#2 { \@@_remove_empty_components:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_remove_empty_components:Nn {NV} \cs_new_protected_nopar:Npn \spath_remove_empty_components:N #1 { \spath_remove_empty_components:NV #1#1 } \cs_generate_variant:Nn \spath_remove_empty_components:N {c} \cs_new_protected_nopar:Npn \spath_gremove_empty_components:Nn #1#2 { \@@_remove_empty_components:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gremove_empty_components:Nn {NV} \cs_new_protected_nopar:Npn \spath_gremove_empty_components:N #1 { \spath_gremove_empty_components:NV #1#1 } \cs_generate_variant:Nn \spath_gremove_empty_components:N {c} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\spath_if_eq:nn} % Test if two soft paths are equal, we allow a little tolerance on the calculations. % \begin{macrocode} \prg_new_protected_conditional:Npnn \spath_if_eq:nn #1#2 { T, F, TF } { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_set:Nn \l_@@_tmpb_tl {#2} \bool_gset_true:N \g_@@_tmpa_bool \int_compare:nNnTF {\tl_count:N \l_@@_tmpa_tl} = {\tl_count:N \l_@@_tmpb_tl} { \int_step_inline:nnnn {1} {3} {\tl_count:N \l_@@_tmpa_tl} { \tl_set:Nx \l_@@_tmpc_tl {\tl_item:Nn \l_@@_tmpa_tl {##1}} \tl_set:Nx \l_@@_tmpd_tl {\tl_item:Nn \l_@@_tmpb_tl {##1}} \tl_if_eq:NNF \l_@@_tmpc_tl \l_@@_tmpd_tl { \bool_gset_false:N \g_@@_tmpa_bool } \dim_set:Nn \l_@@_tmpa_dim {\tl_item:Nn \l_@@_tmpa_tl {##1+1}} \dim_set:Nn \l_@@_tmpb_dim {\tl_item:Nn \l_@@_tmpb_tl {##1+1}} \dim_compare:nF { \dim_abs:n { \l_@@_tmpa_dim - \l_@@_tmpb_dim } < 0.001pt } { \bool_gset_false:N \g_@@_tmpa_bool } \dim_set:Nn \l_@@_tmpa_dim {\tl_item:Nn \l_@@_tmpa_tl {##1+2}} \dim_set:Nn \l_@@_tmpb_dim {\tl_item:Nn \l_@@_tmpb_tl {##1+2}} \dim_compare:nF { \dim_abs:n { \l_@@_tmpa_dim - \l_@@_tmpb_dim } < 0.001pt } { \bool_gset_false:N \g_@@_tmpa_bool } } } { \bool_gset_false:N \g_@@_tmpa_bool } \group_end: \bool_if:NTF \g_@@_tmpa_bool { \prg_return_true: } { \prg_return_false: } } \prg_generate_conditional_variant:Nnn \spath_if_eq:nn {VV, Vn, nV, vv} {TF, T, F} % \end{macrocode} % \end{macro} % % \subsection{Splitting Commands} % % \begin{macro}[internal]{ % \spath_split_curve:NNnn, % \spath_gsplit_curve:NNnn % } % Splits a Bezier cubic into pieces, storing the pieces in the first two arguments. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_split_curve:nn #1#2 { \group_begin: \tl_set_eq:NN \l_@@_tmpa_tl \c_spath_moveto_tl \tl_put_right:Nx \l_@@_tmpa_tl { {\tl_item:nn {#1} {2}} {\tl_item:nn {#1} {3}} } \tl_put_right:NV \l_@@_tmpa_tl \c_spath_curvetoa_tl \tl_put_right:Nx \l_@@_tmpa_tl { {\fp_to_dim:n { (1 - #2) * \tl_item:nn {#1} {2} + (#2) * \tl_item:nn {#1} {5} }} {\fp_to_dim:n { (1 - #2) * \tl_item:nn {#1} {3} + (#2) * \tl_item:nn {#1} {6} }} } \tl_put_right:NV \l_@@_tmpa_tl \c_spath_curvetob_tl \tl_put_right:Nx \l_@@_tmpa_tl { {\fp_to_dim:n { (1 - #2)^2 * \tl_item:nn {#1} {2} + 2 * (1 - #2) * (#2) * \tl_item:nn {#1} {5} + (#2)^2 * \tl_item:nn {#1} {8} }} {\fp_to_dim:n { (1 - #2)^2 * \tl_item:nn {#1} {3} + 2 * (1 - #2) * (#2) * \tl_item:nn {#1} {6} + (#2)^2 * \tl_item:nn {#1} {9} }} } \tl_put_right:NV \l_@@_tmpa_tl \c_spath_curveto_tl \tl_put_right:Nx \l_@@_tmpa_tl { {\fp_to_dim:n { (1 - #2)^3 * \tl_item:nn {#1} {2} + 3 * (1 - #2)^2 * (#2) * \tl_item:nn {#1} {5} + 3 * (1 - #2) * (#2)^2 * \tl_item:nn {#1} {8} + (#2)^3 * \tl_item:nn {#1} {11} }} {\fp_to_dim:n { (1 - #2)^3 * \tl_item:nn {#1} {3} + 3 * (1 - #2)^2 * (#2) * \tl_item:nn {#1} {6} + 3 * (1 - #2) * (#2)^2 * \tl_item:nn {#1} {9} + (#2)^3 * \tl_item:nn {#1} {12} }} } \tl_gclear:N \g_@@_output_tl \@@_tl_gput_right_braced:NV \g_@@_output_tl \l_@@_tmpa_tl \tl_clear:N \l_@@_tmpa_tl \tl_set_eq:NN \l_@@_tmpa_tl \c_spath_moveto_tl \tl_put_right:Nx \l_@@_tmpa_tl { {\fp_to_dim:n { (1 - #2)^3 * \tl_item:nn {#1} {2} + 3 * (1 - #2)^2 * (#2) * \tl_item:nn {#1} {5} + 3 * (1 - #2) * (#2)^2 * \tl_item:nn {#1} {8} + (#2)^3 * \tl_item:nn {#1} {11} }} {\fp_to_dim:n { (1 - #2)^3 * \tl_item:nn {#1} {3} + 3 * (1 - #2)^2 * (#2) * \tl_item:nn {#1} {6} + 3 * (1 - #2) * (#2)^2 * \tl_item:nn {#1} {9} + (#2)^3 * \tl_item:nn {#1} {12} }} } \tl_put_right:NV \l_@@_tmpa_tl \c_spath_curvetoa_tl \tl_put_right:Nx \l_@@_tmpa_tl { {\fp_to_dim:n { (1 - #2)^2 * \tl_item:nn {#1} {5} + 2 * (1 - #2) * (#2) * \tl_item:nn {#1} {8} + (#2)^2 * \tl_item:nn {#1} {11} }} {\fp_to_dim:n { (1 - #2)^2 * \tl_item:nn {#1} {6} + 2 * (1 - #2) * (#2) * \tl_item:nn {#1} {9} + (#2)^2 * \tl_item:nn {#1} {12} }} } \tl_put_right:NV \l_@@_tmpa_tl \c_spath_curvetob_tl \tl_put_right:Nx \l_@@_tmpa_tl { {\fp_to_dim:n { (1 - #2) * \tl_item:nn {#1} {8} + (#2) * \tl_item:nn {#1} {11} }} {\fp_to_dim:n { (1 - #2) * \tl_item:nn {#1} {9} + (#2) * \tl_item:nn {#1} {12} }} } \tl_put_right:NV \l_@@_tmpa_tl \c_spath_curveto_tl \tl_put_right:Nx \l_@@_tmpa_tl { {\tl_item:nn {#1} {11}} {\tl_item:nn {#1} {12}} } \@@_tl_gput_right_braced:NV \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_generate_variant:Nn \@@_split_curve:nn {nv, nV} \cs_new_protected_nopar:Npn \spath_split_curve:NNnn #1#2#3#4 { \@@_split_curve:nn {#3}{#4} \tl_set:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}} \tl_set:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_curve:NNnn {NNnV, NNVn, NNVV} \cs_new_protected_nopar:Npn \spath_gsplit_curve:NNnn #1#2#3#4 { \@@_split_curve:nn {#3}{#4} \tl_gset:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}} \tl_gset:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_curve:NNnn {NNnV, NNVn, NNVV} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \spath_maybe_split_curve:Nn, % \spath_maybe_gsplit_curve:Nn, % } % Possibly splits a bezier curve to ensure that the pieces don't self-intersect. % Figuring out whether a Bezier cubic self intersects is apparently a difficult problem so we don't bother. % We compute a point such that if there is an intersection then it lies on either side of the point. % I don't recall where the formula came from! % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_maybe_split_curve:n #1 { \group_begin: \fp_set:Nn \l_@@_tmpa_fp { ( \tl_item:nn {#1} {3} - 3 * \tl_item:nn {#1} {6} + 3 * \tl_item:nn {#1} {9} - \tl_item:nn {#1} {12} ) * (3 * \tl_item:nn {#1} {8} - 3 * \tl_item:nn {#1} {11}) - ( \tl_item:nn {#1} {2} - 3 * \tl_item:nn {#1} {5} + 3 * \tl_item:nn {#1} {8} - \tl_item:nn {#1} {11} ) * (3 * \tl_item:nn {#1} {9} - 3 * \tl_item:nn {#1} {12}) } \fp_set:Nn \l_@@_tmpb_fp { ( \tl_item:nn {#1} {2} - 3 * \tl_item:nn {#1} {5} + 3 * \tl_item:nn {#1} {8} - \tl_item:nn {#1} {11} ) * ( 3 * \tl_item:nn {#1} {6} - 6 * \tl_item:nn {#1} {9} + 3 * \tl_item:nn {#1} {12} ) - ( \tl_item:nn {#1} {3} - 3 * \tl_item:nn {#1} {6} + 3 * \tl_item:nn {#1} {9} - \tl_item:nn {#1} {12} ) * ( 3 * \tl_item:nn {#1} {5} - 6 * \tl_item:nn {#1} {8} + 3 * \tl_item:nn {#1} {11} ) } \fp_compare:nTF { \l_@@_tmpb_fp != 0 } { \fp_set:Nn \l_@@_tmpa_fp {.5 * \l_@@_tmpa_fp / \l_@@_tmpb_fp} \fp_compare:nTF { 0 < \l_@@_tmpa_fp && \l_@@_tmpa_fp < 1 } { \@@_split_curve:nV {#1} \l_@@_tmpa_fp } { \tl_gset:Nn \g_@@_output_tl { {#1} {} } } } { \tl_gset:Nn \g_@@_output_tl { {#1} {} } } \group_end: } \cs_new_protected_nopar:Npn \spath_maybe_split_curve:NNn #1#2#3 { \@@_maybe_split_curve:n {#3} \tl_set:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}} \tl_set:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_maybe_split_curve:NNn {NNn, NNV } \cs_new_protected_nopar:Npn \spath_maybe_gsplit_curve:NNn #1#2#3 { \@@_maybe_split_curve:n {#3} \tl_gset:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}} \tl_gset:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_maybe_gsplit_curve:NNn {NNn, NNV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_split_curves:Nn, % \spath_gsplit_curves:Nn, % } % Slurp through the path ensuring that beziers don't self-intersect. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_split_curves:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_clear:N \l_@@_tmpb_tl \tl_clear:N \l_@@_tmpc_tl \bool_do_until:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl} \tl_case:NnF \l_@@_tmpc_tl { \c_spath_curvetoa_tl { \tl_clear:N \l_@@_tmpd_tl \tl_set_eq:NN \l_@@_tmpd_tl \c_spath_moveto_tl \tl_put_right:Nx \l_@@_tmpd_tl { { \dim_use:N \l_@@_tmpa_dim } { \dim_use:N \l_@@_tmpb_dim } } \dim_set:Nn \l_@@_tmpa_dim { \tl_item:Nn \l_@@_tmpa_tl {8} } \dim_set:Nn \l_@@_tmpb_dim { \tl_item:Nn \l_@@_tmpa_tl {9} } \prg_replicate:nn {3} { \tl_put_right:Nx \l_@@_tmpd_tl { \tl_item:Nn \l_@@_tmpa_tl {1} {\tl_item:Nn \l_@@_tmpa_tl {2}} {\tl_item:Nn \l_@@_tmpa_tl {3}} } \prg_replicate:nn {3} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } } \spath_maybe_split_curve:NNV \l_@@_tmpd_tl \l_@@_tmpe_tl \l_@@_tmpd_tl \prg_replicate:nn {3} { \tl_set:Nx \l_@@_tmpd_tl {\tl_tail:N \l_@@_tmpd_tl} \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl} } \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpd_tl \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpe_tl } } { \dim_set:Nn \l_@@_tmpa_dim { \tl_item:Nn \l_@@_tmpa_tl {2} } \dim_set:Nn \l_@@_tmpb_dim { \tl_item:Nn \l_@@_tmpa_tl {3} } \tl_put_right:Nx \l_@@_tmpb_tl { \tl_item:Nn \l_@@_tmpa_tl {1} {\tl_item:Nn \l_@@_tmpa_tl {2}} {\tl_item:Nn \l_@@_tmpa_tl {3}} } \prg_replicate:nn {3} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_split_curves:Nn #1#2 { \@@_split_curves:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_curves:Nn {NV, cV, cn, cv } \cs_new_protected_nopar:Npn \spath_split_curves:N #1 { \spath_split_curves:NV #1#1 } \cs_generate_variant:Nn \spath_split_curves:N {c} \cs_new_protected_nopar:Npn \spath_gsplit_curves:Nn #1#2 { \@@_split_curves:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_curves:Nn {NV, cV, cn, cv } \cs_new_protected_nopar:Npn \spath_gsplit_curves:N #1 { \spath_gsplit_curves:NV #1#1 } \cs_generate_variant:Nn \spath_gsplit_curves:N {c} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_split_line:NNnn, % \spath_gsplit_line:NNnn % } % Splits a line segment. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_split_line:nn #1#2 { \group_begin: \tl_set_eq:NN \l_@@_tmpa_tl \c_spath_moveto_tl \tl_put_right:Nx \l_@@_tmpa_tl { {\tl_item:nn {#1} {2}} {\tl_item:nn {#1} {3}} } \tl_put_right:NV \l_@@_tmpa_tl \c_spath_lineto_tl \tl_put_right:Nx \l_@@_tmpa_tl { {\fp_to_dim:n { (1 - #2) * \tl_item:nn {#1} {2} + (#2) * \tl_item:nn {#1} {5} }} {\fp_to_dim:n { (1 - #2) * \tl_item:nn {#1} {3} + (#2) * \tl_item:nn {#1} {6} }} } \tl_gclear:N \g_@@_output_tl \@@_tl_gput_right_braced:NV \g_@@_output_tl \l_@@_tmpa_tl \tl_clear:N \l_@@_tmpa_tl \tl_set_eq:NN \l_@@_tmpa_tl \c_spath_moveto_tl \tl_put_right:Nx \l_@@_tmpa_tl { {\fp_to_dim:n { (1 - #2) * \tl_item:nn {#1} {2} + (#2) * \tl_item:nn {#1} {5} }} {\fp_to_dim:n { (1 - #2) * \tl_item:nn {#1} {3} + (#2) * \tl_item:nn {#1} {6} }} } \tl_put_right:NV \l_@@_tmpa_tl \c_spath_lineto_tl \tl_put_right:Nx \l_@@_tmpa_tl { {\tl_item:nn {#1} {5}} {\tl_item:nn {#1} {6}} } \@@_tl_gput_right_braced:NV \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_new_protected_nopar:Npn \spath_split_line:NNnn #1#2#3#4 { \@@_split_line:nn {#3}{#4} \tl_set:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}} \tl_set:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_line:NNnn {NNnV, NNVn, NNVV} \cs_new_protected_nopar:Npn \spath_gsplit_line:NNnn #1#2#3#4 { \@@_split_line:nn {#3}{#4} \tl_gset:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}} \tl_gset:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_line:NNnn {NNnV, NNVn, NNVV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_split_at:NNnn, % \spath_split_at:Nnn, % \spath_split_at:Nn, % \spath_gsplit_at:NNnn % \spath_gsplit_at:Nnn, % \spath_gsplit_at:Nn % } % Split a path according to the parameter generated by the intersection routine. % The versions with two \texttt{N} arguments stores the two parts in two macros, the version with a single \texttt{N} joins them back into a single path (as separate components). % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_split_at:nn #1#2 { \group_begin: \int_set:Nn \l_@@_tmpa_int {\fp_to_int:n {floor(#2) + 1}} \fp_set:Nn \l_@@_tmpa_fp {#2 - floor(#2)} % Is split point near one end or other of a component? \fp_compare:nT { \l_@@_tmpa_fp < 0.01 } { % Near the start, so we'll place it at the start \fp_set:Nn \l_@@_tmpa_fp {0} } \fp_compare:nT { \l_@@_tmpa_fp > 0.99 } { % Near the end, so we'll place it at the end \fp_set:Nn \l_@@_tmpa_fp {0} \int_incr:N \l_@@_tmpa_int } \int_zero:N \l_@@_tmpb_int \bool_set_true:N \l_@@_tmpa_bool \tl_set:Nn \l_@@_tmpe_tl {#1} \tl_clear:N \l_@@_tmpc_tl \dim_zero:N \l_@@_tmpa_dim \dim_zero:N \l_@@_tmpb_dim \bool_until_do:nn { \tl_if_empty_p:N \l_@@_tmpe_tl || \int_compare_p:n { \l_@@_tmpa_int == \l_@@_tmpb_int } } { \tl_set:Nx \l_@@_tmpf_tl {\tl_head:N \l_@@_tmpe_tl} \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl } \tl_case:Nn \l_@@_tmpf_tl { \c_spath_lineto_tl { \int_incr:N \l_@@_tmpb_int } \c_spath_curvetoa_tl { \int_incr:N \l_@@_tmpb_int } } \int_compare:nT { \l_@@_tmpb_int < \l_@@_tmpa_int } { \tl_put_right:NV \l_@@_tmpc_tl \l_@@_tmpf_tl \tl_put_right:Nx \l_@@_tmpc_tl {{ \tl_head:N \l_@@_tmpe_tl }} \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpe_tl} \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl } \tl_put_right:Nx \l_@@_tmpc_tl {{ \tl_head:N \l_@@_tmpe_tl }} \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpe_tl} \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl } } } \tl_clear:N \l_@@_tmpd_tl \tl_put_right:NV \l_@@_tmpd_tl \c_spath_moveto_tl \tl_put_right:Nx \l_@@_tmpd_tl { {\dim_use:N \l_@@_tmpa_dim} {\dim_use:N \l_@@_tmpb_dim} } \fp_compare:nTF { \l_@@_tmpa_fp == 0 } { \tl_set_eq:NN \l_@@_tmpb_tl \l_@@_tmpd_tl \tl_if_empty:NF \l_@@_tmpe_tl { \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpf_tl \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpe_tl } } { \tl_case:Nn \l_@@_tmpf_tl { \c_spath_lineto_tl { \tl_put_right:NV \l_@@_tmpd_tl \l_@@_tmpf_tl \tl_put_right:Nx \l_@@_tmpd_tl {{ \tl_head:N \l_@@_tmpe_tl }} \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl } \tl_put_right:Nx \l_@@_tmpd_tl {{ \tl_head:N \l_@@_tmpe_tl }} \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl } \spath_split_line:NNVV \l_@@_tmpa_tl \l_@@_tmpb_tl \l_@@_tmpd_tl \l_@@_tmpa_fp \prg_replicate:nn {3} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \tl_put_right:NV \l_@@_tmpc_tl \l_@@_tmpa_tl \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpe_tl } \c_spath_curvetoa_tl { \tl_put_right:NV \l_@@_tmpd_tl \l_@@_tmpf_tl \tl_put_right:Nx \l_@@_tmpd_tl {{ \tl_head:N \l_@@_tmpe_tl }} \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl } \tl_put_right:Nx \l_@@_tmpd_tl {{ \tl_head:N \l_@@_tmpe_tl }} \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl } \prg_replicate:nn {2} { \tl_put_right:Nx \l_@@_tmpd_tl { \tl_head:N \l_@@_tmpe_tl } \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl } \tl_put_right:Nx \l_@@_tmpd_tl {{ \tl_head:N \l_@@_tmpe_tl }} \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl } \tl_put_right:Nx \l_@@_tmpd_tl {{ \tl_head:N \l_@@_tmpe_tl }} \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl } } \spath_split_curve:NNVV \l_@@_tmpa_tl \l_@@_tmpb_tl \l_@@_tmpd_tl \l_@@_tmpa_fp \prg_replicate:nn {3} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \tl_put_right:NV \l_@@_tmpc_tl \l_@@_tmpa_tl \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpe_tl } } } \tl_gclear:N \g_@@_output_tl \@@_tl_gput_right_braced:NV \g_@@_output_tl \l_@@_tmpc_tl \@@_tl_gput_right_braced:NV \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_split_at:NNnn #1#2#3#4 { \@@_split_at:nn {#3}{#4} \tl_set:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}} \tl_set:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_at:NNnn {NNVn, NNVV, NNnV} \cs_new_protected_nopar:Npn \spath_gsplit_at:NNnn #1#2#3#4 { \@@_split_at:nn {#3}{#4} \tl_gset:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}} \tl_gset:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_at:NNnn {NNVn, NNVV, NNnV} % \end{macrocode} % % % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_split_at:Nnn #1#2#3 { \@@_split_at:nn {#2}{#3} \tl_set:Nx #1 { \tl_item:Nn \g_@@_output_tl {1} \tl_item:Nn \g_@@_output_tl {2} } \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_at:Nnn {NVn, NVV} \cs_new_protected_nopar:Npn \spath_split_at:Nn #1#2 { \spath_split_at:NVn #1#1{#2} } \cs_new_protected_nopar:Npn \spath_gsplit_at:Nnn #1#2#3 { \@@_split_at:nn {#2}{#3} \tl_gset:Nx #1 { \tl_item:Nn \g_@@_output_tl {1} \tl_item:Nn \g_@@_output_tl {2} } \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_at:Nnn {NVn, NVV} \cs_new_protected_nopar:Npn \spath_gsplit_at:Nn #1#2 { \spath_gsplit_at:NVn #1#1{#2} } % \end{macrocode} % \end{macro} % % % \subsection{Shortening Paths} % % This code relates to shortening paths. % For curved paths, the routine uses the derivative at the end to figure out how far back to shorten. % This means that the actual length that it shortens by is approximate, but it is guaranteed to be along its length. % % As in the previous section, there are various versions. % In particular, there are versions where the path can be specified by a macro and is saved back into that macro. % % \begin{macro}[internal]{ % \spath_shorten_at_end:Nnn % } % This macro shortens a path from the end by a dimension. % % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_shorten_at_end:nn #1#2 { \int_compare:nTF { \tl_count:n {#1} > 3 } { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_reverse:N \l_@@_tmpa_tl \tl_set:Nx \l_@@_tmpb_tl {\tl_item:Nn \l_@@_tmpa_tl {3}} \tl_clear:N \l_@@_tmpe_tl \tl_if_eq:NNTF \l_@@_tmpb_tl \c_spath_curveto_tl { \int_set:Nn \l_@@_tmpa_int {3} } { \int_set:Nn \l_@@_tmpa_int {1} } \prg_replicate:nn { \l_@@_tmpa_int } { \tl_put_right:Nx \l_@@_tmpe_tl { {\tl_head:N \l_@@_tmpa_tl} } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpe_tl { {\tl_head:N \l_@@_tmpa_tl} } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpe_tl { \tl_head:N \l_@@_tmpa_tl } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \tl_put_right:Nx \l_@@_tmpe_tl { {\tl_item:Nn \l_@@_tmpa_tl {1}} {\tl_item:Nn \l_@@_tmpa_tl {2}} } \tl_put_right:NV \l_@@_tmpe_tl \c_spath_moveto_tl \tl_reverse:N \l_@@_tmpa_tl \fp_set:Nn \l_@@_tmpa_fp { \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {4}} - \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {1}} } \fp_set:Nn \l_@@_tmpb_fp { \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {5}} - \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {2}} } \fp_set:Nn \l_@@_tmpc_fp { sqrt( \l_@@_tmpa_fp * \l_@@_tmpa_fp + \l_@@_tmpb_fp * \l_@@_tmpb_fp ) * \l_@@_tmpa_int } \fp_compare:nTF { \l_@@_tmpc_fp > #2 } { \fp_set:Nn \l_@@_tmpc_fp { (\l_@@_tmpc_fp - #2)/ \l_@@_tmpc_fp } \tl_reverse:N \l_@@_tmpe_tl \tl_if_eq:NNTF \l_@@_tmpb_tl \c_spath_curveto_tl { \spath_split_curve:NNVV \l_@@_tmpc_tl \l_@@_tmpd_tl \l_@@_tmpe_tl \l_@@_tmpc_fp } { \spath_split_line:NNVV \l_@@_tmpc_tl \l_@@_tmpd_tl \l_@@_tmpe_tl \l_@@_tmpc_fp } \prg_replicate:nn {3} { \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} } \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpc_tl } { \int_compare:nT { \tl_count:N \l_@@_tmpa_tl > 3 } { \dim_set:Nn \l_@@_tmpa_dim {\fp_to_dim:n {#2 - \l_@@_tmpc_fp } } \spath_shorten_at_end:NV \l_@@_tmpa_tl \l_@@_tmpa_dim } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } { \tl_gset:Nn \g_@@_output_tl {#1} } } \cs_new_protected_nopar:Npn \spath_shorten_at_end:Nnn #1#2#3 { \@@_shorten_at_end:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_shorten_at_end:Nnn {NVV, cnn, cVV, NVn} \cs_new_protected_nopar:Npn \spath_shorten_at_end:Nn #1#2 { \spath_shorten_at_end:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_shorten_at_end:Nn {cn, cV, NV} \cs_new_protected_nopar:Npn \spath_gshorten_at_end:Nnn #1#2#3 { \@@_shorten_at_end:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gshorten_at_end:Nnn {NVV, cnn, cVV, NVn} \cs_new_protected_nopar:Npn \spath_gshorten_at_end:Nn #1#2 { \spath_gshorten_at_end:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gshorten_at_end:Nn {cn, cV, NV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_shorten_at_start:Nnn, % \spath_shorten_at_start:Nn, % \spath_gshorten_at_start:Nnn, % \spath_gshorten_at_start:Nn % } % % This macro shortens a path from the start by a dimension. % % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_shorten_at_start:nn #1#2 { \int_compare:nTF { \tl_count:n {#1} > 3 } { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_set:Nx \l_@@_tmpb_tl {\tl_item:Nn \l_@@_tmpa_tl {4}} \tl_clear:N \l_@@_tmpe_tl \tl_if_eq:NNTF \l_@@_tmpb_tl \c_spath_curvetoa_tl { \int_set:Nn \l_@@_tmpa_int {3} } { \int_set:Nn \l_@@_tmpa_int {1} } \tl_set_eq:NN \l_@@_tmpe_tl \c_spath_moveto_tl \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl } \prg_replicate:nn { \l_@@_tmpa_int } { \@@_tl_put_right_braced:Nx \l_@@_tmpe_tl {\tl_item:Nn \l_@@_tmpa_tl {1}} \@@_tl_put_right_braced:Nx \l_@@_tmpe_tl {\tl_item:Nn \l_@@_tmpa_tl {2}} \tl_put_right:Nx \l_@@_tmpe_tl {\tl_item:Nn \l_@@_tmpa_tl {3}} \prg_replicate:nn {3} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl } } } \@@_tl_put_right_braced:Nx \l_@@_tmpe_tl {\tl_item:Nn \l_@@_tmpa_tl {1}} \@@_tl_put_right_braced:Nx \l_@@_tmpe_tl {\tl_item:Nn \l_@@_tmpa_tl {2}} \fp_set:Nn \l_@@_tmpa_fp { \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {5}} - \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {2}} } \fp_set:Nn \l_@@_tmpb_fp { \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {6}} - \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {3}} } \fp_set:Nn \l_@@_tmpc_fp { sqrt( \l_@@_tmpa_fp * \l_@@_tmpa_fp + \l_@@_tmpb_fp * \l_@@_tmpb_fp ) * \l_@@_tmpa_int } \fp_compare:nTF { \l_@@_tmpc_fp > #2 } { \fp_set:Nn \l_@@_tmpc_fp { #2/ \l_@@_tmpc_fp } \tl_if_eq:NNTF \l_@@_tmpb_tl \c_spath_curvetoa_tl { \spath_split_curve:NNVV \l_@@_tmpc_tl \l_@@_tmpd_tl \l_@@_tmpe_tl \l_@@_tmpc_fp } { \spath_split_line:NNVV \l_@@_tmpc_tl \l_@@_tmpd_tl \l_@@_tmpe_tl \l_@@_tmpc_fp } \prg_replicate:nn {2} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \tl_put_left:NV \l_@@_tmpa_tl \l_@@_tmpd_tl } { \tl_put_left:NV \l_@@_tmpa_tl \c_spath_moveto_tl \int_compare:nT { \tl_count:N \l_@@_tmpa_tl > 3 } { \dim_set:Nn \l_@@_tmpa_dim {\fp_to_dim:n {#2 - \l_@@_tmpc_fp } } \spath_shorten_at_start:NV \l_@@_tmpa_tl \l_@@_tmpa_dim } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } { \tl_gset:Nn \g_@@_output_tl {#1} } } \cs_new_protected_nopar:Npn \spath_shorten_at_start:Nnn #1#2#3 { \@@_shorten_at_start:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_shorten_at_start:Nnn {NVV, cnn, cVV, NVn} \cs_new_protected_nopar:Npn \spath_shorten_at_start:Nn #1#2 { \spath_shorten_at_start:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_shorten_at_start:Nn {cn, cV, NV} \cs_new_protected_nopar:Npn \spath_gshorten_at_start:Nnn #1#2#3 { \@@_shorten_at_start:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gshorten_at_start:Nnn {NVV, cnn, cVV, NVn} \cs_new_protected_nopar:Npn \spath_gshorten_at_start:Nn #1#2 { \spath_gshorten_at_start:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gshorten_at_start:Nn {cn, cV, NV} % \end{macrocode} % \end{macro} % % % \subsection{Points on a Path} % % \begin{macro}[internal]{ % \spath_point_at:Nnn, % \spath_gpoint_at:Nnn, % } % % Get the location of a point on a path, using the same location specification as the intersection library. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_point_at:nn #1#2 { \group_begin: \int_set:Nn \l_@@_tmpa_int {\fp_to_int:n {floor(#2) + 1}} \fp_set:Nn \l_@@_tmpa_fp {#2 - floor(#2)} \spath_segments_to_seq:Nn \l_@@_tmpa_seq {#1} \int_compare:nTF { \l_@@_tmpa_int < 1 } { \spath_initialpoint:Nn \l_@@_tmpc_tl {#1} } { \int_compare:nTF { \l_@@_tmpa_int > \seq_count:N \l_@@_tmpa_seq } { \spath_finalpoint:Nn \l_@@_tmpc_tl {#1} } { \tl_set:Nx \l_@@_tmpa_tl {\seq_item:Nn \l_@@_tmpa_seq { \l_@@_tmpa_int} } \int_compare:nTF { \tl_count:N \l_@@_tmpa_tl > 3 } { \tl_set:Nx \l_@@_tmpb_tl {\tl_item:Nn \l_@@_tmpa_tl {4}} } { \tl_set:Nx \l_@@_tmpb_tl {\tl_item:Nn \l_@@_tmpa_tl {1}} } \tl_clear:N \l_@@_tmpc_tl \tl_case:Nn \l_@@_tmpb_tl { \c_spath_moveto_tl { \tl_set:Nx \l_@@_tmpc_tl { { \tl_item:Nn \l_@@_tmpa_tl {2} } { \tl_item:Nn \l_@@_tmpa_tl {3} } } } \c_spath_lineto_tl { \tl_set:Nx \l_@@_tmpc_tl { {\fp_to_dim:n { (1 - \l_@@_tmpa_fp) * ( \tl_item:Nn \l_@@_tmpa_tl {2} ) + \l_@@_tmpa_fp * ( \tl_item:Nn \l_@@_tmpa_tl {5} ) } } {\fp_to_dim:n { (1 - \l_@@_tmpa_fp) * ( \tl_item:Nn \l_@@_tmpa_tl {3} ) + \l_@@_tmpa_fp * ( \tl_item:Nn \l_@@_tmpa_tl {6} ) } } } } \c_spath_closepath_tl { \tl_set:Nx \l_@@_tmpc_tl { {\fp_to_dim:n { (1 - \l_@@_tmpa_fp) * ( \tl_item:Nn \l_@@_tmpa_tl {2} ) + \l_@@_tmpa_fp * ( \tl_item:Nn \l_@@_tmpa_tl {5} ) } } {\fp_to_dim:n { (1 - \l_@@_tmpa_fp) * ( \tl_item:Nn \l_@@_tmpa_tl {3} ) + \l_@@_tmpa_fp * ( \tl_item:Nn \l_@@_tmpa_tl {6} ) } } } } \c_spath_curvetoa_tl { \tl_set:Nx \l_@@_tmpc_tl { {\fp_to_dim:n { (1 - \l_@@_tmpa_fp)^3 * \tl_item:Nn \l_@@_tmpa_tl {2} + 3 * (1 - \l_@@_tmpa_fp)^2 * (\l_@@_tmpa_fp) * \tl_item:Nn \l_@@_tmpa_tl {5} + 3 * (1 - \l_@@_tmpa_fp) * (\l_@@_tmpa_fp)^2 * \tl_item:Nn \l_@@_tmpa_tl {8} + (\l_@@_tmpa_fp)^3 * \tl_item:Nn \l_@@_tmpa_tl {11} }} {\fp_to_dim:n { (1 - \l_@@_tmpa_fp)^3 * \tl_item:Nn \l_@@_tmpa_tl {3} + 3 * (1 - \l_@@_tmpa_fp)^2 * (\l_@@_tmpa_fp) * \tl_item:Nn \l_@@_tmpa_tl {6} + 3 * (1 - \l_@@_tmpa_fp) * (\l_@@_tmpa_fp)^2 * \tl_item:Nn \l_@@_tmpa_tl {9} + (\l_@@_tmpa_fp)^3 * \tl_item:Nn \l_@@_tmpa_tl {12} }} } } } } } \tl_gclear:N \g_@@_output_tl \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpc_tl \group_end: } \cs_new_protected_nopar:Npn \spath_point_at:Nnn #1#2#3 { \@@_point_at:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_point_at:Nnn {NVn, NVV, NnV} \cs_new_protected_nopar:Npn \spath_gpoint_at:Nnn #1#2#3 { \@@_point_at:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gpoint_at:Nnn {NVn, NVV, NnV} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \spath_tangent_at:Nnn, % \spath_gtangent_at:Nnn, % } % % Get the tangent at a point on a path, using the same location specification as the intersection library. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_tangent_at:nn #1#2 { \group_begin: \int_set:Nn \l_@@_tmpa_int {\fp_to_int:n {floor(#2) + 1}} \fp_set:Nn \l_@@_tmpa_fp {#2 - floor(#2)} \spath_segments_to_seq:Nn \l_@@_tmpa_seq {#1} \int_compare:nTF { \l_@@_tmpa_int < 1 } { \spath_initialpoint:Nn \l_@@_tmpc_tl {#1} } { \int_compare:nTF { \l_@@_tmpa_int > \seq_count:N \l_@@_tmpa_seq } { \spath_finalpoint:Nn \l_@@_tmpc_tl {#1} } { \tl_set:Nx \l_@@_tmpa_tl {\seq_item:Nn \l_@@_tmpa_seq { \l_@@_tmpa_int} } \int_compare:nTF { \tl_count:N \l_@@_tmpa_tl > 3 } { \tl_set:Nx \l_@@_tmpb_tl {\tl_item:Nn \l_@@_tmpa_tl {4}} } { \tl_set:Nx \l_@@_tmpb_tl {\tl_item:Nn \l_@@_tmpa_tl {1}} } \tl_clear:N \l_@@_tmpc_tl \tl_case:Nn \l_@@_tmpb_tl { \c_spath_moveto_tl { \tl_set:Nx \l_@@_tmpc_tl { { \tl_item:Nn \l_@@_tmpa_tl {2} } { \tl_item:Nn \l_@@_tmpa_tl {3} } } } \c_spath_lineto_tl { \tl_set:Nx \l_@@_tmpc_tl { {\fp_to_dim:n { ( \tl_item:Nn \l_@@_tmpa_tl {5} ) - ( \tl_item:Nn \l_@@_tmpa_tl {2} ) } } {\fp_to_dim:n { ( \tl_item:Nn \l_@@_tmpa_tl {6} ) - ( \tl_item:Nn \l_@@_tmpa_tl {3} ) } } } } \c_spath_closepath_tl { \tl_set:Nx \l_@@_tmpc_tl { {\fp_to_dim:n { ( \tl_item:Nn \l_@@_tmpa_tl {5} ) - ( \tl_item:Nn \l_@@_tmpa_tl {2} ) } } {\fp_to_dim:n { ( \tl_item:Nn \l_@@_tmpa_tl {6} ) - ( \tl_item:Nn \l_@@_tmpa_tl {3} ) } } } } \c_spath_curvetoa_tl { \tl_set:Nx \l_@@_tmpc_tl { {\fp_to_dim:n { 3*(1 - \l_@@_tmpa_fp)^2 * (\tl_item:Nn \l_@@_tmpa_tl {5} - \tl_item:Nn \l_@@_tmpa_tl {2}) + 6 * (1 - \l_@@_tmpa_fp) * (\l_@@_tmpa_fp) * (\tl_item:Nn \l_@@_tmpa_tl {8} - \tl_item:Nn \l_@@_tmpa_tl {5}) + 3*(\l_@@_tmpa_fp)^2 * (\tl_item:Nn \l_@@_tmpa_tl {11} - \tl_item:Nn \l_@@_tmpa_tl {8}) } } {\fp_to_dim:n { 3*(1 - \l_@@_tmpa_fp)^2 * (\tl_item:Nn \l_@@_tmpa_tl {6} - \tl_item:Nn \l_@@_tmpa_tl {3}) + 6 * (1 - \l_@@_tmpa_fp) * (\l_@@_tmpa_fp) * (\tl_item:Nn \l_@@_tmpa_tl {9} - \tl_item:Nn \l_@@_tmpa_tl {6}) + 3*(\l_@@_tmpa_fp)^2 * (\tl_item:Nn \l_@@_tmpa_tl {12} - \tl_item:Nn \l_@@_tmpa_tl {9}) }} } } } } } \tl_gclear:N \g_@@_output_tl \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpc_tl \group_end: } \cs_new_protected_nopar:Npn \spath_tangent_at:Nnn #1#2#3 { \@@_tangent_at:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_tangent_at:Nnn {NVn, NVV, NnV} \cs_new_protected_nopar:Npn \spath_gtangent_at:Nnn #1#2#3 { \@@_tangent_at:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gtangent_at:Nnn {NVn, NVV, NnV} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \spath_transformation_at:Nnn, % \spath_gtransformation_at:Nnn % } % Gets a transformation that will align to a point on the path with the x-axis along the path. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_transformation_at:nn #1#2 { \group_begin: \tl_clear:N \l_@@_tmpa_tl \@@_tangent_at:nn {#1}{#2} \tl_set_eq:NN \l_@@_tmpb_tl \g_@@_output_tl \fp_set:Nn \l_@@_tmpa_fp { sqrt( (\tl_item:Nn \l_@@_tmpb_tl {1})^2 + (\tl_item:Nn \l_@@_tmpb_tl {2})^2 ) } \fp_compare:nTF {\l_@@_tmpa_fp = 0} { \fp_set:Nn \l_@@_tmpa_fp {1} \fp_set:Nn \l_@@_tmpb_fp {0} } { \fp_set:Nn \l_@@_tmpb_fp { (\tl_item:Nn \l_@@_tmpb_tl {2}) / \l_@@_tmpa_fp } \fp_set:Nn \l_@@_tmpa_fp { (\tl_item:Nn \l_@@_tmpb_tl {1}) / \l_@@_tmpa_fp } } \tl_set:Nx \l_@@_tmpa_tl { { \fp_to_decimal:n { \l_@@_tmpa_fp } } { \fp_to_decimal:n { \l_@@_tmpb_fp } } { \fp_to_decimal:n {- \l_@@_tmpb_fp } } { \fp_to_decimal:n { \l_@@_tmpa_fp } } } \@@_point_at:nn {#1}{#2} \tl_put_right:NV \l_@@_tmpa_tl \g_@@_output_tl \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_new_protected_nopar:Npn \spath_transformation_at:Nnn #1#2#3 { \@@_transformation_at:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_transformation_at:Nnn {NVn, NVV, NnV, NvV} \cs_new_protected_nopar:Npn \spath_gtransformation_at:Nnn #1#2#3 { \@@_transformation_at:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gtransformation_at:Nnn {NVn, NVV, NnV} % \end{macrocode} % \end{macro} % % \subsection{Intersection Routines} % % Note: I'm not consistent with number schemes. % The intersection library is 0-based, but the user interface is 1-based (since if we ``count'' in a \Verb+\foreach+ then it starts at 1). % This should be more consistent. % % \begin{macro}[internal]{ % \spath_intersect:NN, % \spath_intersect:nn, % } % Pass two spaths to pgf's intersection routine. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_intersect:NN #1#2 { \pgfintersectionofpaths% {% \pgfsetpath #1 }{% \pgfsetpath #2 } } \cs_new_protected_nopar:Npn \spath_intersect:nn #1#2 { \tl_set:Nn \l_@@_intersecta_tl {#1} \tl_set:Nn \l_@@_intersectb_tl {#2} \spath_intersect:NN \l_@@_intersecta_tl \l_@@_intersectb_tl } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_split_component_at_intersections:Nnn % } % Split a component where it intersects a path. % Key assumption is that the first path is a single component, so if it is closed then the end joins up to the beginning. % The component is modified but the path is not. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_split_component_at_intersections:nn #1#2 { \group_begin: \tl_clear:N \l_@@_tmpe_tl \seq_clear:N \l_@@_tmpb_seq % Find the intersections of these segments \tl_set:Nn \l_@@_tmpb_tl {#1} \tl_set:Nn \l_@@_tmpc_tl {#2} \spath_reallength:Nn \l_@@_tmpa_int {#1} % Remember if the component is closed \spath_finalaction:NV \l_@@_tmpa_tl \l_@@_tmpb_tl \bool_set:Nn \l_@@_closed_bool { \tl_if_eq_p:NN \l_@@_tmpa_tl \c_spath_closepath_tl } % Open it \spath_open:N \l_@@_tmpb_tl % Sort intersections along the component \pgfintersectionsortbyfirstpath \spath_intersect:NN \l_@@_tmpb_tl \l_@@_tmpc_tl % If we get intersections \int_compare:nT {\pgfintersectionsolutions > 0} { % Find the times of the intersections on the component \int_step_inline:nnnn {1} {1} {\pgfintersectionsolutions} { \pgfintersectiongetsolutiontimes{##1}{\l_@@_tmph_tl}{\l_@@_tmpi_tl} \seq_put_left:NV \l_@@_tmpb_seq \l_@@_tmph_tl } \seq_get_left:NN \l_@@_tmpb_seq \l_@@_tmpa_tl \fp_compare:nT { \l_@@_tmpa_tl > \l_@@_tmpa_int - .01 } { \bool_set_false:N \l_@@_closed_bool } \seq_get_right:NN \l_@@_tmpb_seq \l_@@_tmpa_tl \fp_compare:nT { \l_@@_tmpa_tl < .01 } { \bool_set_false:N \l_@@_closed_bool } \tl_set:Nn \l_@@_tmpg_tl {-1} \seq_map_inline:Nn \l_@@_tmpb_seq { \tl_set:Nn \l_@@_tmph_tl {##1} \tl_set_eq:NN \l_@@_tmpa_tl \l_@@_tmph_tl \int_compare:nT { \fp_to_int:n {floor( \l_@@_tmph_tl) } = \fp_to_int:n {floor( \l_@@_tmpg_tl) } } { \tl_set:Nx \l_@@_tmph_tl { \fp_eval:n { floor( \l_@@_tmph_tl ) + ( \l_@@_tmph_tl - floor( \l_@@_tmph_tl) ) / ( \l_@@_tmpg_tl - floor( \l_@@_tmpg_tl) ) } } } \tl_set_eq:NN \l_@@_tmpg_tl \l_@@_tmpa_tl \spath_split_at:NNVV \l_@@_tmpd_tl \l_@@_tmpf_tl \l_@@_tmpb_tl \l_@@_tmph_tl \tl_put_left:NV \l_@@_tmpe_tl \l_@@_tmpf_tl \tl_set_eq:NN \l_@@_tmpb_tl \l_@@_tmpd_tl } \tl_put_left:NV \l_@@_tmpe_tl \l_@@_tmpb_tl \spath_remove_empty_components:N \l_@@_tmpe_tl \tl_set_eq:NN \l_@@_tmpb_tl \l_@@_tmpe_tl } \bool_if:NT \l_@@_closed_bool { \spath_join_component:Nn \l_@@_tmpb_tl {1} } \tl_gclear:N \g_@@_output_tl \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_split_component_at_intersections:Nnn #1#2#3 { \@@_split_component_at_intersections:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_component_at_intersections:Nnn {NVn, NVV} \cs_new_protected_nopar:Npn \spath_split_component_at_intersections:Nn #1#2 { \spath_split_component_at_intersections:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_split_component_at_intersections:Nn {cn, cv} \cs_new_protected_nopar:Npn \spath_gsplit_component_at_intersections:Nnn #1#2#3 { \@@_split_component_at_intersections:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_component_at_intersections:Nnn {NVn, NVV} \cs_new_protected_nopar:Npn \spath_gsplit_component_at_intersections:Nn #1#2 { \spath_gsplit_component_at_intersections:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gsplit_component_at_intersections:Nn {cn, cv} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \spath_split_path_at_intersections:Nnn, % \spath_split_path_at_intersections:Nn, % \spath_gsplit_path_at_intersections:Nnn, % \spath_gsplit_path_at_intersections:Nn, % \spath_split_at_intersections:NNnn, % \spath_split_at_intersections:NN, % \spath_gsplit_at_intersections:NNnn, % \spath_gsplit_at_intersections:NN, % } % Split paths at their intersections. % The \Verb+path+ versions only split the first path. % The others split both paths. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_split_path_at_intersections:nn #1#2 { \group_begin: \seq_clear:N \l_@@_tmpa_seq \seq_clear:N \l_@@_tmpb_seq \spath_components_to_seq:Nn \l_@@_tmpa_seq {#1} \seq_map_inline:Nn \l_@@_tmpa_seq { \spath_split_component_at_intersections:Nnn \l_@@_tmpa_tl {##1} {#2} \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpa_tl } \tl_gclear:N \g_@@_output_tl \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpb_seq {} } \group_end: } \cs_new_protected_nopar:Npn \spath_split_path_at_intersections:Nnn #1#2#3 { \@@_split_path_at_intersections:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_path_at_intersections:Nnn {NVn, NVV, cVn, cVV, cvn, cvv} \cs_new_protected_nopar:Npn \spath_split_path_at_intersections:Nn #1#2 { \spath_split_path_at_intersections:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_split_path_at_intersections:Nn {cv, NV} \cs_new_protected_nopar:Npn \spath_gsplit_path_at_intersections:Nnn #1#2#3 { \@@_split_path_at_intersections:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_path_at_intersections:Nnn {NVn, NVV, cVn, cVV, cvn, cvv} \cs_new_protected_nopar:Npn \spath_gsplit_path_at_intersections:Nn #1#2 { \spath_gsplit_path_at_intersections:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gsplit_path_at_intersections:Nn {cv, NV} \cs_new_protected_nopar:Npn \spath_split_at_intersections:NNnn #1#2#3#4 { \@@_split_path_at_intersections:nn {#3}{#4} \tl_set_eq:NN #1 \g_@@_output_tl \@@_split_path_at_intersections:nn {#4}{#3} \tl_set_eq:NN #2 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_at_intersections:NNnn {NNVn, NNVV, ccVn, ccVV, ccvn, ccvv} \cs_new_protected_nopar:Npn \spath_split_at_intersections:NN #1#2 { \spath_split_at_intersections:NNVV #1#2#1#2 } \cs_generate_variant:Nn \spath_split_at_intersections:NN {cc} \cs_new_protected_nopar:Npn \spath_gsplit_at_intersections:NNnn #1#2#3#4 { \@@_split_path_at_intersections:nn {#3}{#4} \tl_gset_eq:NN #1 \g_@@_output_tl \@@_split_path_at_intersections:nn {#4}{#3} \tl_gset_eq:NN #2 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_at_intersections:NNnn {NNVn, NNVV, ccVn, ccVV, ccvn, ccvv} \cs_new_protected_nopar:Npn \spath_gsplit_at_intersections:NN #1#2 { \spath_gsplit_at_intersections:NNVV #1#2#1#2 } \cs_generate_variant:Nn \spath_gsplit_at_intersections:NN {cc} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \spath_split_component_at_self_intersections:Nn, % \spath_split_component_at_self_intersections:N, % \spath_gsplit_component_at_self_intersections:Nn, % \spath_gsplit_component_at_self_intersections:N, % } % Given a component of a path, split it at points where it self-intersects. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_split_component_at_self_intersections:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpe_tl {#1} % Remember if the component is closed \spath_finalaction:NV \l_@@_tmpa_tl \l_@@_tmpe_tl \bool_set:Nn \l_@@_closed_bool { \tl_if_eq_p:NN \l_@@_tmpa_tl \c_spath_closepath_tl } % Copy the path \tl_set:Nn \l_@@_tmpe_tl {#1} % Open the path \spath_open:N \l_@@_tmpe_tl % Ensure beziers don't self-intersect \spath_split_curves:N \l_@@_tmpe_tl % Make a copy for later \tl_set_eq:NN \l_@@_tmpg_tl \l_@@_tmpe_tl % Clear some token lists and sequences \tl_clear:N \l_@@_tmpd_tl \seq_clear:N \l_@@_tmpb_seq \int_zero:N \l_@@_tmpa_int \pgfintersectionsortbyfirstpath % Split the path into a sequence of segments \spath_segments_to_seq:NV \l_@@_tmpa_seq \l_@@_tmpe_tl \seq_map_indexed_inline:Nn \l_@@_tmpa_seq { \seq_map_indexed_inline:Nn \l_@@_tmpa_seq { % Don't intersect a segment with itself \int_compare:nF { ##1 == ####1 } { \spath_intersect:nn {##2} {####2} \int_compare:nT {\pgfintersectionsolutions > 0} { % Find the times of the intersections on each path \int_step_inline:nnnn {1} {1} {\pgfintersectionsolutions} { \pgfintersectiongetsolutiontimes {########1}{\l_@@_tmpb_tl}{\l_@@_tmpc_tl} \bool_if:nT { !( \fp_compare_p:n { \l_@@_tmpb_tl > .99 } && \int_compare_p:n {##1 + 1 == ####1} ) && !( \fp_compare_p:n { \l_@@_tmpb_tl < .01 } && \int_compare_p:n {##1 - 1 == ####1} ) && !( \l_@@_closed_bool && \fp_compare_p:n { \l_@@_tmpb_tl < .01 } && \int_compare_p:n {##1 == 1} && \int_compare_p:n {\seq_count:N \l_@@_tmpa_seq == ####1} ) && !( \l_@@_closed_bool && \fp_compare_p:n { \l_@@_tmpb_tl > .99 } && \int_compare_p:n {####1 == 1} && \int_compare_p:n {\seq_count:N \l_@@_tmpa_seq == ##1} ) } { \tl_set:Nx \l_@@_tmpa_tl {\fp_to_decimal:n {\l_@@_tmpb_tl + ##1 - 1}} \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpa_tl } } } } } } % Sort the sequence by reverse order along the path \seq_sort:Nn \l_@@_tmpb_seq { \fp_compare:nNnTF { ##1 } < { ##2 } { \sort_return_swapped: } { \sort_return_same: } } \seq_get_left:NN \l_@@_tmpb_seq \l_@@_tmpa_tl \fp_compare:nT { \l_@@_tmpa_tl > \seq_count:N \l_@@_tmpa_seq - .01 } { \bool_set_false:N \l_@@_closed_bool } \seq_get_right:NN \l_@@_tmpb_seq \l_@@_tmpa_tl \fp_compare:nT { \l_@@_tmpa_tl < .01 } { \bool_set_false:N \l_@@_closed_bool } % Restore the original copy of the path \tl_set_eq:NN \l_@@_tmpe_tl \l_@@_tmpg_tl % Clear the token lists \tl_clear:N \l_@@_tmpf_tl \tl_clear:N \l_@@_tmph_tl \tl_clear:N \l_@@_tmpg_tl \tl_set:Nn \l_@@_tmpi_tl {-1} \seq_map_inline:Nn \l_@@_tmpb_seq { \tl_set:Nn \l_@@_tmpb_tl {##1} \tl_set_eq:NN \l_@@_tmpa_tl \l_@@_tmpb_tl \int_compare:nT { \fp_to_int:n {floor( \l_@@_tmpb_tl ) } = \fp_to_int:n {floor( \l_@@_tmpi_tl) } } { \tl_set:Nx \l_@@_tmpb_tl { \fp_eval:n { floor( \l_@@_tmpb_tl ) + ( \l_@@_tmpb_tl - floor( \l_@@_tmpb_tl) ) / ( \l_@@_tmpi_tl - floor( \l_@@_tmpi_tl) ) } } } \tl_set_eq:NN \l_@@_tmpi_tl \l_@@_tmpa_tl \spath_split_at:NNVV \l_@@_tmpf_tl \l_@@_tmph_tl \l_@@_tmpe_tl \l_@@_tmpb_tl \tl_put_left:NV \l_@@_tmpg_tl \l_@@_tmph_tl \tl_set_eq:NN \l_@@_tmpe_tl \l_@@_tmpf_tl } \tl_put_left:NV \l_@@_tmpg_tl \l_@@_tmpe_tl \tl_if_empty:NT \l_@@_tmpg_tl { \tl_set_eq:NN \l_@@_tmpg_tl \l_@@_tmpe_tl } \spath_remove_empty_components:N \l_@@_tmpg_tl % Do something with closed \bool_if:NT \l_@@_closed_bool { \spath_join_component:Nn \l_@@_tmpg_tl {1} } \tl_gclear:N \g_@@_output_tl \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpg_tl \group_end: } \cs_new_protected_nopar:Npn \spath_split_component_at_self_intersections:Nn #1#2 { \@@_split_component_at_self_intersections:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_component_at_self_intersections:Nn {NV} \cs_new_protected_nopar:Npn \spath_split_component_at_self_intersections:N #1 { \spath_split_component_at_self_intersections:NV #1#1 } \cs_generate_variant:Nn \spath_split_component_at_self_intersections:N {c} \cs_new_protected_nopar:Npn \spath_gsplit_component_at_self_intersections:Nn #1#2 { \@@_split_component_at_self_intersections:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_component_at_self_intersections:Nn {NV} \cs_new_protected_nopar:Npn \spath_gsplit_component_at_self_intersections:N #1 { \spath_gsplit_component_at_self_intersections:NV #1#1 } \cs_generate_variant:Nn \spath_gsplit_component_at_self_intersections:N {c} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_split_at_self_intersections:Nn, % \spath_split_at_self_intersections:N, % \spath_gsplit_at_self_intersections:Nn, % \spath_gsplit_at_self_intersections:N, % } % Split a path at its self intersections. % We iterate over the components, splitting each where it meets all the others and itself. % To make this more efficient, we split against the components of the original path rather than updating each time. % % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_split_at_self_intersections:n #1 { \group_begin: \spath_components_to_seq:Nn \l_@@_tmpa_seq {#1} \seq_clear:N \l_@@_tmpb_seq \seq_clear:N \l_@@_tmpc_seq % Iterate over the components of the original path. \bool_do_until:nn { \seq_if_empty_p:N \l_@@_tmpa_seq } { % Get the next component \seq_pop_left:NN \l_@@_tmpa_seq \l_@@_tmpa_tl % Copy for later \tl_set_eq:NN \l_@@_tmpc_tl \l_@@_tmpa_tl \int_compare:nT { \tl_count:N \l_@@_tmpa_tl > 3 } { % Split against itself \spath_split_component_at_self_intersections:N \l_@@_tmpa_tl % Grab the rest of the path \tl_set:Nx \l_@@_tmpb_tl { \seq_use:Nn \l_@@_tmpb_seq {} \seq_use:Nn \l_@@_tmpa_seq {} } % Split against the rest of the path \spath_split_path_at_intersections:NV \l_@@_tmpa_tl \l_@@_tmpb_tl } % Save the split path \seq_put_right:NV \l_@@_tmpc_seq \l_@@_tmpa_tl % Add the original copy to the sequence of processed components \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpc_tl } \tl_gclear:N \g_@@_output_tl \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpc_seq {} } \group_end: } \cs_generate_variant:Nn \@@_split_at_self_intersections:n {V, v} \cs_new_protected_nopar:Npn \spath_split_at_self_intersections:Nn #1#2 { \@@_split_at_self_intersections:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_at_self_intersections:Nn {NV, cn, cV, cv} \cs_new_protected_nopar:Npn \spath_split_at_self_intersections:N #1 { \spath_split_at_self_intersections:NV #1#1 } \cs_generate_variant:Nn \spath_split_at_self_intersections:N {c} \cs_new_protected_nopar:Npn \spath_gsplit_at_self_intersections:Nn #1#2 { \@@_split_at_self_intersections:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_at_self_intersections:Nn {NV, cn, cV, cv} \cs_new_protected_nopar:Npn \spath_gsplit_at_self_intersections:N #1 { \spath_gsplit_at_self_intersections:NV #1#1 } \cs_generate_variant:Nn \spath_gsplit_at_self_intersections:N {c} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_join_component:Nnn, % \spath_join_component:Nn, % \spath_gjoin_component:Nnn, % \spath_gjoin_component:Nn, % } % Join the specified component of the spath to its predecessor. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_join_component:nn #1#2 { \group_begin: \spath_numberofcomponents:Nn \l_@@_tmpa_int {#1} \bool_if:nTF { \int_compare_p:n { #2 >= 1 } && \int_compare_p:n { #2 <= \l_@@_tmpa_int } } { \int_compare:nTF { #2 == 1 } { \int_compare:nTF { \l_@@_tmpa_int == 1 } { \tl_set:Nn \l_@@_tmpa_tl {#1} \spath_initialpoint:Nn \l_@@_tmpb_tl {#1} \tl_put_right:NV \l_@@_tmpa_tl \c_spath_closepath_tl \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpb_tl \tl_gclear:N \g_@@_output_tl \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl } { \spath_components_to_seq:Nn \l_@@_tmpa_seq {#1} \seq_pop_left:NN \l_@@_tmpa_seq \l_@@_tmpa_tl \prg_replicate:nn {3} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \seq_put_right:NV \l_@@_tmpa_seq \l_@@_tmpa_tl \tl_gclear:N \g_@@_output_tl \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpa_seq {}} } } { \spath_components_to_seq:Nn \l_@@_tmpa_seq {#1} \seq_clear:N \l_@@_tmpb_seq \seq_map_indexed_inline:Nn \l_@@_tmpa_seq { \tl_set:Nn \l_@@_tmpa_tl {##2} \int_compare:nT {##1 = #2} { \prg_replicate:nn {3} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } } \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpa_tl } \tl_gclear:N \g_@@_output_tl \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpb_seq {}} } } { \tl_gclear:N \g_@@_output_tl \tl_gset:Nn \g_@@_output_tl {#1} } \group_end: } \cs_new_protected_nopar:Npn \spath_join_component:Nnn #1#2#3 { \@@_join_component:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_join_component:Nnn {NVn, NVV} \cs_new_protected_nopar:Npn \spath_join_component:Nn #1#2 { \spath_join_component:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_join_component:Nn {cn, NV, cV} \cs_new_protected_nopar:Npn \spath_gjoin_component:Nnn #1#2#3 { \@@_join_component:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gjoin_component:Nnn {NVn, NVV} \cs_new_protected_nopar:Npn \spath_gjoin_component:Nn #1#2 { \spath_gjoin_component:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gjoin_component:Nn {cn, NV, cV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_spot_weld_components:Nn, % \spath_spot_weld_components:N, % \spath_spot_gweld_components:Nn, % \spath_spot_gweld_components:N, % } % Weld together any components where the last point of one is at the start point of the next (within a tolerance). % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_spot_weld_components:n #1 { \group_begin: \dim_zero:N \l_@@_move_x_dim \dim_zero:N \l_@@_move_y_dim \spath_components_to_seq:Nn \l_@@_tmpa_seq {#1} \seq_clear:N \l_@@_tmpb_seq \dim_set:Nn \l_@@_move_x_dim {\tl_item:nn {#1} {2} + 10 pt} \dim_set:Nn \l_@@_move_y_dim {\tl_item:nn {#1} {3} + 10 pt} \int_set:Nn \l_@@_tmpa_int {\seq_count:N \l_@@_tmpa_seq} \seq_map_inline:Nn \l_@@_tmpa_seq { \tl_set:Nn \l_@@_tmpa_tl {##1} \bool_if:nT { \dim_compare_p:n { \dim_abs:n {\l_@@_move_x_dim - \tl_item:Nn \l_@@_tmpa_tl {2} } < 0.01pt } && \dim_compare_p:n { \dim_abs:n {\l_@@_move_y_dim - \tl_item:Nn \l_@@_tmpa_tl {3} } < 0.01pt } } { \prg_replicate:nn {3} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \int_decr:N \l_@@_tmpa_int } \tl_reverse:N \l_@@_tmpa_tl \dim_set:Nn \l_@@_move_x_dim {\tl_item:Nn \l_@@_tmpa_tl {2}} \dim_set:Nn \l_@@_move_y_dim {\tl_item:Nn \l_@@_tmpa_tl {1}} \tl_reverse:N \l_@@_tmpa_tl \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpa_tl } \tl_set:Nx \l_@@_tmpa_tl {\seq_use:Nn \l_@@_tmpb_seq {} } \spath_components_to_seq:NV \l_@@_tmpb_seq \l_@@_tmpa_tl \spath_initialpoint:Nn \l_@@_tmpa_tl {#1} \spath_finalpoint:Nn \l_@@_tmpb_tl {#1} \bool_if:nT { \dim_compare_p:n { \dim_abs:n { \tl_item:Nn \l_@@_tmpa_tl {1} - \tl_item:Nn \l_@@_tmpb_tl {1} } < 0.01pt } && \dim_compare_p:n { \dim_abs:n { \tl_item:Nn \l_@@_tmpa_tl {2} - \tl_item:Nn \l_@@_tmpb_tl {2} } < 0.01pt } } { \int_compare:nTF { \seq_count:N \l_@@_tmpb_seq > 1 } { \seq_pop_left:NN \l_@@_tmpb_seq \l_@@_tmpb_tl \prg_replicate:nn {3} { \tl_set:Nx \l_@@_tmpb_tl {\tl_tail:N \l_@@_tmpb_tl} } \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpb_tl } { \tl_set:NV \l_@@_tmpb_tl \c_spath_closepath_tl \tl_put_right:Nx \l_@@_tmpb_tl { { \tl_item:Nn \l_@@_tmpa_tl {1} } { \tl_item:Nn \l_@@_tmpa_tl {2} } } \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpb_tl } } \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpb_seq {}} \group_end: } \cs_new_protected_nopar:Npn \spath_spot_weld_components:Nn #1#2 { \@@_spot_weld_components:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_spot_weld_components:Nn {NV, cV, cn} \cs_new_protected_nopar:Npn \spath_spot_weld_components:N #1 { \spath_spot_weld_components:NV #1#1 } \cs_generate_variant:Nn \spath_spot_weld_components:N {c} \cs_new_protected_nopar:Npn \spath_spot_gweld_components:Nn #1#2 { \@@_spot_weld_components:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_spot_gweld_components:Nn {NV, cV, cn} \cs_new_protected_nopar:Npn \spath_spot_gweld_components:N #1 { \spath_spot_gweld_components:NV #1#1 } \cs_generate_variant:Nn \spath_spot_gweld_components:N {c} % \end{macrocode} % \end{macro} % % \subsection{Exporting Commands} % % \begin{macro}[internal]{ % \spath_convert_to_svg:Nn, % \spath_gconvert_to_svg:Nn, % } % Convert the soft path to an SVG document. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_convert_to_svg:n #1 { \group_begin: \tl_clear:N \l_@@_tmpa_tl \tl_put_right:Nn \l_@@_tmpa_tl { \iow_newline: \iow_newline: \iow_newline: \iow_newline: \iow_newline: } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_new_protected_nopar:Npn \spath_convert_to_svg:Nn #1#2 { \@@_convert_to_svg:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_new_protected_nopar:Npn \spath_gconvert_to_svg:Nn #1#2 { \@@_convert_to_svg:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_export_to_svg:nn % } % Save a soft path to an SVG file. % \begin{macrocode} \iow_new:N \g_@@_stream \cs_new_protected_nopar:Npn \spath_export_to_svg:nn #1#2 { \group_begin: \spath_convert_to_svg:Nn \l_@@_tmpa_tl {#2} \iow_open:Nn \g_@@_stream {#1 .svg} \iow_now:Nx \g_@@_stream { \tl_use:N \l_@@_tmpa_tl } \iow_close:N \g_@@_stream \group_end: } \cs_generate_variant:Nn \spath_export_to_svg:nn {nv, nV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\spath_show:n} % Displays the soft path on the terminal. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_show:n #1 { \int_step_inline:nnnn {1} {3} {\tl_count:n {#1}} { \iow_term:x { \tl_item:nn {#1} {##1} {\tl_item:nn {#1} {##1+1}} {\tl_item:nn {#1} {##1+2}} } } } \cs_generate_variant:Nn \spath_show:n {V, v} % \end{macrocode} % \end{macro} % \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}[internal]{ % \spath_get_current_path:N, % \spath_gget_current_path:N % } % Grab the current soft path from PGF. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_get_current_path:N #1 { \pgfsyssoftpath@getcurrentpath #1 } % \end{macrocode} % % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_gget_current_path:N #1 { \pgfsyssoftpath@getcurrentpath #1 \tl_gset_eq:NN #1 #1 } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\spath_protocol_path:n} % This feeds the bounding box of the soft path to PGF to ensure that its current bounding box contains the soft path. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_protocol_path:n #1 { \spath_minbb:Nn \l_@@_tmpa_tl {#1} \exp_last_unbraced:NV \pgf@protocolsizes\l_@@_tmpa_tl \spath_maxbb:Nn \l_@@_tmpa_tl {#1} \exp_last_unbraced:NV \pgf@protocolsizes\l_@@_tmpa_tl } \cs_generate_variant:Nn \spath_protocol_path:n {V} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_set_current_path:n, % \spath_set_current_path:N % } % Sets the current path to the specified soft path. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_set_current_path:n #1 { \spath_protocol_path:n {#1} \tl_set:Nn \l_@@_tmpa_tl {#1} \pgfsyssoftpath@setcurrentpath\l_@@_tmpa_tl } \cs_new_protected_nopar:Npn \spath_set_current_path:N #1 { \spath_protocol_path:V #1 \pgfsyssoftpath@setcurrentpath #1 } \cs_generate_variant:Nn \spath_set_current_path:N {c} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_use_path:nn % } % Uses the given soft path at the PGF level. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_use_path:nn #1#2 { \spath_set_current_path:n {#1} \pgfusepath{#2} } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\spath_tikz_path:nn} % Uses the given soft path at the TikZ level. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_tikz_path:nn #1#2 { \path[#1] \pgfextra{ \spath_set_current_path:n {#2} \tl_put_right:Nn \tikz@preactions {\def\tikz@actions@path{#2}} }; } \cs_generate_variant:Nn \spath_tikz_path:nn {Vn, VV, nv, Vv, nV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\spath_set_tikz_data:n} % Sets the \Verb+\tikz@lastx+ and other coordinates from the soft path. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_set_tikz_data:n #1 { \spath_finalpoint:Nn \l_@@_tmpa_tl {#1} \tl_set:Nx \l_@@_tmpa_tl { \exp_not:c {pgf@x}=\tl_item:Nn \l_@@_tmpa_tl {1} \exp_not:c {pgf@y}=\tl_item:Nn \l_@@_tmpa_tl {2} } \use:c {pgf@process}{% \tl_use:N \l_@@_tmpa_tl \pgftransforminvert \use:c {pgf@pos@transform@glob} } \tl_set:Nx \l_@@_tmpa_tl { \exp_not:c {tikz@lastx}=\exp_not:c {pgf@x} \exp_not:c {tikz@lasty}=\exp_not:c {pgf@y} \exp_not:c {tikz@lastxsaved}=\exp_not:c {pgf@x} \exp_not:c {tikz@lastysaved}=\exp_not:c {pgf@y} } \tl_use:N \l_@@_tmpa_tl \spath_finalmovepoint:Nn \l_@@_tmpa_tl {#1} \ifpgfsyssoftpathmovetorelevant% \tl_gset_eq:cN {pgfsyssoftpath@lastmoveto} \l_@@_tmpa_tl \fi \tl_set:Nx \l_@@_tmpa_tl { \exp_not:c {pgf@x}=\tl_item:Nn \l_@@_tmpa_tl {1} \exp_not:c {pgf@y}=\tl_item:Nn \l_@@_tmpa_tl {2} } \use:c {pgf@process}{% \tl_use:N \l_@@_tmpa_tl \pgftransforminvert \use:c {pgf@pos@transform@glob} } \tl_set:Nx \l_@@_tmpa_tl { \exp_not:c {tikz@lastmovetox}=\exp_not:c {pgf@x} \exp_not:c {tikz@lastmovetoy}=\exp_not:c {pgf@y} } \tl_use:N \l_@@_tmpa_tl \tl_clear_new:c {tikz@timer} \tl_set:cn {tikz@timer} { \spath_reallength:Nn \l_@@_tmpa_int {#1} \tl_set_eq:Nc \l_@@_tmpb_tl {tikz@time} \tl_set:Nx \l_@@_tmpb_tl {\fp_to_decimal:n {(\l_@@_tmpb_tl) * (\l_@@_tmpa_int)}} \spath_transformation_at:NnV \l_@@_tmpc_tl {#1} \l_@@_tmpb_tl \tl_set:Nx \l_@@_tmpa_tl { \exp_not:N \pgfpoint { \tl_item:Nn \l_@@_tmpc_tl {5} } { \tl_item:Nn \l_@@_tmpc_tl {6} } } \exp_args:NV \pgftransformshift \l_@@_tmpa_tl \ifpgfresetnontranslationattime \pgftransformresetnontranslations \fi \ifpgfslopedattime \tl_set:Nx \l_@@_tmpa_tl { { \tl_item:Nn \l_@@_tmpc_tl {1} } { \tl_item:Nn \l_@@_tmpc_tl {2} } { \tl_item:Nn \l_@@_tmpc_tl {3} } { \tl_item:Nn \l_@@_tmpc_tl {4} } } \ifpgfallowupsidedownattime \else \fp_compare:nT { \tl_item:Nn \l_@@_tmpc_tl {4} < 0} { \tl_set:Nx \l_@@_tmpa_tl { { \fp_eval:n { - (\tl_item:Nn \l_@@_tmpc_tl {1})} } { \fp_eval:n { - (\tl_item:Nn \l_@@_tmpc_tl {2})} } { \fp_eval:n { - (\tl_item:Nn \l_@@_tmpc_tl {3})} } { \fp_eval:n { - (\tl_item:Nn \l_@@_tmpc_tl {4})} } } } \fi \tl_put_right:Nn \l_@@_tmpa_tl {{\pgfpointorigin}} \exp_last_unbraced:NV \pgftransformcm \l_@@_tmpa_tl \fi } } \cs_generate_variant:Nn \spath_set_tikz_data:n {V, v} % \end{macrocode} % \end{macro} % % \iffalse % % \fi % % \section{The TikZ interface} % % \iffalse %<*tikzspath3> % \fi % % This provides an interface to the soft path manipulation routines via a series of TikZ keys. % They all live in the \texttt{spath} family. % % % \begin{macrocode} \RequirePackage{spath3} \RequirePackage{expl3} \ExplSyntaxOn \tl_new:N \l_@@_current_tl \tl_new:N \l_@@_reverse_tl \tl_new:N \l_@@_prefix_tl \tl_new:N \l_@@_suffix_tl \tl_new:N \g_@@_smuggle_tl \seq_new:N \g_@@_tmpa_seq \seq_new:N \g_@@_tmpb_seq \bool_new:N \l_@@_draft_bool % \end{macrocode} % % We surround all the keys with checks to ensure that the soft path under consideration does actually exist, but if it doesn't we should warn the user. % % \begin{macrocode} \msg_new:nnn { spath3 } { missing soft path } { Soft~ path~ #1~ doesn't~ exist } \msg_new:nnn { spath3 } { empty soft path } { Soft~ path~ #1~ is~ empty } % \end{macrocode} % % When saving a soft path, by default we use a naming convention that is compatible with the intersections library so that paths saved here and paths saved by the \texttt{name path} facility of the intersections library are mutually exchangeable. % % \begin{macrocode} \tl_set:Nn \l_@@_prefix_tl {tikz@intersect@path@name@} \tl_set:Nn \l_@@_suffix_tl {} % \end{macrocode} % % When a soft path is grabbed from TikZ we're usually deep in a group so I've adapted the code from the intersections library to dig the definition out of the group without making everything global. % % \begin{macrocode} \tl_new:N \g_@@_tikzfinish_tl \cs_new_protected_nopar:Npn \spath_at_end_of_path: { \tl_use:N \g_@@_tikzfinish_tl \tl_gclear:N \g_@@_tikzfinish_tl } \tl_put_right:Nn \tikz@finish {\spath_at_end_of_path:} \cs_new_protected_nopar:Npn \spath_save_path:Nn #1#2 { \tl_gput_right:Nn \g_@@_tikzfinish_tl { \tl_clear_new:N #1 \tl_set:Nn #1 {#2} } } \cs_generate_variant:Nn \spath_save_path:Nn {cn, NV, cV} \cs_new_protected_nopar:Npn \spath_gsave_path:Nn #1#2 { \tl_gput_right:Nn \g_@@_tikzfinish_tl { \tl_gclear_new:N #1 \tl_gset:Nn #1 {#2} } } \cs_generate_variant:Nn \spath_gsave_path:Nn {cn, NV, cV} % \end{macrocode} % % \begin{macro}[internal]{ % \@@_process_tikz_point:Nn % } % Process a point via TikZ and store the resulting dimensions. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_process_tikz_point:Nn #1#2 { \group_begin: \use:c {tikz@scan@one@point} \use:n #2 \scan_stop: \tl_gset:Nx \g_@@_output_tl { { \dim_use:c {pgf@x} } { \dim_use:c {pgf@y} } } \group_end: \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } % \end{macrocode} % \end{macro} % % When we split a soft path into components, we make it a comma separated list so that it can be fed into a \Verb+\foreach+ loop. % This can also make it possible to extract a single component, but to do this we need a wrapper around \Verb+\clist_item:Nn+ (there doesn't appear to be a PGF way of getting an item of a CS list). % % \begin{macrocode} \cs_set_eq:NN \getComponentOf \clist_item:Nn % \end{macrocode} % % Now we define all of our keys. % % \begin{macrocode} \tikzset{ % \end{macrocode} % % We're in the \texttt{spath} key family. % % \begin{macrocode} spath/.is~family, spath/.cd, % \end{macrocode} % % We provide for saving soft paths with a specific prefix and suffix in the name. % The default is to make it compatible with the intersections library. % % \begin{macrocode} set~ prefix/.store~ in=\l_@@_prefix_tl, prefix/.is~choice, prefix/default/.style={ /tikz/spath/set~ prefix=tikz@intersect@path@name@ }, set~ suffix/.store~ in=\l_@@_suffix_tl, suffix/.is~choice, suffix/default/.style={ /tikz/spath/set~ suffix={} }, set~ name/.style={ /tikz/spath/prefix=#1, /tikz/spath/suffix=#1 }, % \end{macrocode} % % Keys for saving and cloning a soft path. % % \begin{macrocode} save/.code={ \tikz@addmode{ \spath_get_current_path:N \l_@@_tmpa_tl \spath_bake_round:NV \l_@@_tmpa_tl \l_@@_tmpa_tl \spath_save_path:cV {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} \l_@@_tmpa_tl } }, clone/.code~ 2~ args={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} { \tl_clear_new:c {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} \tl_set_eq:cc {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #2 } } }, clone~ global/.code~ 2~ args={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} { \tl_gclear_new:c {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} \tl_gset_eq:cc {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #2 } } }, 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:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_save_to_aux:c {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Restores a soft path to the current path. % % \begin{macrocode} restore/.code={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \tl_if_empty:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \tl_clear:N \l_@@_tmpa_tl \tl_put_right:NV \l_@@_tmpa_tl \c_spath_moveto_tl \tl_put_right:Nn \l_@@_tmpa_tl {{0pt}{0pt}} \spath_set_current_path:V \l_@@_tmpa_tl \spath_set_tikz_data:V \l_@@_tmpa_tl \msg_warning:nnn { spath3 } { empty soft path } { #1 } } { \spath_set_current_path:c {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} \spath_set_tikz_data:v {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} } } { \tl_clear:N \l_@@_tmpa_tl \tl_put_right:NV \l_@@_tmpa_tl \c_spath_moveto_tl \tl_put_right:Nn \l_@@_tmpa_tl {{0pt}{0pt}} \spath_set_current_path:V \l_@@_tmpa_tl \spath_set_tikz_data:V \l_@@_tmpa_tl \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Restores the reverse of a soft path to the current path. % % \begin{macrocode} restore~ reverse/.code={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \tl_if_empty:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \tl_clear:N \l_@@_tmpa_tl \tl_put_right:NV \l_@@_tmpa_tl \c_spath_moveto_tl \tl_put_right:Nn \l_@@_tmpa_tl {{0pt}{0pt}} \spath_set_current_path:V \l_@@_tmpa_tl \spath_set_tikz_data:V \l_@@_tmpa_tl \msg_warning:nnn { spath3 } { empty soft path } { #1 } } { \spath_reverse:Nv \l_@@_reverse_tl {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} \spath_set_current_path:N \l_@@_reverse_tl \spath_set_tikz_data:V \l_@@_reverse_tl } } { \tl_clear:N \l_@@_tmpa_tl \tl_put_right:NV \l_@@_tmpa_tl \c_spath_moveto_tl \tl_put_right:Nn \l_@@_tmpa_tl {{0pt}{0pt}} \spath_set_current_path:V \l_@@_tmpa_tl \spath_set_tikz_data:V \l_@@_tmpa_tl \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Diagnostic, show the current path in the terminal and log. % % \begin{macrocode} show~current~path/.code={ \tikz@addmode{ \pgfsyssoftpath@getcurrentpath\l_@@_tmpa_tl \iow_term:n {---~ current~ soft~ path~ ---} \spath_show:V \l_@@_tmpa_tl } }, % \end{macrocode} % % Diagnostic, show the named soft path in the terminal and log. % % \begin{macrocode} show/.code={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \tl_if_empty:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \msg_warning:nnn { spath3 } { empty soft path } { #1 } } { \iow_term:n {---~ soft~ path~ #1~ ---} \spath_show:v {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} } } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Appends the named path to the current path with a weld. % % \begin{macrocode} append/.code={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_get_current_path:N \l_@@_current_tl \spath_finalpoint:NV \l_@@_tmpa_tl \l_@@_current_tl \tl_set_eq:Nc \l_@@_tmpb_tl {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} \spath_translate_to:NV \l_@@_tmpb_tl \l_@@_tmpa_tl \spath_append_no_move:NV \l_@@_current_tl \l_@@_tmpb_tl \spath_set_current_path:N \l_@@_current_tl \spath_set_tikz_data:V \l_@@_tmpb_tl } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Joins the second named path to the first. % % \begin{macrocode} join~ with/.code~ 2~ args={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \tl_if_exist:cTF {\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} } { \msg_warning:nnn { spath3 } { missing soft path } { #2 } } } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Does a ``spot weld'' on a soft path, which means that any components that start where the previous component ends are welded together. % % \begin{macrocode} spot~ weld/.code={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_spot_weld_components:c {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, spot~ weld~ globally/.code={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_spot_gweld_components:c {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Reverses the named path. % % \begin{macrocode} reverse/.code={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_reverse:c {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, reverse~ globally/.code={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_reverse:c {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Appends the reversal of the path to the current path. % % \begin{macrocode} append~ reverse/.code={ \tl_if_exist:cTF {\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_finalpoint:NV \l_@@_tmpa_tl \l_@@_current_tl \spath_translate_to:NV \l_@@_reverse_tl \l_@@_tmpa_tl \spath_append_no_move:NV \l_@@_current_tl \l_@@_reverse_tl \spath_set_current_path:N \l_@@_current_tl \spath_set_tikz_data:V \l_@@_reverse_tl } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \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:cTF {\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_data:v {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Inserts the reverse of the named path. % % \begin{macrocode} insert~ reverse/.code={ \tl_if_exist:cTF {\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_data:V \l_@@_reverse_tl } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Appends the named path to the current path without translating it. % % \begin{macrocode} append~ no~ move/.code={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_get_current_path:N \l_@@_current_tl \spath_append_no_move: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_data:v {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Appends the reverse of the named path without translating it. % % \begin{macrocode} append~ reverse~ no~ move/.code={ \tl_if_exist:cTF {\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_no_move:NV \l_@@_current_tl \l_@@_reverse_tl \spath_set_current_path:N \l_@@_current_tl \spath_set_tikz_data:V \l_@@_reverse_tl } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Adjust a path to span between two points. % % \begin{macrocode} span/.code ~n~ args={3}{ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \@@_process_tikz_point:Nn \l_@@_tmpa_tl {#2} \@@_process_tikz_point:Nn \l_@@_tmpb_tl {#3} \spath_span:cVV {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} \l_@@_tmpa_tl \l_@@_tmpb_tl } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, span~ global/.code ~n~ args={3}{ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \@@_process_tikz_point:Nn \l_@@_tmpa_tl {#2} \@@_process_tikz_point:Nn \l_@@_tmpb_tl {#3} \spath_gspan:cVV {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} \l_@@_tmpa_tl \l_@@_tmpb_tl } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Defines a to path % % \begin{macrocode} to/.style={ to~path={ [ spath/span={#1}{(\tikztostart)}{(\tikztotarget)}, spath/append~no~move={#1}, ] \tikztonodes } }, % \end{macrocode} % % Splice three paths together, transforming the middle one so that it exactly fits between the first and third. % % \begin{macrocode} splice/.code ~n~ args={3}{ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} { \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #3 \tl_use:N \l_@@_suffix_tl} { \spath_splice_between:cvv {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} {\tl_use:N \l_@@_prefix_tl #3 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #3 } } } { \msg_warning:nnn { spath3 } { missing soft path } { #2 } } } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, splice~ global/.code ~n~ args={3}{ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} { \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #3 \tl_use:N \l_@@_suffix_tl} { \spath_gsplice_between:cvv {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} {\tl_use:N \l_@@_prefix_tl #3 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #3 } } } { \msg_warning:nnn { spath3 } { missing soft path } { #2 } } } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Join the components of a path by splicing in the second path whenever the components are sufficiently far apart. % % \begin{macrocode} join~ components~ with/.code~2~args={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} { \spath_spot_weld_components:c {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} \spath_components_to_seq:Nv \l_@@_tmpa_seq {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} \seq_pop_left:NN \l_@@_tmpa_seq \l_@@_tmpa_tl \seq_map_inline:Nn \l_@@_tmpa_seq { \spath_splice_between:Nvn \l_@@_tmpa_tl {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} {##1} } \tl_set_eq:cN {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} \l_@@_tmpa_tl } { \msg_warning:nnn { spath3 } { missing soft path } { #2 } } } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, join~ components~ globally~ with/.code~2~args={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} { \spath_spot_gweld_components:c {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} \spath_components_to_seq:Nv \l_@@_tmpa_seq {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} \seq_pop_left:NN \l_@@_tmpa_seq \l_@@_tmpa_tl \seq_map_inline:Nn \l_@@_tmpa_seq { \spath_gsplice_between:Nvn \l_@@_tmpa_tl {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} {##1} } \tl_set_eq:cN {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} \l_@@_tmpa_tl } { \msg_warning:nnn { spath3 } { missing soft path } { #2 } } } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Close a path. % % \begin{macrocode} close/.code={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_close:c {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, close~ globally/.code={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_gclose:c {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Close a path with another path. % % \begin{macrocode} close~ with/.code~ 2~ args={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} { \spath_close_with:cv {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #2 } } } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, close~ globally~ with/.code~ 2~ args={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} { \spath_gclose_with:cv {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #2 } } } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % These keys shorten the path. % % \begin{macrocode} shorten~ at~ end/.code~ 2~ args={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_shorten_at_end:cn {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {#2} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, shorten~ at~ start/.code~ 2~ args ={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_shorten_at_start:cn {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {#2} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, shorten~ at~ both~ ends/.code~ 2~ args={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_shorten_at_end:cn {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {#2} \spath_shorten_at_start:cn {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {#2} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, shorten~ globally~ at~ end/.code~ 2~ args={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_gshorten_at_end:cn {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {#2} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, shorten~ globally~ at~ start/.code~ 2~ args ={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_gshorten_at_start:cn {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {#2} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, shorten~ globally~ at~ both~ ends/.code~ 2~ args={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_shorten_at_end:cn {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {#2} \spath_shorten_at_start:cn {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {#2} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % This translates the named path. % % \begin{macrocode} translate/.code~ n~ args={3}{ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_translate:cnn {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}{#2}{#3} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, translate~ globally/.code~ n~ args={3}{ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_gtranslate:cnn {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}{#2}{#3} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % This normalises the named path. % % \begin{macrocode} normalise/.code={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_normalise:c {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, normalise~ globally/.code={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_gnormalise:c {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Exports the path as an SVG file. % % \begin{macrocode} export~ to~ svg/.code={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_export_to_svg:nv {#1} {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Transforms the named path using TikZ transformation specifications. % % \begin{macrocode} transform/.code~ 2~ args={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \group_begin: \pgftransformreset \tikzset{#2} \pgfgettransform \l_@@_tmpa_tl \tl_gset:Nn \g_@@_smuggle_tl { \spath_transform:cnnnnnn {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} } \tl_gput_right:NV \g_@@_smuggle_tl \l_@@_tmpa_tl \group_end: \tl_use:N \g_@@_smuggle_tl } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, transform~globally/.code~ 2~ args={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \group_begin: \pgftransformreset \tikzset{#2} \pgfgettransform \l_@@_tmpa_tl \tl_gset:Nn \g_@@_smuggle_tl { \spath_gtransform:cnnnnnn {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} } \tl_gput_right:NV \g_@@_smuggle_tl \l_@@_tmpa_tl \group_end: \tl_use:N \g_@@_smuggle_tl } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Splits first path where it intersects with the second. % % \begin{macrocode} split~ at~ intersections~ with/.code~ n~ args={2}{ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} { \spath_split_path_at_intersections:cv {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #2 } } } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, split~ globally~ at~ intersections~ with/.code~ n~ args={2}{ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} { \spath_gsplit_path_at_intersections:cv {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #2 } } } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Splits two paths at their mutual intersections. % % \begin{macrocode} split~ at~ intersections/.code~ n~ args={2}{ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} { \spath_split_at_intersections:cc {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #2 } } } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, split~ globally~ at~ intersections/.code~ n~ args={2}{ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} { \spath_gsplit_at_intersections:cc {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {\tl_use:N \l_@@_prefix_tl #2 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #2 } } } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Splits a path at its self-intersections. % % \begin{macrocode} split~ at~ self~ intersections/.code={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_split_at_self_intersections:c {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, split~ globally~ at~ self~ intersections/.code={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_gsplit_at_self_intersections:c {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Extract the components of a path into a comma separated list (suitable for using in a \Verb+\foreach+ loop). % % \begin{macrocode} get~ components~ of/.code~ 2~ args={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \clist_clear_new:N #2 \spath_components_to_seq:Nv \l_@@_tmpa_seq {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} \seq_map_inline:Nn \l_@@_tmpa_seq { \tl_new:c { \tl_use:N \l_@@_prefix_tl anonymous_\int_use:N \g_@@_anon_int \tl_use:N \l_@@_suffix_tl } \tl_set:cn { \tl_use:N \l_@@_prefix_tl anonymous_\int_use:N \g_@@_anon_int \tl_use:N \l_@@_suffix_tl } {##1} \clist_put_right:Nx #2 {anonymous_\int_use:N \g_@@_anon_int} \int_gincr:N \g_@@_anon_int } } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, get~ components~ of~ globally/.code~ 2~ args={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \clist_gclear_new:N #2 \spath_components_to_seq:Nv \l_@@_tmpa_seq {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} \seq_map_inline:Nn \l_@@_tmpa_seq { \tl_new:c { \tl_use:N \l_@@_prefix_tl anonymous_\int_use:N \g_@@_anon_int \tl_use:N \l_@@_suffix_tl } \tl_gset:cn { \tl_use:N \l_@@_prefix_tl anonymous_\int_use:N \g_@@_anon_int \tl_use:N \l_@@_suffix_tl } {##1} \clist_gput_right:Nx #2 {anonymous_\int_use:N \g_@@_anon_int} \int_gincr:N \g_@@_anon_int } } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Loop through the components of a soft path and render each as a separate TikZ path so that they can be individually styled. % % \begin{macrocode} render~ components/.code={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \group_begin: \spath_components_to_seq:Nv \l_@@_tmpa_seq {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} \seq_map_indexed_inline:Nn \l_@@_tmpa_seq { \spath_tikz_path:nn { every~ spath~ component/.try, spath ~component~ ##1/.try, spath ~component/.try={##1}, every~ #1~ component/.try, #1 ~component~ ##1/.try, #1 ~component/.try={##1}, } { ##2 } } \group_end: } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % This puts gaps between components of a soft path. % The list of components is passed through a \Verb+\foreach+ loop so can use the shortcut syntax from those loops. % % \begin{macrocode} insert~ gaps~ after~ components/.code~ n~ args={3}{ \tl_if_exist:cTF {\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 \spath_numberofcomponents:Nv \l_@@_tmpa_int {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} \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 { \int_mod:nn { \l_@@_tmpa_tl }{ \l_@@_tmpa_int } + 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 } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, insert~ gaps~ globally~ after~ components/.code~ n~ args={3}{ \tl_if_exist:cTF {\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 \spath_numberofcomponents:Nv \l_@@_tmpa_int {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} \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 { \int_mod:nn { \l_@@_tmpa_tl }{ \l_@@_tmpa_int } + 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_gset_eq:cN {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Join the specified components together, joining each to its previous one. % % \begin{macrocode} join~ components/.code~ 2~ args={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \seq_gclear:N \g_@@_tmpa_seq \foreach \l_@@_tmpa_tl in {#2} { \seq_gput_right:NV \g_@@_tmpa_seq \l_@@_tmpa_tl } \seq_gsort:Nn \g_@@_tmpa_seq { \int_compare:nNnTF {##1} > {##2} { \sort_return_same: } { \sort_return_swapped: } } \seq_map_inline:Nn \g_@@_tmpa_seq { \spath_join_component:cn {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}{##1} } } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, join~ components~ globally/.code~ 2~ args={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \seq_gclear:N \g_@@_tmpa_seq \foreach \l_@@_tmpa_tl in {#2} { \seq_gput_right:NV \g_@@_tmpa_seq \l_@@_tmpa_tl } \seq_gsort:Nn \g_@@_tmpa_seq { \int_compare:nNnTF {##1} > {##2} { \sort_return_same: } { \sort_return_swapped: } } \seq_map_inline:Nn \g_@@_tmpa_seq { \spath_gjoin_component:cn {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl}{##1} } } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % Remove all components of the path that don't actually draw anything. % % \begin{macrocode} remove~ empty~ components/.code={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_remove_empty_components:c {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, remove~ empty~ components~ globally/.code={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_gremove_empty_components:c {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % % Join the specified components together, joining each to its previous one. % % \begin{macrocode} remove~ components/.code~ 2~ args={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \seq_gclear:N \g_@@_tmpa_seq \foreach \l_@@_tmpa_tl in {#2} { \seq_gput_right:NV \g_@@_tmpa_seq \l_@@_tmpa_tl } \seq_gsort:Nn \g_@@_tmpa_seq { \int_compare:nNnTF {##1} < {##2} { \sort_return_same: } { \sort_return_swapped: } } \spath_components_to_seq:Nv \l_@@_tmpa_seq {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} \seq_gpop_left:NNF \g_@@_tmpa_seq \l_@@_tmpa_tl { \tl_clear:N \l_@@_tmpa_tl } \seq_clear:N \l_@@_tmpb_seq \seq_map_indexed_inline:Nn \l_@@_tmpa_seq { \tl_set:Nn \l_@@_tmpb_tl {##1} \tl_if_eq:NNTF \l_@@_tmpb_tl \l_@@_tmpa_tl { \seq_gpop_left:NNF \g_@@_tmpa_seq \l_@@_tmpa_tl { \tl_clear:N \l_@@_tmpa_tl } } { \seq_put_right:Nn \l_@@_tmpb_seq {##2} } } \tl_set:cx {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {\seq_use:Nn \l_@@_tmpb_seq {} } } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, remove~ components~ globally/.code~ 2~ args={ \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \seq_gclear:N \g_@@_tmpa_seq \foreach \l_@@_tmpa_tl in {#2} { \seq_gput_right:NV \g_@@_tmpa_seq \l_@@_tmpa_tl } \seq_gsort:Nn \g_@@_tmpa_seq { \int_compare:nNnTF {##1} < {##2} { \sort_return_same: } { \sort_return_swapped: } } \spath_components_to_seq:Nv \l_@@_tmpa_seq {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} \seq_gpop_left:NNF \g_@@_tmpa_seq \l_@@_tmpa_tl { \tl_clear:N \l_@@_tmpa_tl } \seq_clear:N \l_@@_tmpb_seq \seq_map_indexed_inline:Nn \l_@@_tmpa_seq { \tl_set:Nn \l_@@_tmpb_tl {##1} \tl_if_eq:NNTF \l_@@_tmpb_tl \l_@@_tmpa_tl { \seq_gpop_left:NNF \g_@@_tmpa_seq \l_@@_tmpa_tl { \tl_clear:N \l_@@_tmpa_tl } } { \seq_put_right:Nn \l_@@_tmpb_seq {##2} } } \tl_gset:cx {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} {\seq_use:Nn \l_@@_tmpb_seq {} } } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } }, % \end{macrocode} % % This puts a conditional around the \texttt{spot weld} key because when figuring out a knot drawing then we will initially want to render it without the spot weld to keep the number of components constant. % % \begin{macrocode} draft~ mode/.is~ choice, draft~ mode/true/.code={ \bool_set_true:N \l_@@_draft_bool }, draft~ mode/false/.code={ \bool_set_false:N \l_@@_draft_bool }, maybe~ spot~ weld/.code={ \bool_if:NF \l_@@_draft_bool { \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_spot_weld_components:c {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } } }, maybe~ spot~ weld~ globally/.code={ \bool_if:NF \l_@@_draft_bool { \tl_if_exist:cTF {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} { \spath_spot_gweld_components:c {\tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl} } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } } } }, % \end{macrocode} % % Set the transformation to lie along a path. % % \begin{macrocode} transform~ to/.code~ 2~ args={ \group_begin: \tl_if_exist:cTF { \tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl } { \spath_reallength:Nv \l_@@_tmpa_int { \tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl } \tl_set:Nx \l_@@_tmpb_tl {\fp_to_decimal:n {(#2) * (\l_@@_tmpa_int)}} \spath_transformation_at:NvV \l_@@_tmpc_tl { \tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl } \l_@@_tmpb_tl \tl_gset_eq:NN \g_@@_smuggle_tl \l_@@_tmpc_tl } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } \tl_gset_eq:NN \g_@@_smuggle_tl { {1}{0}{0}{1}{0pt}{0pt} } } \group_end: \exp_last_unbraced:NV \pgfsettransformentries \g_@@_smuggle_tl \tl_gclear:N \g_@@_smuggle_tl }, % \end{macrocode} % % As above, but with a possible extra \(180^\circ\) rotation if needed to ensure that the new \(y\)--axis points vaguely upwards. % % \begin{macrocode} upright~ transform~ to/.code~ 2~ args={ \group_begin: \tl_if_exist:cTF { \tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl } { \spath_reallength:Nv \l_@@_tmpa_int { \tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl } \tl_set:Nx \l_@@_tmpb_tl {\fp_to_decimal:n {(#2) * (\l_@@_tmpa_int)}} \spath_transformation_at:NvV \l_@@_tmpc_tl { \tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl } \l_@@_tmpb_tl \tl_gset_eq:NN \g_@@_smuggle_tl \l_@@_tmpc_tl } { \msg_warning:nnn { spath3 } { missing soft path } { #1 } \tl_gset_eq:NN \g_@@_smuggle_tl { {1}{0}{0}{1}{0pt}{0pt} } } \fp_compare:nT { \tl_item:Nn \g_@@_smuggle_tl {4} < 0} { \tl_gset:Nx \g_@@_smuggle_tl { { \fp_eval:n { - (\tl_item:Nn \g_@@_smuggle_tl {1})} } { \fp_eval:n { - (\tl_item:Nn \g_@@_smuggle_tl {2})} } { \fp_eval:n { - (\tl_item:Nn \g_@@_smuggle_tl {3})} } { \fp_eval:n { - (\tl_item:Nn \g_@@_smuggle_tl {4})} } { \tl_item:Nn \g_@@_smuggle_tl {5} } { \tl_item:Nn \g_@@_smuggle_tl {6} } } } \group_end: \exp_last_unbraced:NV \pgfsettransformentries \g_@@_smuggle_tl \tl_gclear:N \g_@@_smuggle_tl }, % \end{macrocode} % % This is a useful set of styles for drawing a knot diagram. % % \begin{macrocode} knot/.style~ n~ args={3}{ spath/split~ at~ self~ intersections=#1, spath/remove~ empty~ components=#1, spath/insert~ gaps~ after~ components={#1}{#2}{#3}, spath/maybe~ spot~ weld=#1, spath/render~ components=#1 }, global~ knot/.style~ n~ args={3}{ spath/split~ globally~ at~ self~ intersections=#1, spath/remove~ empty~ components~ globally=#1, spath/insert~ gaps~ globally ~after~ components={#1}{#2}{#3}, spath/maybe~ spot~ weld~ globally=#1, spath/render~ components=#1 }, } % \end{macrocode} % % This defines a coordinate system that finds a position on a soft path. % % \begin{macrocode} \tikzdeclarecoordinatesystem{spath}{% \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_trim_spaces:N \l_@@_tmpa_tl \seq_set_split:NnV \l_@@_tmpa_seq {~} \l_@@_tmpa_tl \seq_pop_right:NN \l_@@_tmpa_seq \l_@@_tmpb_tl \tl_set:Nx \l_@@_tmpa_tl { \seq_use:Nn \l_@@_tmpa_seq {~} } \tl_if_exist:cTF { \tl_use:N \l_@@_prefix_tl \tl_use:N \l_@@_tmpa_tl \tl_use:N \l_@@_suffix_tl } { \tl_set_eq:Nc \l_@@_tmpa_tl { \tl_use:N \l_@@_prefix_tl \tl_use:N \l_@@_tmpa_tl \tl_use:N \l_@@_suffix_tl } \tl_if_empty:NTF \l_@@_tmpa_tl { \tl_gset_eq:NN \g_@@_smuggle_tl \pgfpointorigin } { \spath_reallength:NV \l_@@_tmpa_int \l_@@_tmpa_tl \tl_set:Nx \l_@@_tmpb_tl {\fp_to_decimal:n {(\l_@@_tmpb_tl) * (\l_@@_tmpa_int)}} \spath_point_at:NVV \l_@@_tmpc_tl \l_@@_tmpa_tl \l_@@_tmpb_tl \tl_clear:N \l_@@_tmpd_tl \tl_put_right:Nn \l_@@_tmpd_tl {\pgf@x=} \tl_put_right:Nx \l_@@_tmpd_tl {\tl_item:Nn \l_@@_tmpc_tl {1}} \tl_put_right:Nn \l_@@_tmpd_tl {\relax} \tl_put_right:Nn \l_@@_tmpd_tl {\pgf@y=} \tl_put_right:Nx \l_@@_tmpd_tl {\tl_item:Nn \l_@@_tmpc_tl {2}} \tl_put_right:Nn \l_@@_tmpd_tl {\relax} \tl_gset_eq:NN \g_@@_smuggle_tl \l_@@_tmpd_tl } } { \msg_warning:nnx { spath3 } { missing soft path } { \tl_use:N \l_@@_tmpa_tl } \tl_gset_eq:NN \g_@@_smuggle_tl \pgfpointorigin } \group_end: \use:c {pgf@process}{% \tl_use:N \g_@@_smuggle_tl \pgftransforminvert \use:c {pgf@pos@transform@glob} } } \ExplSyntaxOff % \end{macrocode} % \iffalse % % \fi % % \iffalse %<*calligraphy> % \fi % % \section{The Calligraphy Package} % % \begin{macrocode} %<@@=cal> % \end{macrocode} % % \subsection{Initialisation} % \begin{macrocode} \RequirePackage{spath3} \ExplSyntaxOn \tl_new:N \l_@@_tmpa_tl \tl_new:N \l_@@_tmpb_tl \tl_new:N \l_@@_tmp_path_tl \tl_new:N \l_@@_tmp_rpath_tl \tl_new:N \l_@@_tmp_rpathb_tl \tl_new:N \l_@@_tmp_patha_tl \seq_new:N \l_@@_tmpa_seq \int_new:N \l_@@_tmpa_int \int_new:N \l_@@_tmpb_int \int_new:N \g_@@_path_component_int \int_new:N \g_@@_label_int \fp_new:N \l_@@_tmpa_fp \fp_new:N \l_@@_tmpb_fp \fp_new:N \l_@@_tmpc_fp \fp_new:N \l_@@_tmpd_fp \fp_new:N \l_@@_tmpe_fp \dim_new:N \l_@@_tmpa_dim \dim_new:N \l_@@_tmpb_dim \dim_new:N \l_@@_tmpc_dim \dim_new:N \l_@@_tmpd_dim \dim_new:N \l_@@_tmpe_dim \dim_new:N \l_@@_tmpf_dim \dim_new:N \l_@@_tmpg_dim \dim_new:N \l_@@_tmph_dim \bool_new:N \l_@@_annotate_bool \bool_new:N \l_@@_taper_start_bool \bool_new:N \l_@@_taper_end_bool \bool_new:N \l_@@_taperable_bool \dim_new:N \l_@@_taper_width_dim \dim_new:N \l_@@_line_width_dim \bool_set_true:N \l_@@_taper_start_bool \bool_set_true:N \l_@@_taper_end_bool \cs_generate_variant:Nn \tl_put_right:Nn {Nv} \msg_new:nnn { calligraphy } { undefined pen } { The~ pen~ "#1"~ is~ not~ defined. } % \end{macrocode} % % \subsection{TikZ Keys} % % The public interface to this package is through TikZ keys and styles. % \begin{macrocode} \tikzset{ define~pen/.code={ \tikzset{pen~name=#1} \pgf@relevantforpicturesizefalse \tikz@addmode{ \pgfsyssoftpath@getcurrentpath\l_@@_tmpa_tl \spath_components_to_seq:NV \l_@@_tmpa_seq \l_@@_tmpa_tl \seq_gclear_new:c {g_@@_pen_\pgfkeysvalueof{/tikz/pen~name}_seq} \seq_gset_eq:cN {g_@@_pen_\pgfkeysvalueof{/tikz/pen~name}_seq} \l_@@_tmpa_seq \pgfusepath{discard}% } }, define~pen/.default={default}, use~pen/.code={ \tikzset{pen~name=#1} \int_gzero:N \g_@@_path_component_int \cs_set_eq:NN \pgfpathmoveto \cal_moveto:n \tikz@addmode{ \pgfsyssoftpath@getcurrentpath\l_@@_tmpa_tl \spath_components_to_seq:NV \l_@@_tmpa_seq \l_@@_tmpa_tl \tl_if_exist:cTF {g_@@_pen_\pgfkeysvalueof{/tikz/pen~name}_seq} { \cal_path_create:Nc \l_@@_tmpa_seq {g_@@_pen_\pgfkeysvalueof{/tikz/pen~name}_seq} } { \msg_warning:nnx { calligraphy } { undefined pen } { \pgfkeysvalueof{/tikz/pen~name} } } } }, use~pen/.default={default}, pen~name/.initial={default}, copperplate/.style={pen~name=copperplate}, pen~colour/.initial={black}, weight/.is~choice, weight/heavy/.style={ line~width=\pgfkeysvalueof{/tikz/heavy~line~width}, taper~width=\pgfkeysvalueof{/tikz/light~line~width}, }, weight/light/.style={ line~width=\pgfkeysvalueof{/tikz/light~line~width}, taper~width=0pt, }, heavy/.style={ weight=heavy }, light/.style={ weight=light }, heavy~line~width/.initial=2pt, light~line~width/.initial=1pt, taper/.is~choice, taper/.default=both, taper/none/.style={ taper~start=false, taper~end=false, }, taper/both/.style={ taper~start=true, taper~end=true, }, taper/start/.style={ taper~start=true, taper~end=false, }, taper/end/.style={ taper~start=false, taper~end=true, }, taper~start/.code={ \tl_if_eq:nnTF {#1} {true} { \bool_set_true:N \l_@@_taper_start_bool } { \bool_set_false:N \l_@@_taper_start_bool } }, taper~start/.default={true}, taper~end/.code={ \tl_if_eq:nnTF {#1} {true} { \bool_set_true:N \l_@@_taper_end_bool } { \bool_set_false:N \l_@@_taper_end_bool } }, taper~end/.default={true}, taper~width/.code={\dim_set:Nn \l_@@_taper_width_dim {#1}}, nib~style/.code~2~args={ \tl_clear_new:c {l_@@_nib_style_#1} \tl_set:cn {l_@@_nib_style_#1} {#2} }, stroke~style/.code~2~args={ \tl_clear_new:c {l_@@_stroke_style_#1} \tl_set:cn {l_@@_stroke_style_#1} {#2} }, this~stroke~style/.code={ \tl_clear_new:c {l_@@_stroke_inline_style_ \int_use:N \g_@@_path_component_int} \tl_set:cn {l_@@_stroke_inline_style_ \int_use:N \g_@@_path_component_int} {#1} }, annotate/.style={ annotate~if, annotate~reset, annotation~style/.update~value={#1}, }, annotate~if/.default={true}, annotate~if/.code={ \tl_if_eq:nnTF {#1} {true} { \bool_set_true:N \l_@@_annotate_bool } { \bool_set_false:N \l_@@_annotate_bool } }, annotate~reset/.code={ \int_gzero:N \g_@@_label_int }, annotation~style/.initial={draw,->}, annotation~shift/.initial={(0,1ex)}, every~annotation~node/.initial={anchor=south~west}, annotation~node~style/.code~2~args={ \tl_clear_new:c {l_@@_annotation_style_ #1 _tl} \tl_set:cn {l_@@_annotation_style_ #1 _tl}{#2} }, tl~use:N/.code={ \exp_args:NV \pgfkeysalso #1 }, tl~use:c/.code={ \tl_if_exist:cT {#1} { \exp_args:Nv \pgfkeysalso {#1} } }, /handlers/.update~style/.code={ \tl_if_eq:nnF {#1} {\pgfkeysnovalue} { \pgfkeys{\pgfkeyscurrentpath/.code=\pgfkeysalso{#1}} } }, /handlers/.update~value/.code={ \tl_if_eq:nnF {#1} {\pgfkeysnovalue} { \pgfkeyssetvalue{\pgfkeyscurrentpath}{#1} } }, } % \end{macrocode} % % Some wrappers around the TikZ keys. % \begin{macrocode} \NewDocumentCommand \pen { O{} } { \path[define~ pen,every~ calligraphy~ pen/.try,#1] } \NewDocumentCommand \definepen { O{} } { \tikz \path[define~ pen,every~ calligraphy~ pen/.try,#1] } \NewDocumentCommand \calligraphy { O{} } { \path[use~ pen,every~ calligraphy/.try,#1] } % \end{macrocode} % % \subsection{The Path Creation} % % \begin{macro}[internal]{\cal_path_create:NN} % This is the main command for creating the calligraphic paths. % First argument is the given path % Second argument is the pen path % \begin{macrocode} \cs_new_protected_nopar:Npn \cal_path_create:NN #1#2 { \int_zero:N \l_@@_tmpa_int \seq_map_inline:Nn #1 { \int_compare:nT {\tl_count:n {##1} > 3} { \int_incr:N \l_@@_tmpa_int \int_zero:N \l_@@_tmpb_int \tl_set:Nn \l_@@_tmp_path_tl {##1} \spath_open:N \l_@@_tmp_path_tl \spath_reverse:NV \l_@@_tmp_rpath_tl \l_@@_tmp_path_tl \seq_map_inline:Nn #2 { \int_incr:N \l_@@_tmpb_int \group_begin: \pgfsys@beginscope \cal_apply_style:c {l_@@_stroke_style_ \int_use:N \l_@@_tmpa_int} \cal_apply_style:c {l_@@_stroke_inline_style_ \int_use:N \l_@@_tmpa_int} \cal_apply_style:c {l_@@_nib_style_ \int_use:N \l_@@_tmpb_int} \spath_initialpoint:Nn \l_@@_tmpa_tl {####1} \tl_set_eq:NN \l_@@_tmp_patha_tl \l_@@_tmp_path_tl \spath_translate:NV \l_@@_tmp_patha_tl \l_@@_tmpa_tl \int_compare:nTF {\tl_count:n {####1} == 3} { \cal_at_least_three:N \l_@@_tmp_patha_tl \spath_protocol_path:V \l_@@_tmp_patha_tl \tikz@options \dim_set:Nn \l_@@_line_width_dim {\pgflinewidth} \cal_maybe_taper:N \l_@@_tmp_patha_tl } { \spath_weld:Nn \l_@@_tmp_patha_tl {####1} \spath_weld:NV \l_@@_tmp_patha_tl \l_@@_tmp_rpath_tl \spath_reverse:Nn \l_@@_tmp_rpathb_tl {####1} \spath_weld:NV \l_@@_tmp_patha_tl \l_@@_tmp_rpathb_tl \tl_clear:N \l_@@_tmpa_tl \tl_set:Nn \l_@@_tmpa_tl { fill=\pgfkeysvalueof{/tikz/pen~colour}, draw=none } \tl_if_exist:cT {l_@@_stroke_style_ \int_use:N \l_@@_tmpa_int} { \tl_put_right:Nv \l_@@_tmpa_tl {l_@@_stroke_style_ \int_use:N \l_@@_tmpa_int} } \tl_if_exist:cT {l_@@_stroke_inline_style_ \int_use:N \l_@@_tmpa_int} { \tl_put_right:Nn \l_@@_tmpa_tl {,} \tl_put_right:Nv \l_@@_tmpa_tl {l_@@_stroke_inline_style_ \int_use:N \l_@@_tmpa_int} } \tl_if_exist:cT {l_@@_nib_style_ \int_use:N \l_@@_tmpb_int} { \tl_put_right:Nn \l_@@_tmpa_tl {,} \tl_put_right:Nv \l_@@_tmpa_tl {l_@@_nib_style_ \int_use:N \l_@@_tmpb_int} } \spath_tikz_path:VV \l_@@_tmpa_tl \l_@@_tmp_patha_tl } \pgfsys@endscope \group_end: } \bool_if:NT \l_@@_annotate_bool { \seq_get_right:NN #2 \l_@@_tmpa_tl \spath_finalpoint:NV \l_@@_tmpa_tl \l_@@_tmpa_tl \spath_translate:NV \l_@@_tmp_path_tl \l_@@_tmpa_tl \tikz@scan@one@point \pgfutil@firstofone \pgfkeysvalueof{/tikz/annotation~shift} \spath_translate:Nnn \l_@@_tmp_path_tl {\pgf@x} {\pgf@y} \pgfkeysgetvalue{/tikz/annotation~style}{\l_@@_tmpa_tl} \spath_tikz_path:VV \l_@@_tmpa_tl \l_@@_tmp_path_tl \spath_finalpoint:NV \l_@@_tmpa_tl \l_@@_tmp_path_tl \exp_last_unbraced:NV \pgfqpoint \l_@@_tmpa_tl \begin{scope}[reset~ cm] \node[ every~annotation~node/.try, tl~use:c = {l_@@_annotation_style_ \int_use:N \l_@@_tmpa_int _tl} ] at (\pgf@x,\pgf@y) {\int_use:N \l_@@_tmpa_int}; \end{scope} } } } } \cs_generate_variant:Nn \cal_path_create:NN {Nc} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\cal_moveto:n} % When creating the path, we need to keep track of the number of components so that we can apply styles accordingly. % \begin{macrocode} \cs_new_eq:NN \cal_orig_moveto:n \pgfpathmoveto \cs_new_nopar:Npn \cal_moveto:n #1 { \int_gincr:N \g_@@_path_component_int \cal_orig_moveto:n {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\cal_apply_style:N} % Interface for applying \Verb+\tikzset+ to a token list. % \begin{macrocode} \cs_new_nopar:Npn \cal_apply_style:N #1 { \tl_if_exist:NT #1 { \exp_args:NV \tikzset #1 } } \cs_generate_variant:Nn \cal_apply_style:N {c} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\cal_at_least_three:Nn} % A tapered path has to have at least three components. % This figures out if it is necessary and sets up the splitting. % \begin{macrocode} \cs_new_protected_nopar:Npn \cal_at_least_three:Nn #1#2 { \spath_reallength:Nn \l_@@_tmpa_int {#2} \tl_clear:N \l_@@_tmpb_tl \tl_set:Nn \l_@@_tmpb_tl {#2} \int_compare:nTF {\l_@@_tmpa_int = 1} { \spath_split_at:Nn \l_@@_tmpb_tl {2/3} \spath_split_at:Nn \l_@@_tmpb_tl {1/2} } { \int_compare:nT {\l_@@_tmpa_int = 2} { \spath_split_at:Nn \l_@@_tmpb_tl {1.5} \spath_split_at:Nn \l_@@_tmpb_tl {.5} } } \tl_set_eq:NN #1 \l_@@_tmpb_tl } \cs_generate_variant:Nn \cal_at_least_three:Nn {NV} \cs_new_protected_nopar:Npn \cal_at_least_three:N #1 { \cal_at_least_three:NV #1#1 } \cs_generate_variant:Nn \cal_at_least_three:N {c} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\cal_maybe_taper:N} % Possibly tapers the path, depending on the booleans. % \begin{macrocode} \cs_new_protected_nopar:Npn \cal_maybe_taper:N #1 { \tl_set_eq:NN \l_@@_tmpa_tl #1 \bool_if:NT \l_@@_taper_start_bool { \dim_set:Nn \l_@@_tmpa_dim {\tl_item:Nn \l_@@_tmpa_tl {2}} \dim_set:Nn \l_@@_tmpb_dim {\tl_item:Nn \l_@@_tmpa_tl {3}} \tl_set:Nx \l_@@_tmpb_tl {\tl_item:Nn \l_@@_tmpa_tl {4}} \tl_case:NnF \l_@@_tmpb_tl { \c_spath_lineto_tl { \bool_set_true:N \l_@@_taperable_bool \dim_set:Nn \l_@@_tmpg_dim {\tl_item:Nn \l_@@_tmpa_tl {5}} \dim_set:Nn \l_@@_tmph_dim {\tl_item:Nn \l_@@_tmpa_tl {6}} \dim_set:Nn \l_@@_tmpc_dim {(2\l_@@_tmpa_dim + \l_@@_tmpg_dim)/3} \dim_set:Nn \l_@@_tmpd_dim {(2\l_@@_tmpb_dim + \l_@@_tmph_dim)/3} \dim_set:Nn \l_@@_tmpe_dim {(\l_@@_tmpa_dim + 2\l_@@_tmpg_dim)/3} \dim_set:Nn \l_@@_tmpf_dim {(\l_@@_tmpb_dim + 2\l_@@_tmph_dim)/3} \prg_replicate:nn {4} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \tl_put_left:NV \l_@@_tmpa_tl \c_spath_moveto_tl } \c_spath_curvetoa_tl { \bool_set_true:N \l_@@_taperable_bool \dim_set:Nn \l_@@_tmpc_dim {\tl_item:Nn \l_@@_tmpa_tl {5}} \dim_set:Nn \l_@@_tmpd_dim {\tl_item:Nn \l_@@_tmpa_tl {6}} \dim_set:Nn \l_@@_tmpe_dim {\tl_item:Nn \l_@@_tmpa_tl {8}} \dim_set:Nn \l_@@_tmpf_dim {\tl_item:Nn \l_@@_tmpa_tl {9}} \dim_set:Nn \l_@@_tmpg_dim {\tl_item:Nn \l_@@_tmpa_tl {11}} \dim_set:Nn \l_@@_tmph_dim {\tl_item:Nn \l_@@_tmpa_tl {12}} \prg_replicate:nn {10} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \tl_put_left:NV \l_@@_tmpa_tl \c_spath_moveto_tl } } { \bool_set_false:N \l_@@_taperable_bool } \bool_if:NT \l_@@_taperable_bool { \@@_taper_aux: } } \bool_if:NT \l_@@_taper_end_bool { \dim_set:Nn \l_@@_tmpa_dim {\tl_item:Nn \l_@@_tmpa_tl {-2}} \dim_set:Nn \l_@@_tmpb_dim {\tl_item:Nn \l_@@_tmpa_tl {-1}} \tl_set:Nx \l_@@_tmpb_tl {\tl_item:Nn \l_@@_tmpa_tl {-3}} \tl_case:NnF \l_@@_tmpb_tl { \c_spath_lineto_tl { \bool_set_true:N \l_@@_taperable_bool \dim_set:Nn \l_@@_tmpg_dim {\tl_item:Nn \l_@@_tmpa_tl {-5}} \dim_set:Nn \l_@@_tmph_dim {\tl_item:Nn \l_@@_tmpa_tl {-4}} \dim_set:Nn \l_@@_tmpc_dim {(2\l_@@_tmpa_dim + \l_@@_tmpg_dim)/3} \dim_set:Nn \l_@@_tmpd_dim {(2\l_@@_tmpb_dim + \l_@@_tmph_dim)/3} \dim_set:Nn \l_@@_tmpe_dim {(\l_@@_tmpa_dim + 2\l_@@_tmpg_dim)/3} \dim_set:Nn \l_@@_tmpf_dim {(\l_@@_tmpb_dim + 2\l_@@_tmph_dim)/3} \tl_reverse:N \l_@@_tmpa_tl \prg_replicate:nn {3} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \tl_reverse:N \l_@@_tmpa_tl } \c_spath_curveto_tl { \bool_set_true:N \l_@@_taperable_bool \dim_set:Nn \l_@@_tmpc_dim {\tl_item:Nn \l_@@_tmpa_tl {-5}} \dim_set:Nn \l_@@_tmpd_dim {\tl_item:Nn \l_@@_tmpa_tl {-4}} \dim_set:Nn \l_@@_tmpe_dim {\tl_item:Nn \l_@@_tmpa_tl {-8}} \dim_set:Nn \l_@@_tmpf_dim {\tl_item:Nn \l_@@_tmpa_tl {-7}} \dim_set:Nn \l_@@_tmpg_dim {\tl_item:Nn \l_@@_tmpa_tl {-11}} \dim_set:Nn \l_@@_tmph_dim {\tl_item:Nn \l_@@_tmpa_tl {-10}} \tl_reverse:N \l_@@_tmpa_tl \prg_replicate:nn {9} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \tl_reverse:N \l_@@_tmpa_tl } } { \bool_set_false:N \l_@@_taperable_bool } \bool_if:NT \l_@@_taperable_bool { \@@_taper_aux: } } \pgfsyssoftpath@setcurrentpath\l_@@_tmpa_tl \pgfsetstrokecolor{\pgfkeysvalueof{/tikz/pen~colour}} \pgfusepath{stroke} } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\@@_taper_aux:} % Auxiliary macro to avoid unnecessary code duplication. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_taper_aux: { \tl_clear:N \l_@@_tmpb_tl \tl_put_right:NV \l_@@_tmpb_tl \c_spath_moveto_tl \fp_set:Nn \l_@@_tmpa_fp { \l_@@_tmpd_dim - \l_@@_tmpb_dim } \fp_set:Nn \l_@@_tmpb_fp { \l_@@_tmpa_dim - \l_@@_tmpc_dim } \fp_set:Nn \l_@@_tmpe_fp { (\l_@@_tmpa_fp^2 + \l_@@_tmpb_fp^2)^.5 } \fp_set:Nn \l_@@_tmpa_fp { .5*\l_@@_taper_width_dim * \l_@@_tmpa_fp / \l_@@_tmpe_fp } \fp_set:Nn \l_@@_tmpb_fp { .5*\l_@@_taper_width_dim * \l_@@_tmpb_fp / \l_@@_tmpe_fp } \fp_set:Nn \l_@@_tmpc_fp { \l_@@_tmph_dim - \l_@@_tmpf_dim } \fp_set:Nn \l_@@_tmpd_fp { \l_@@_tmpe_dim - \l_@@_tmpg_dim } \fp_set:Nn \l_@@_tmpe_fp { (\l_@@_tmpc_fp^2 + \l_@@_tmpd_fp^2)^.5 } \fp_set:Nn \l_@@_tmpc_fp { .5*\l_@@_line_width_dim * \l_@@_tmpc_fp / \l_@@_tmpe_fp } \fp_set:Nn \l_@@_tmpd_fp { .5*\l_@@_line_width_dim * \l_@@_tmpd_fp / \l_@@_tmpe_fp } \tl_put_right:Nx \l_@@_tmpb_tl { {\dim_eval:n { \fp_to_dim:N \l_@@_tmpa_fp + \l_@@_tmpa_dim}} {\dim_eval:n { \fp_to_dim:N \l_@@_tmpb_fp + \l_@@_tmpb_dim}} } \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curvetoa_tl \tl_put_right:Nx \l_@@_tmpb_tl { {\dim_eval:n { \fp_to_dim:N \l_@@_tmpa_fp + \l_@@_tmpc_dim}} {\dim_eval:n { \fp_to_dim:N \l_@@_tmpb_fp + \l_@@_tmpd_dim}} } \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curvetob_tl \tl_put_right:Nx \l_@@_tmpb_tl { {\dim_eval:n { \fp_to_dim:N \l_@@_tmpc_fp + \l_@@_tmpe_dim}} {\dim_eval:n { \fp_to_dim:N \l_@@_tmpd_fp + \l_@@_tmpf_dim}} } \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curveto_tl \tl_put_right:Nx \l_@@_tmpb_tl { {\dim_eval:n { \fp_to_dim:N \l_@@_tmpc_fp + \l_@@_tmpg_dim}} {\dim_eval:n { \fp_to_dim:N \l_@@_tmpd_fp + \l_@@_tmph_dim}} } \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curvetoa_tl \tl_put_right:Nx \l_@@_tmpb_tl { { \dim_eval:n { \fp_to_dim:N \l_@@_tmpc_fp + \l_@@_tmpg_dim - \fp_to_dim:n{ 1.32 * \l_@@_tmpd_fp } } } { \dim_eval:n { \fp_to_dim:N \l_@@_tmpd_fp + \l_@@_tmph_dim + \fp_to_dim:n {1.32* \l_@@_tmpc_fp } } } } \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curvetob_tl \tl_put_right:Nx \l_@@_tmpb_tl { { \dim_eval:n { -\fp_to_dim:N \l_@@_tmpc_fp + \l_@@_tmpg_dim - \fp_to_dim:n {1.32 * \l_@@_tmpd_fp } } } { \dim_eval:n { -\fp_to_dim:N \l_@@_tmpd_fp + \l_@@_tmph_dim + \fp_to_dim:n {1.32 * \l_@@_tmpc_fp } } } } \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curveto_tl \tl_put_right:Nx \l_@@_tmpb_tl { {\dim_eval:n { -\fp_to_dim:N \l_@@_tmpc_fp + \l_@@_tmpg_dim}} {\dim_eval:n { -\fp_to_dim:N \l_@@_tmpd_fp + \l_@@_tmph_dim}} } \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curvetoa_tl \tl_put_right:Nx \l_@@_tmpb_tl { {\dim_eval:n { -\fp_to_dim:N \l_@@_tmpc_fp + \l_@@_tmpe_dim}} {\dim_eval:n { -\fp_to_dim:N \l_@@_tmpd_fp + \l_@@_tmpf_dim}} } \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curvetob_tl \tl_put_right:Nx \l_@@_tmpb_tl { {\dim_eval:n { -\fp_to_dim:N \l_@@_tmpa_fp + \l_@@_tmpc_dim}} {\dim_eval:n { -\fp_to_dim:N \l_@@_tmpb_fp + \l_@@_tmpd_dim}} } \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curveto_tl \tl_put_right:Nx \l_@@_tmpb_tl { {\dim_eval:n { -\fp_to_dim:N \l_@@_tmpa_fp + \l_@@_tmpa_dim}} {\dim_eval:n { -\fp_to_dim:N \l_@@_tmpb_fp + \l_@@_tmpb_dim}} } \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curvetoa_tl \tl_put_right:Nx \l_@@_tmpb_tl { { \dim_eval:n { -\fp_to_dim:N \l_@@_tmpa_fp + \l_@@_tmpa_dim + \fp_to_dim:n{ 1.32 * \l_@@_tmpb_fp} } } { \dim_eval:n { -\fp_to_dim:N \l_@@_tmpb_fp + \l_@@_tmpb_dim - \fp_to_dim:n {1.32* \l_@@_tmpa_fp} } } } \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curvetob_tl \tl_put_right:Nx \l_@@_tmpb_tl { { \dim_eval:n { \fp_to_dim:N \l_@@_tmpa_fp + \l_@@_tmpa_dim + \fp_to_dim:n {1.32 * \l_@@_tmpb_fp} } } { \dim_eval:n { \fp_to_dim:N \l_@@_tmpb_fp + \l_@@_tmpb_dim - \fp_to_dim:n {1.32 * \l_@@_tmpa_fp} } } } \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curveto_tl \tl_put_right:Nx \l_@@_tmpb_tl { {\dim_eval:n { \fp_to_dim:N \l_@@_tmpa_fp + \l_@@_tmpa_dim}} {\dim_eval:n { \fp_to_dim:N \l_@@_tmpb_fp + \l_@@_tmpb_dim}} } \pgfsyssoftpath@setcurrentpath\l_@@_tmpb_tl \pgfsetfillcolor{\pgfkeysvalueof{/tikz/pen~colour}} \pgfusepath{fill} } % \end{macrocode} % \end{macro} % % Defines a copperplate pen. % \begin{macrocode} \tl_set:Nn \l_@@_tmpa_tl {\pgfsyssoftpath@movetotoken{0pt}{0pt}} \spath_components_to_seq:NV \l_@@_tmpa_seq \l_@@_tmpa_tl \seq_gclear_new:N \g_@@_pen_copperplate_seq \seq_gset_eq:NN \g_@@_pen_copperplate_seq \l_@@_tmpa_seq % \end{macrocode} % % \begin{macro}[internal]{\CopperplatePath} % This is used in the decorations section to convert a path to a copperplate path. % \begin{macrocode} \DeclareDocumentCommand \CopperplatePath { m } { \spath_components_to_seq:NV \l_@@_tmpa_seq #1 \cal_path_create:NN \l_@@_tmpa_seq \g_@@_pen_copperplate_seq } % \end{macrocode} % \end{macro} % % \begin{macrocode} \ExplSyntaxOff % \end{macrocode} % % \subsection{Decorations} % % If a decoration library is loaded we define some decorations that use the calligraphy library, specifically the copperplate pen with its tapering. % % First, a brace decoration. % \begin{macrocode} \expandafter\ifx\csname pgfdeclaredecoration\endcsname\relax \else \pgfdeclaredecoration{calligraphic brace}{brace}% {% \state{brace}[width=+\pgfdecoratedremainingdistance,next state=final]% {% \pgfsyssoftpath@setcurrentpath{\pgfutil@empty}% \pgfpathmoveto{\pgfpointorigin}% \pgfpathcurveto% {% \pgfqpoint% {.15\pgfdecorationsegmentamplitude}% {.3\pgfdecorationsegmentamplitude}% }% {% \pgfqpoint% {.5\pgfdecorationsegmentamplitude}% {.5\pgfdecorationsegmentamplitude}% }% {% \pgfqpoint% {\pgfdecorationsegmentamplitude}% {.5\pgfdecorationsegmentamplitude}% }% {% \pgftransformxshift% {+\pgfdecorationsegmentaspect\pgfdecoratedremainingdistance}% \pgfpathlineto% {% \pgfqpoint% {-\pgfdecorationsegmentamplitude}% {.5\pgfdecorationsegmentamplitude}% }% \pgfpathcurveto {% \pgfqpoint% {-.5\pgfdecorationsegmentamplitude}% {.5\pgfdecorationsegmentamplitude}% }% {% \pgfqpoint% {-.15\pgfdecorationsegmentamplitude}% {.7\pgfdecorationsegmentamplitude}% }% {% \pgfqpoint% {0\pgfdecorationsegmentamplitude}% {1\pgfdecorationsegmentamplitude}% }% \pgfpathmoveto% {% \pgfqpoint% {0\pgfdecorationsegmentamplitude}% {1\pgfdecorationsegmentamplitude}% }% \pgfpathcurveto% {% \pgfqpoint% {.15\pgfdecorationsegmentamplitude}% {.7\pgfdecorationsegmentamplitude}% }% {% \pgfqpoint% {.5\pgfdecorationsegmentamplitude}% {.5\pgfdecorationsegmentamplitude}% }% {% \pgfqpoint% {\pgfdecorationsegmentamplitude}% {.5\pgfdecorationsegmentamplitude}% }% }% {% \pgftransformxshift{+\pgfdecoratedremainingdistance}% \pgfpathlineto% {% \pgfqpoint% {-\pgfdecorationsegmentamplitude}% {.5\pgfdecorationsegmentamplitude}% }% \pgfpathcurveto% {% \pgfqpoint% {-.5\pgfdecorationsegmentamplitude}% {.5\pgfdecorationsegmentamplitude}% }% {% \pgfqpoint% {-.15\pgfdecorationsegmentamplitude}% {.3\pgfdecorationsegmentamplitude}% }% {\pgfqpoint{0pt}{0pt}}% }% \tikzset{% taper width=.5\pgflinewidth,% taper% }%% \pgfsyssoftpath@getcurrentpath\cal@tmp@path% \CopperplatePath{\cal@tmp@path}% }% \state{final}{}% }% % \end{macrocode} % % The second is a straightened parenthesis (so that when very large it doesn't bow out too far). % \begin{macrocode} \pgfdeclaredecoration{calligraphic straight parenthesis}{brace} { \state{brace}[width=+\pgfdecoratedremainingdistance,next state=final]% {% \pgfsyssoftpath@setcurrentpath{\pgfutil@empty}% \pgfpathmoveto{\pgfpointorigin}% \pgfpathcurveto% {% \pgfqpoint% {.76604\pgfdecorationsegmentamplitude}% {.64279\pgfdecorationsegmentamplitude}% }% {% \pgfqpoint% {2.3333\pgfdecorationsegmentamplitude}% {\pgfdecorationsegmentamplitude}% }% {% \pgfqpoint% {3.3333\pgfdecorationsegmentamplitude}% {\pgfdecorationsegmentamplitude}% }% {% \pgftransformxshift{+\pgfdecoratedremainingdistance}% \pgfpathlineto% {% \pgfqpoint% {-3.3333\pgfdecorationsegmentamplitude}% {\pgfdecorationsegmentamplitude}% }% \pgfpathcurveto% {% \pgfqpoint% {-2.3333\pgfdecorationsegmentamplitude}% {\pgfdecorationsegmentamplitude}% }% {% \pgfqpoint% {-.76604\pgfdecorationsegmentamplitude}% {.64279\pgfdecorationsegmentamplitude}% }% {\pgfqpoint{0pt}{0pt}}% }% \tikzset{% taper width=.5\pgflinewidth,% taper% }% \pgfsyssoftpath@getcurrentpath\cal@tmp@path% \CopperplatePath{\cal@tmp@path}% }% \state{final}{}% } % \end{macrocode} % % The third is a curved parenthesis. % \begin{macrocode} \pgfdeclaredecoration{calligraphic curved parenthesis}{brace} { \state{brace}[width=+\pgfdecoratedremainingdistance,next state=final]% {% \pgfsyssoftpath@setcurrentpath{\pgfutil@empty}% \pgfpathmoveto{\pgfpointorigin}% \pgf@xa=\pgfdecoratedremainingdistance\relax% \advance\pgf@xa by -1.5890\pgfdecorationsegmentamplitude\relax% \edef\cgrphy@xa{\the\pgf@xa}% \pgfpathcurveto% {% \pgfqpoint% {1.5890\pgfdecorationsegmentamplitude}% {1.3333\pgfdecorationsegmentamplitude}% }% {\pgfqpoint{\cgrphy@xa}{1.3333\pgfdecorationsegmentamplitude}}% {\pgfqpoint{\pgfdecoratedremainingdistance}{0pt}}% \tikzset{% taper width=.5\pgflinewidth,% taper% }% \pgfsyssoftpath@getcurrentpath\cal@tmp@path% \CopperplatePath{\cal@tmp@path}% }% \state{final}{}% } % \end{macrocode} % End the conditional for if pgfdecoration module is loaded % \begin{macrocode} \fi % \end{macrocode} % \iffalse % % \fi % % % \iffalse %<*knots> % \fi %% % \section{Drawing Knots} % % \begin{macrocode} %<@@=knot> % \end{macrocode} % % \subsection{Initialisation} % % We load the \Verb+spath3+ library and the \Verb+intersections+ TikZ library. % Then we get going. % \begin{macrocode} \RequirePackage{spath3} \usetikzlibrary{intersections,spath3} \ExplSyntaxOn \tl_new:N \l_@@_tmpa_tl \tl_new:N \l_@@_tmpb_tl \tl_new:N \l_@@_tmpc_tl \tl_new:N \l_@@_tmpd_tl \tl_new:N \l_@@_tmpg_tl \tl_new:N \l_@@_redraws_tl \tl_new:N \l_@@_clip_width_tl \tl_new:N \l_@@_name_tl \tl_new:N \l_@@_node_tl \tl_new:N \l_@@_aux_tl \tl_new:N \l_@@_auxa_tl \tl_new:N \l_@@_prefix_tl \seq_new:N \l_@@_segments_seq \int_new:N \l_@@_tmpa_int \int_new:N \l_@@_strands_int \int_new:N \g_@@_intersections_int \int_new:N \g_@@_filaments_int \int_new:N \l_@@_component_start_int \fp_new:N \l_@@_tmpa_fp \fp_new:N \l_@@_tmpb_fp \dim_new:N \l_@@_tmpa_dim \dim_new:N \l_@@_tmpb_dim \dim_new:N \l_@@_tolerance_dim \dim_new:N \l_@@_redraw_tolerance_dim \dim_new:N \l_@@_clip_bg_radius_dim \dim_new:N \l_@@_clip_draw_radius_dim \bool_new:N \l_@@_draft_bool \bool_new:N \l_@@_ignore_ends_bool \bool_new:N \l_@@_self_intersections_bool \bool_new:N \l_@@_splits_bool \bool_new:N \l_@@_super_draft_bool \bool_new:N \l_@@_prepend_prev_bool \bool_new:N \l_@@_append_next_bool \bool_new:N \l_@@_skip_bool \bool_new:N \l_@@_save_bool \seq_new:N \g_@@_nodes_seq \bool_set_true:N \l_@@_ignore_ends_bool % \end{macrocode} % % Configuration is via TikZ keys and styles. % \begin{macrocode} \tikzset{ spath/prefix/knot/.style={ spath/set~ prefix=knot strand, }, spath/suffix/knot/.style={ spath/set~ suffix={}, }, knot/.code={ \tl_if_eq:nnTF {#1} {none} { \tikz@addmode{\tikz@mode@doublefalse} } { \tikz@addmode{\tikz@mode@doubletrue} \tl_if_eq:nnTF {\pgfkeysnovalue} {#1} { \tikz@addoption{\pgfsetinnerstrokecolor{.}} } { \pgfsetinnerstrokecolor{#1} } \tikz@addoption{ \pgfsetstrokecolor{knotbg} } \tl_set:Nn \tikz@double@setup{ \pgfsetinnerlinewidth{\pgflinewidth} \pgfsetlinewidth{\dim_eval:n {\tl_use:N \l_@@_gap_tl \pgflinewidth}} } } }, knot~ gap/.store~ in=\l_@@_gap_tl, knot~ gap=3, knot~ diagram/.is~family, knot~ diagram/.unknown/.code={ \tl_set_eq:NN \l_@@_tmpa_tl \pgfkeyscurrentname \pgfkeysalso{ /tikz/\l_@@_tmpa_tl=#1 } }, background~ colour/.code={% \colorlet{knotbg}{#1}% }, background~ color/.code={% \colorlet{knotbg}{#1}% }, background~ colour=white, knot~ diagram, name/.store~ in=\l_@@_name_tl, name={knot}, save~ intersections/.is~ choice, save~ intersections/.default=true, save~ intersections/true/.code={ \bool_set_true:N \l_@@_save_bool }, save~ intersections/false/.code={ \bool_set_false:N \l_@@_save_bool }, every~ strand/.style={draw}, ignore~ endpoint~ intersections/.code={ \tl_if_eq:nnTF {#1} {true} { \bool_set_true:N \l_@@_ignore_ends_bool } { \bool_set_false:N \l_@@_ignore_ends_bool } }, ignore~ endpoint~ intersections/.default=true, consider~ self~ intersections/.is~choice, consider~ self~ intersections/true/.code={ \bool_set_true:N \l_@@_self_intersections_bool \bool_set_true:N \l_@@_splits_bool }, consider~ self~ intersections/false/.code={ \bool_set_false:N \l_@@_self_intersections_bool \bool_set_false:N \l_@@_splits_bool }, consider~ self~ intersections/no~ splits/.code={ \bool_set_true:N \l_@@_self_intersections_bool \bool_set_false:N \l_@@_splits_bool }, consider~ self~ intersections/.default={true}, clip~ radius/.code={ \dim_set:Nn \l_@@_clip_bg_radius_dim {#1} \dim_set:Nn \l_@@_clip_draw_radius_dim {#1+2pt} }, clip~ draw~ radius/.code={ \dim_set:Nn \l_@@_clip_draw_radius_dim {#1} }, clip~ background~ radius/.code={ \dim_set:Nn \l_@@_clip_bg_radius_dim {#1} }, clip~ radius=10pt, end~ tolerance/.code={ \dim_set:Nn \l_@@_tolerance_dim {#1} }, end~ tolerance=14pt, clip/.style={ clip }, background~ clip/.style={ clip }, clip~ width/.code={ \tl_set:Nn \l_@@_clip_width_tl {#1} }, clip~ width=3, flip~ crossing/.code={% \tl_clear_new:c {l_@@_crossing_#1} \tl_set:cn {l_@@_crossing_#1} {x} }, ignore~ crossing/.code={% \tl_clear_new:c {l_@@_ignore_crossing_#1} \tl_set:cn {l_@@_ignore_crossing_#1} {x} }, draft~ mode/.is~ choice, draft~ mode/off/.code={% \bool_set_false:N \l_@@_draft_bool \bool_set_false:N \l_@@_super_draft_bool }, draft~ mode/crossings/.code={% \bool_set_true:N \l_@@_draft_bool \bool_set_false:N \l_@@_super_draft_bool }, draft~ mode/strands/.code={% \bool_set_true:N \l_@@_draft_bool \bool_set_true:N \l_@@_super_draft_bool }, draft/.is~ family, draft, crossing~ label/.style={ overlay, fill=white, fill~ opacity=.5, text~ opacity=1, text=blue, pin~ edge={blue,<-} }, strand~ label/.style={ overlay, circle, draw=purple, fill=white, fill~ opacity=.5, text~ opacity=1, text=purple, inner~ sep=0pt }, } % \end{macrocode} % % Wrapper around \Verb+\tikzset+ for applying keys from a token list, checking for if the given token list exists. % \begin{macrocode} \cs_new_nopar:Npn \knot_apply_style:N #1 { \tl_if_exist:NT #1 { \exp_args:NV \tikzset #1 } } \cs_generate_variant:Nn \knot_apply_style:N {c} % \end{macrocode} % %\begin{macro}[internal]{\flipcrossings} % The user can specify a comma separated list of crossings to flip. % \begin{macrocode} \NewDocumentCommand \flipcrossings {m} { \tikzset{knot~ diagram/flip~ crossing/.list={#1}}% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\strand} % This is how the user specifies a strand of the knot. % \begin{macrocode} \NewDocumentCommand \strand { O{} } { \int_incr:N \l_@@_strands_int \tl_clear_new:c {l_@@_options_strand \int_use:N \l_@@_strands_int} \tl_set:cn {l_@@_options_strand \int_use:N \l_@@_strands_int} {#1} \path[#1,spath/set~ name=knot,spath/save=\int_use:N \l_@@_strands_int] } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{knot} % This is the wrapper environment that calls the knot generation code. % \begin{macrocode} \NewDocumentEnvironment{knot} { O{} } { \knot_initialise:n {#1} } { \knot_render: } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_initialise:n} % Set up some stuff before loading in the strands. % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_initialise:n #1 { \tikzset{knot~ diagram/.cd,every~ knot~ diagram/.try,#1} \int_zero:N \l_@@_strands_int \tl_clear:N \l_@@_redraws_tl \seq_gclear:N \g_@@_nodes_seq } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_render:} % This is the code that starts the work of rendering the knot. % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_render: { % \end{macrocode} % Start a scope and reset the transformation (since all transformations have already been taken into account when defining the strands). % \begin{macrocode} \pgfscope \pgftransformreset % \end{macrocode} % Set the dimension for deciding when to include neighbouring strands % \begin{macrocode} \dim_set:Nn \l_@@_redraw_tolerance_dim {\fp_to_dim:n { sqrt(2) * max(\l_@@_clip_bg_radius_dim, \l_@@_clip_draw_radius_dim) } } % \end{macrocode} % Loop through the strands drawing each one for the first time. % \begin{macrocode} \int_step_function:nnnN {1} {1} {\l_@@_strands_int} \knot_draw_strand:n % \end{macrocode} % In super draft mode we don't do anything else. % \begin{macrocode} \bool_if:NF \l_@@_super_draft_bool { % \end{macrocode} % In draft mode we draw labels at the ends of the strands; this also handles splitting curves to avoid self-intersections of Bezier curves if that's requested. % \begin{macrocode} \int_step_function:nnnN {1} {1} {\l_@@_strands_int} \knot_draw_labels:n % \end{macrocode} % If we're considering self intersections we need to split the strands into filaments. % \begin{macrocode} \bool_if:NTF \l_@@_self_intersections_bool { \knot_split_strands: \int_set_eq:NN \l_@@_tmpa_int \g_@@_filaments_int \tl_set:Nn \l_@@_prefix_tl {filament} } { \int_set_eq:NN \l_@@_tmpa_int \l_@@_strands_int \tl_set:Nn \l_@@_prefix_tl {strand} } % \end{macrocode} % Initialise the intersection count. % \begin{macrocode} \int_gzero:N \g_@@_intersections_int % \end{macrocode} % If in draft mode we label the intersections, otherwise we just stick a coordinate at each one. % \begin{macrocode} \tl_clear:N \l_@@_node_tl \bool_if:NT \l_@@_draft_bool { \tl_set:Nn \l_@@_node_tl { \exp_not:N \node[coordinate, pin={[ node~ contents={\int_use:N \g_@@_intersections_int}, knot~ diagram/draft/crossing~ label, knot~ diagram/draft/crossing~ \int_use:N \g_@@_intersections_int \c_space_tl label/.try ] }] } } % \end{macrocode} % This double loop steps through the pieces (strands or filaments) and computes the intersections and does stuff with those. % \begin{macrocode} \int_step_variable:nnnNn {1} {1} {\l_@@_tmpa_int - 1} \l_@@_tmpa_tl { \int_step_variable:nnnNn {\tl_use:N \l_@@_tmpa_tl + 1} {1} {\l_@@_tmpa_int} \l_@@_tmpb_tl { \knot_intersections:VV \l_@@_tmpa_tl \l_@@_tmpb_tl } } % \end{macrocode} % If any redraws were requested, do them here. % \begin{macrocode} \tl_use:N \l_@@_redraws_tl % \end{macrocode} % Draw the crossing nodes % \begin{macrocode} \seq_use:Nn \g_@@_nodes_seq {} } % \end{macrocode} % Close the scope % \begin{macrocode} \endpgfscope } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_draw_strand:n} % This renders a strand using the options originally specified. % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_draw_strand:n #1 { \pgfscope \group_begin: \spath_bake_round:c {knot strand #1} \tl_set:Nn \l_@@_tmpa_tl {knot~ diagram/every~ strand/.try,} \tl_put_right:Nv \l_@@_tmpa_tl {l_@@_options_strand #1} \tl_put_right:Nn \l_@@_tmpa_tl { , knot~ diagram/only~ when~ rendering/.try, only~ when~ rendering/.try } \spath_tikz_path:Vv \l_@@_tmpa_tl {knot strand #1} \group_end: \endpgfscope } \cs_generate_variant:Nn \tl_put_right:Nn {Nv} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_draw_labels:n} % Draw a label at each end of each strand, if in draft mode. % Also, if requested, split potentially self intersecting Bezier curves. % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_draw_labels:n #1 { \bool_if:NT \l_@@_draft_bool { \spath_finalpoint:Nv \l_@@_tmpb_tl {knot strand #1} \dim_set:Nn \l_@@_tmpa_dim {\tl_item:Nn \l_@@_tmpb_tl {1}} \dim_set:Nn \l_@@_tmpb_dim {\tl_item:Nn \l_@@_tmpb_tl {2}} \node[ knot~ diagram/draft/strand~label ] at (\l_@@_tmpa_dim,\l_@@_tmpb_dim) {#1}; \spath_initialpoint:Nv \l_@@_tmpb_tl {knot strand #1} \dim_set:Nn \l_@@_tmpa_dim {\tl_item:Nn \l_@@_tmpb_tl {1}} \dim_set:Nn \l_@@_tmpb_dim {\tl_item:Nn \l_@@_tmpb_tl {2}} \node[ knot~ diagram/draft/strand~label ] at (\l_@@_tmpa_dim,\l_@@_tmpb_dim) {#1}; } \bool_if:nT { \l_@@_self_intersections_bool && \l_@@_splits_bool } { \tl_clear:N \l_@@_tmpa_tl \spath_initialpoint:Nv \l_@@_tmpa_tl {knot strand #1} \tl_put_left:NV \l_@@_tmpa_tl \c_spath_moveto_tl \spath_segments_to_seq:Nv \l_@@_segments_seq {knot strand #1} \seq_map_function:NN \l_@@_segments_seq \knot_split_self_intersects:N \tl_set_eq:cN {knot strand #1} \l_@@_tmpa_tl } } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_split_self_intersects:N} % This is the macro that does the split. % Figuring out whether a Bezier cubic self intersects is apparently a difficult problem so we don't bother. % We compute a point such that if there is an intersection then it lies on either side of the point. % I don't recall where the formula came from! % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_split_self_intersects:N #1 { \tl_set:Nx \l_@@_tmpc_tl {\tl_item:nn {#1} {4}} \tl_case:NnF \l_@@_tmpc_tl { \c_spath_curvetoa_tl { \fp_set:Nn \l_@@_tmpa_fp { (\tl_item:nn {#1} {3} - 3 * \tl_item:nn {#1} {6} + 3 * \tl_item:nn {#1} {9} - \tl_item:nn {#1} {12}) * (3 * \tl_item:nn {#1} {8} - 3 * \tl_item:nn {#1} {11}) - (\tl_item:nn {#1} {2} - 3 * \tl_item:nn {#1} {5} + 3 * \tl_item:nn {#1} {8} - \tl_item:nn {#1} {11}) * (3 * \tl_item:nn {#1} {9} - 3 * \tl_item:nn {#1} {12}) } \fp_set:Nn \l_@@_tmpb_fp { (\tl_item:nn {#1} {2} - 3 * \tl_item:nn {#1} {5} + 3 * \tl_item:nn {#1} {8} - \tl_item:nn {#1} {11}) * (3 * \tl_item:nn {#1} {6} - 6 * \tl_item:nn {#1} {9} + 3 * \tl_item:nn {#1} {12}) - (\tl_item:nn {#1} {3} - 3 * \tl_item:nn {#1} {6} + 3 * \tl_item:nn {#1} {9} - \tl_item:nn {#1} {12}) * (3 * \tl_item:nn {#1} {5} - 6 * \tl_item:nn {#1} {8} + 3 * \tl_item:nn {#1} {11}) } \fp_compare:nTF { \l_@@_tmpb_fp != 0 } { \fp_set:Nn \l_@@_tmpa_fp {.5 * \l_@@_tmpa_fp / \l_@@_tmpb_fp} \fp_compare:nTF { 0 < \l_@@_tmpa_fp && \l_@@_tmpa_fp < 1 } { \spath_split_curve:NNnV \l_@@_tmpc_tl \l_@@_tmpd_tl {#1} \l_@@_tmpa_fp \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_set:Nx \l_@@_tmpd_tl {\tl_tail:N \l_@@_tmpd_tl} \tl_set:Nx \l_@@_tmpd_tl {\tl_tail:N \l_@@_tmpd_tl} \tl_set:Nx \l_@@_tmpd_tl {\tl_tail:N \l_@@_tmpd_tl} \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpc_tl \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpd_tl } { \tl_set:Nn \l_@@_tmpc_tl {#1} \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpc_tl } } { \tl_set:Nn \l_@@_tmpc_tl {#1} \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpc_tl } } \c_spath_lineto_tl { \tl_set:Nn \l_@@_tmpc_tl {#1} \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpc_tl } } { \tl_put_right:Nn \l_@@_tmpa_tl {#1} } } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_intersections:nn} % This computes the intersections of two pieces and steps through them. % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_intersections:nn #1#2 { \group_begin: \tl_set_eq:NN \l_@@_tmpa_tl \l_@@_prefix_tl \tl_put_right:Nn \l_@@_tmpa_tl {#1} \tl_set_eq:NN \l_@@_tmpb_tl \l_@@_prefix_tl \tl_put_right:Nn \l_@@_tmpb_tl {#2} \tl_set_eq:Nc \l_@@_tmpc_tl {knot \tl_use:N \l_@@_tmpa_tl} \tl_set_eq:Nc \l_@@_tmpd_tl {knot \tl_use:N \l_@@_tmpb_tl} \bool_if:nTF { \l_@@_save_bool && \tl_if_exist_p:c { knot~ intersections~ \tl_use:N \l_@@_name_tl - \tl_use:N \l_@@_tmpa_tl - \tl_use:N \l_@@_tmpb_tl } } { \tl_use:c { knot~ intersections~ \tl_use:N \l_@@_name_tl - \tl_use:N \l_@@_tmpa_tl - \tl_use:N \l_@@_tmpb_tl } } { \pgfintersectionofpaths{\pgfsetpath\l_@@_tmpc_tl}{\pgfsetpath\l_@@_tmpd_tl} } \int_compare:nT {\pgfintersectionsolutions > 0} { \int_step_function:nnnN {1} {1} {\pgfintersectionsolutions} \knot_do_intersection:n } \knot_save_intersections:VV \l_@@_tmpa_tl \l_@@_tmpb_tl \group_end: } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_save_intersections:nn} % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_save_intersections:nn #1#2 { \bool_if:NT \l_@@_save_bool { \tl_clear:N \l_@@_aux_tl \tl_put_right:Nn \l_@@_aux_tl { \def\pgfintersectionsolutions } \tl_put_right:Nx \l_@@_aux_tl { {\int_eval:n {\pgfintersectionsolutions}} } \int_compare:nT {\pgfintersectionsolutions > 0} { \int_step_inline:nnnn {1} {1} {\pgfintersectionsolutions} { \pgfpointintersectionsolution{##1} \dim_set:Nn \l_@@_tmpa_dim {\pgf@x} \dim_set:Nn \l_@@_tmpb_dim {\pgf@y} \tl_put_right:Nn \l_@@_aux_tl { \expandafter\def\csname pgfpoint@intersect@solution@##1\endcsname } \tl_put_right:Nx \l_@@_aux_tl { { \exp_not:N \pgf@x = \dim_use:N \l_@@_tmpa_dim \exp_not:N \relax \exp_not:N \pgf@y = \dim_use:N \l_@@_tmpb_dim \exp_not:N \relax } } } \tl_set:Nn \l_@@_auxa_tl {\expandafter \gdef \csname knot~ intersections~} \tl_put_right:Nx \l_@@_auxa_tl {\tl_use:N \l_@@_name_tl - #1 - #2} \tl_put_right:Nn \l_@@_auxa_tl {\endcsname} \tl_put_right:Nx \l_@@_auxa_tl {{\tl_to_str:N \l_@@_aux_tl}} \protected@write\@auxout{}{\tl_to_str:N \l_@@_auxa_tl} } } } \cs_generate_variant:Nn \knot_save_intersections:nn {VV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_do_intersection:n} % This handles a specific intersection. % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_do_intersection:n #1 { % \end{macrocode} % Get the intersection coordinates. % \begin{macrocode} \pgfpointintersectionsolution{#1} \dim_set:Nn \l_@@_tmpa_dim {\pgf@x} \dim_set:Nn \l_@@_tmpb_dim {\pgf@y} % \end{macrocode} % If we're dealing with filaments, we can get false positives from the end points. % \begin{macrocode} \bool_set_false:N \l_@@_skip_bool \bool_if:NT \l_@@_self_intersections_bool { % \end{macrocode} % If one filament preceded the other, test for the intersection being at the relevant end point. % \begin{macrocode} \tl_set:Nn \l_@@_tmpc_tl {knot previous} \tl_put_right:NV \l_@@_tmpc_tl \l_@@_tmpa_tl \tl_set:Nv \l_@@_tmpc_tl \l_@@_tmpc_tl \tl_if_eq:NNT \l_@@_tmpc_tl \l_@@_tmpb_tl { \knot_test_endpoint:NVnT \l_@@_tolerance_dim \l_@@_tmpb_tl {final point} { \bool_set_true:N \l_@@_skip_bool } } \tl_set:Nn \l_@@_tmpc_tl {knot previous} \tl_put_right:NV \l_@@_tmpc_tl \l_@@_tmpb_tl \tl_set:Nv \l_@@_tmpc_tl \l_@@_tmpc_tl \tl_if_eq:NNT \l_@@_tmpc_tl \l_@@_tmpa_tl { \knot_test_endpoint:NVnT \l_@@_tolerance_dim \l_@@_tmpa_tl {final point} { \bool_set_true:N \l_@@_skip_bool } } } % \end{macrocode} % The user can also say that end points of filaments (or strands) should simply be ignored anyway. % \begin{macrocode} \bool_if:NT \l_@@_ignore_ends_bool { \knot_test_endpoint:NVnT \l_@@_tolerance_dim \l_@@_tmpa_tl {initial point} { \bool_set_true:N \l_@@_skip_bool } \knot_test_endpoint:NVnT \l_@@_tolerance_dim \l_@@_tmpa_tl {final point} { \bool_set_true:N \l_@@_skip_bool } \knot_test_endpoint:NVnT \l_@@_tolerance_dim \l_@@_tmpb_tl {initial point} { \bool_set_true:N \l_@@_skip_bool } \knot_test_endpoint:NVnT \l_@@_tolerance_dim \l_@@_tmpb_tl {final point} { \bool_set_true:N \l_@@_skip_bool } } % \end{macrocode} % Assuming that we passed all the above tests, we render the crossing. % \begin{macrocode} \bool_if:NF \l_@@_skip_bool { \int_gincr:N \g_@@_intersections_int % \end{macrocode} % This is the intersection test. % If the intersection finder finds too many, it might be useful to ignore some. % \begin{macrocode} \bool_if:nF { \tl_if_exist_p:c {l_@@_ignore_crossing_ \int_use:N \g_@@_intersections_int} && ! \tl_if_empty_p:c {l_@@_ignore_crossing_ \int_use:N \g_@@_intersections_int} } { % \end{macrocode} % This is the flip test. % We only render one of the paths. % The ``flip'' swaps which one we render. % \begin{macrocode} \bool_if:nTF { \tl_if_exist_p:c {l_@@_crossing_ \int_use:N \g_@@_intersections_int} && ! \tl_if_empty_p:c {l_@@_crossing_ \int_use:N \g_@@_intersections_int} } { \tl_set_eq:NN \l_@@_tmpg_tl \l_@@_tmpb_tl } { \tl_set_eq:NN \l_@@_tmpg_tl \l_@@_tmpa_tl } % \end{macrocode} % Now we know which one we're rendering, we test to see if we should also render its predecessor or successor to ensure that we render a path through the entire crossing region. % \begin{macrocode} \bool_if:NT \l_@@_self_intersections_bool { \knot_test_endpoint:NVnT \l_@@_redraw_tolerance_dim \l_@@_tmpg_tl {initial point} { \bool_set_true:N \l_@@_prepend_prev_bool } { \bool_set_false:N \l_@@_prepend_prev_bool } \knot_test_endpoint:NVnT \l_@@_redraw_tolerance_dim \l_@@_tmpg_tl {final point} { \bool_set_true:N \l_@@_append_next_bool } { \bool_set_false:N \l_@@_append_next_bool } % \end{macrocode} % If either of those tests succeeded, do the appending or prepending. % \begin{macrocode} \bool_if:nT { \l_@@_prepend_prev_bool || \l_@@_append_next_bool } { \tl_clear_new:c {knot \tl_use:N \l_@@_prefix_tl -1} \tl_set_eq:cc {knot \tl_use:N \l_@@_prefix_tl -1} {knot \tl_use:N \l_@@_tmpg_tl} \tl_clear_new:c {l_@@_options_ \tl_use:N \l_@@_prefix_tl -1} \tl_set_eq:cc {l_@@_options_ \tl_use:N \l_@@_prefix_tl -1} {l_@@_options_ \tl_use:N \l_@@_tmpg_tl} \bool_if:nT { \l_@@_prepend_prev_bool && \tl_if_exist_p:c {knot previous \tl_use:N \l_@@_tmpg_tl} && !\tl_if_empty_p:c {knot previous \tl_use:N \l_@@_tmpg_tl} } { \spath_prepend_no_move:cv {knot \tl_use:N \l_@@_prefix_tl -1} {knot \tl_use:c {knot previous \tl_use:N \l_@@_tmpg_tl}} % \end{macrocode} % If we split potentially self intersecting curves, we test to see if we should prepend yet another segment. % \begin{macrocode} \bool_if:nT { \l_@@_splits_bool && \tl_if_exist_p:c {knot previous \tl_use:N \l_@@_tmpg_tl} && !\tl_if_empty_p:c {knot previous \tl_use:N \l_@@_tmpg_tl} } { \knot_test_endpoint:NvnT \l_@@_redraw_tolerance_dim {knot previous \tl_use:N \l_@@_tmpg_tl} {initial point} { \spath_prepend_no_move:cv {knot \tl_use:N \l_@@_prefix_tl -1} {knot \tl_use:c {knot previous \tl_use:c {knot previous \tl_use:N \l_@@_tmpg_tl} } } \tl_set_eq:Nc \l_@@_tmpa_tl {knot \tl_use:N \l_@@_prefix_tl -1} } } } % \end{macrocode} % Now the same for appending. % \begin{macrocode} \bool_if:nT { \l_@@_append_next_bool && \tl_if_exist_p:c {knot next \tl_use:N \l_@@_tmpg_tl} && !\tl_if_empty_p:c {knot previous \tl_use:N \l_@@_tmpg_tl} } { \spath_append_no_move:cv {knot \tl_use:N \l_@@_prefix_tl -1} {knot \tl_use:c {knot next \tl_use:N \l_@@_tmpg_tl}} \bool_if:nT { \l_@@_splits_bool && \tl_if_exist_p:c {knot previous \tl_use:N \l_@@_tmpg_tl} && !\tl_if_empty_p:c {knot previous \tl_use:N \l_@@_tmpg_tl} } { \knot_test_endpoint:NvnT \l_@@_redraw_tolerance_dim {knot previous \tl_use:N \l_@@_tmpg_tl} {final point} { \spath_append_no_move:cv {knot \tl_use:N \l_@@_prefix_tl -1} {knot \tl_use:c {knot next \tl_use:c {knot next \tl_use:N \l_@@_tmpg_tl} } } } } } \tl_set:Nn \l_@@_tmpg_tl {\tl_use:N \l_@@_prefix_tl -1} } } % \end{macrocode} % Now we render the crossing. % \begin{macrocode} \pgfscope \group_begin: \tikzset{ knot~ diagram/every~ intersection/.try, every~ intersection/.try, knot~ diagram/intersection~ \int_use:N \g_@@_intersections_int/.try } \knot_draw_crossing:VVV \l_@@_tmpg_tl \l_@@_tmpa_dim \l_@@_tmpb_dim \coordinate (\l_@@_name_tl \c_space_tl \int_use:N \g_@@_intersections_int) at (\dim_use:N \l_@@_tmpa_dim, \dim_use:N \l_@@_tmpb_dim); \group_end: \endpgfscope % \end{macrocode} % This ends the boolean as to whether to consider the intersection at all % \begin{macrocode} } % \end{macrocode} % And possibly stick a coordinate with a label at the crossing. % \begin{macrocode} \tl_if_empty:NF \l_@@_node_tl { \seq_gpush:Nx \g_@@_nodes_seq { \l_@@_node_tl at (\dim_use:N \l_@@_tmpa_dim, \dim_use:N \l_@@_tmpb_dim) {}; } } } } \cs_generate_variant:Nn \knot_intersections:nn {VV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_test_endpoint:N} % Test whether the point is near the intersection point. % \begin{macrocode} \prg_new_conditional:Npnn \knot_test_endpoint:NN #1#2 {p,T,F,TF} { \dim_compare:nTF { \dim_abs:n { \l_@@_tmpa_dim - \tl_item:Nn #2 {1}} + \dim_abs:n { \l_@@_tmpb_dim - \tl_item:Nn #2 {2}} < #1 } { \prg_return_true: } { \prg_return_false: } } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_test_endpoint:nn} % Wrapper around the above. % \begin{macrocode} \prg_new_protected_conditional:Npnn \knot_test_endpoint:Nnn #1#2#3 {T,F,TF} { \use:c {spath_#3:Nv} \l_@@_tmpd_tl {knot #2} \knot_test_endpoint:NNTF #1 \l_@@_tmpd_tl { \prg_return_true: } { \prg_return_false: } } \cs_generate_variant:Nn \knot_test_endpoint:NnnT {NVnT,NvnT} \cs_generate_variant:Nn \knot_test_endpoint:NnnF {NVnF,NvnF} \cs_generate_variant:Nn \knot_test_endpoint:NnnTF {NVnTF,NvnTF} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_draw_crossing:nnn} % This is the code that actually renders a crossing. % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_draw_crossing:nnn #1#2#3 { \group_begin: \pgfscope \path[knot~ diagram/background~ clip] (#2, #3) circle[radius=\l_@@_clip_bg_radius_dim]; \tl_set:Nn \l_@@_tmpa_tl {knot~ diagram/every~ strand/.try,} \tl_if_exist:cT {l_@@_options_ #1} { \tl_put_right:Nv \l_@@_tmpa_tl {l_@@_options_ #1} } \tl_put_right:Nn \l_@@_tmpa_tl { ,knotbg ,line~ width= \tl_use:N \l_@@_clip_width_tl * \pgflinewidth } \spath_tikz_path:Vv \l_@@_tmpa_tl {knot #1} \endpgfscope \pgfscope \path[knot~ diagram/clip] (#2, #3) circle[radius=\l_@@_clip_draw_radius_dim]; \tl_set:Nn \l_@@_tmpa_tl {knot~ diagram/every~ strand/.try,} \tl_if_exist:cT {l_@@_options_ #1} { \tl_put_right:Nv \l_@@_tmpa_tl {l_@@_options_ #1} } \tl_put_right:Nn \l_@@_tmpa_tl { ,knot~ diagram/only~ when~ rendering/.try ,only~ when~ rendering/.try } \spath_tikz_path:Vv \l_@@_tmpa_tl {knot #1} \endpgfscope \group_end: } \cs_generate_variant:Nn \knot_draw_crossing:nnn {nVV, VVV} \cs_new_protected_nopar:Npn \knot_draw_crossing:nn #1#2 { \tikz@scan@one@point\pgfutil@firstofone #2 \relax \knot_draw_crossing:nVV {#1} \pgf@x \pgf@y } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_split_strands:} % This, and the following macros, are for splitting strands into filaments. % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_split_strands: { \int_gzero:N \g_@@_filaments_int \int_step_function:nnnN {1} {1} {\l_@@_strands_int} \knot_split_strand:n \int_step_function:nnnN {1} {1} {\g_@@_filaments_int} \knot_compute_nexts:n } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_compute_nexts:n} % Each filament needs to know its predecessor and successor. % We work out the predecessors as we go along, this fills in the successors. % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_compute_nexts:n #1 { \tl_clear_new:c {knot next \tl_use:c {knot previous filament #1}} \tl_set:cn {knot next \tl_use:c {knot previous filament #1}} {filament #1} } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_split_strand:n} % Sets up the split for a single strand. % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_split_strand:n #1 { \int_set_eq:NN \l_@@_component_start_int \g_@@_filaments_int \int_incr:N \l_@@_component_start_int \tl_set_eq:Nc \l_@@_tmpa_tl {l_@@_options_strand #1} \spath_segments_to_seq:Nv \l_@@_segments_seq {knot strand #1} \seq_map_function:NN \l_@@_segments_seq \knot_save_filament:N } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_save_filament:N} % Saves a filament as a new \Verb+spath+ object. % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_save_filament:N #1 { \tl_set:Nx \l_@@_tmpb_tl {\tl_item:nn {#1} {4}} \tl_case:NnF \l_@@_tmpb_tl { \c_spath_moveto_tl { \int_compare:nT {\l_@@_component_start_int < \g_@@_filaments_int} { \int_set_eq:NN \l_@@_component_start_int \g_@@_filaments_int } } \c_spath_lineto_tl { \int_gincr:N \g_@@_filaments_int \tl_clear_new:c {knot filament \int_use:N \g_@@_filaments_int} \tl_set:cn {knot filament \int_use:N \g_@@_filaments_int} {#1} \tl_clear_new:c {l_@@_options_filament \int_use:N \g_@@_filaments_int} \tl_set_eq:cN {l_@@_options_filament \int_use:N \g_@@_filaments_int} \l_@@_tmpa_tl \tl_clear_new:c {knot previous filament \int_use:N \g_@@_filaments_int} \int_compare:nF {\l_@@_component_start_int == \g_@@_filaments_int} { \tl_set:cx {knot previous filament \int_use:N \g_@@_filaments_int} {filament \int_eval:n {\g_@@_filaments_int - 1}} } } \c_spath_curvetoa_tl { \int_gincr:N \g_@@_filaments_int \tl_clear_new:c {knot filament \int_use:N \g_@@_filaments_int} \tl_set:cn {knot filament \int_use:N \g_@@_filaments_int} {#1} \tl_clear_new:c {l_@@_options_filament \int_use:N \g_@@_filaments_int} \tl_set_eq:cN {l_@@_options_filament \int_use:N \g_@@_filaments_int} \l_@@_tmpa_tl \tl_clear_new:c {knot previous filament \int_use:N \g_@@_filaments_int} \int_compare:nF {\l_@@_component_start_int == \g_@@_filaments_int} { \tl_set:cx {knot previous filament \int_use:N \g_@@_filaments_int} {filament \int_eval:n {\g_@@_filaments_int - 1}} } } \c_spath_closepath_tl { \int_gincr:N \g_@@_filaments_int \tl_clear_new:c {knot filament \int_use:N \g_@@_filaments_int} \tl_clear:N \l_@@_tmpa_tl \tl_put_right:Nx { \tl_item:nn {#1} {1}\tl_item:nn {#1} {2}\tl_item:nn {#1} {3} } \tl_put_right:NV \l_@@_tmpa_tl \c_spath_lineto_tl \tl_put_right:Nx {\tl_item:nn {#1} {5}\tl_item:nn {#1} {6}} \tl_set:cV {knot filament \int_use:N \g_@@_filaments_int} \l_@@_tmpa_tl \tl_set_eq:cN {l_@@_options_filament \int_use:N \g_@@_filaments_int} \l_@@_tmpa_tl \tl_clear_new:c {knot previous filament \int_use:N \g_@@_filaments_int} \int_compare:nF {\l_@@_component_start_int == \g_@@_filaments_int} { \tl_set:cx {knot previous filament \int_use:N \g_@@_filaments_int} {filament \int_eval:n {\g_@@_filaments_int - 1}} } \tl_set:cx {knot previous filament \int_use:N \l_@@_component_start_int} {filament \int_use:N \g_@@_filaments_int} } } { } } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\redraw} % The user can redraw segments of the strands at specific locations. % \begin{macrocode} \NewDocumentCommand \redraw { m m } { % \tikz@scan@one@point\pgfutil@firstofone #2 \relax \tl_put_right:Nn \l_@@_redraws_tl {\knot_draw_crossing:nn} \tl_put_right:Nx \l_@@_redraws_tl { {strand #1} {#2}% {\dim_use:N \pgf@x} {\dim_use:N \pgf@y} } } % \end{macrocode} % \end{macro} % % \begin{macrocode} \ExplSyntaxOff % \end{macrocode} %<@@=> % % \begin{macro}[internal]{\pgf@sh@@knotanchor} % Add the extra anchors for the knot crossing nodes. % \begin{macrocode} \def\pgf@sh@@knotanchor#1#2{% \anchor{#2 north west}{% \csname pgf@anchor@knot #1@north west\endcsname% \pgf@x=#2\pgf@x% \pgf@y=#2\pgf@y% }% \anchor{#2 north east}{% \csname pgf@anchor@knot #1@north east\endcsname% \pgf@x=#2\pgf@x% \pgf@y=#2\pgf@y% }% \anchor{#2 south west}{% \csname pgf@anchor@knot #1@south west\endcsname% \pgf@x=#2\pgf@x% \pgf@y=#2\pgf@y% }% \anchor{#2 south east}{% \csname pgf@anchor@knot #1@south east\endcsname% \pgf@x=#2\pgf@x% \pgf@y=#2\pgf@y% }% \anchor{#2 north}{% \csname pgf@anchor@knot #1@north\endcsname% \pgf@x=#2\pgf@x% \pgf@y=#2\pgf@y% }% \anchor{#2 east}{% \csname pgf@anchor@knot #1@east\endcsname% \pgf@x=#2\pgf@x% \pgf@y=#2\pgf@y% }% \anchor{#2 west}{% \csname pgf@anchor@knot #1@west\endcsname% \pgf@x=#2\pgf@x% \pgf@y=#2\pgf@y% }% \anchor{#2 south}{% \csname pgf@anchor@knot #1@south\endcsname% \pgf@x=#2\pgf@x% \pgf@y=#2\pgf@y% }% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{knot crossing} % \begin{macrocode} \pgfdeclareshape{knot crossing} { \inheritsavedanchors[from=circle] % this is nearly a circle \inheritanchorborder[from=circle] \inheritanchor[from=circle]{north} \inheritanchor[from=circle]{north west} \inheritanchor[from=circle]{north east} \inheritanchor[from=circle]{center} \inheritanchor[from=circle]{west} \inheritanchor[from=circle]{east} \inheritanchor[from=circle]{mid} \inheritanchor[from=circle]{mid west} \inheritanchor[from=circle]{mid east} \inheritanchor[from=circle]{base} \inheritanchor[from=circle]{base west} \inheritanchor[from=circle]{base east} \inheritanchor[from=circle]{south} \inheritanchor[from=circle]{south west} \inheritanchor[from=circle]{south east} \inheritanchorborder[from=circle] \pgf@sh@@knotanchor{crossing}{2} \pgf@sh@@knotanchor{crossing}{3} \pgf@sh@@knotanchor{crossing}{4} \pgf@sh@@knotanchor{crossing}{8} \pgf@sh@@knotanchor{crossing}{16} \pgf@sh@@knotanchor{crossing}{32} \backgroundpath{ \pgfutil@tempdima=\radius% \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/outer xsep}}% \pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/outer ysep}}% \ifdim\pgf@xb<\pgf@yb% \advance\pgfutil@tempdima by-\pgf@yb% \else% \advance\pgfutil@tempdima by-\pgf@xb% \fi% } } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{knot over cross} % \begin{macrocode} \pgfdeclareshape{knot over cross} { \inheritsavedanchors[from=rectangle] % this is nearly a circle \inheritanchorborder[from=rectangle] \inheritanchor[from=rectangle]{north} \inheritanchor[from=rectangle]{north west} \inheritanchor[from=rectangle]{north east} \inheritanchor[from=rectangle]{center} \inheritanchor[from=rectangle]{west} \inheritanchor[from=rectangle]{east} \inheritanchor[from=rectangle]{mid} \inheritanchor[from=rectangle]{mid west} \inheritanchor[from=rectangle]{mid east} \inheritanchor[from=rectangle]{base} \inheritanchor[from=rectangle]{base west} \inheritanchor[from=rectangle]{base east} \inheritanchor[from=rectangle]{south} \inheritanchor[from=rectangle]{south west} \inheritanchor[from=rectangle]{south east} \inheritanchorborder[from=rectangle] \backgroundpath{ \southwest \pgf@xa=\pgf@x \pgf@ya=\pgf@y \northeast \pgf@xb=\pgf@x \pgf@yb=\pgf@y \pgfpathmoveto{\pgfqpoint{\pgf@xa}{\pgf@ya}} \pgfpathlineto{\pgfqpoint{\pgf@xb}{\pgf@yb}} } \foregroundpath{ % store lower right in xa/ya and upper right in xb/yb \southwest \pgf@xa=\pgf@x \pgf@ya=\pgf@y \northeast \pgf@xb=\pgf@x \pgf@yb=\pgf@y \pgfpathmoveto{\pgfqpoint{\pgf@xa}{\pgf@yb}} \pgfpathlineto{\pgfqpoint{\pgf@xb}{\pgf@ya}} } } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{knot under cross} % \begin{macrocode} \pgfdeclareshape{knot under cross} { \inheritsavedanchors[from=rectangle] % this is nearly a circle \inheritanchorborder[from=rectangle] \inheritanchor[from=rectangle]{north} \inheritanchor[from=rectangle]{north west} \inheritanchor[from=rectangle]{north east} \inheritanchor[from=rectangle]{center} \inheritanchor[from=rectangle]{west} \inheritanchor[from=rectangle]{east} \inheritanchor[from=rectangle]{mid} \inheritanchor[from=rectangle]{mid west} \inheritanchor[from=rectangle]{mid east} \inheritanchor[from=rectangle]{base} \inheritanchor[from=rectangle]{base west} \inheritanchor[from=rectangle]{base east} \inheritanchor[from=rectangle]{south} \inheritanchor[from=rectangle]{south west} \inheritanchor[from=rectangle]{south east} \inheritanchorborder[from=rectangle] \backgroundpath{ \southwest \pgf@xa=\pgf@x \pgf@ya=\pgf@y \northeast \pgf@xb=\pgf@x \pgf@yb=\pgf@y \pgfpathmoveto{\pgfqpoint{\pgf@xa}{\pgf@yb}} \pgfpathlineto{\pgfqpoint{\pgf@xb}{\pgf@ya}} } \foregroundpath{ % store lower right in xa/ya and upper right in xb/yb \southwest \pgf@xa=\pgf@x \pgf@ya=\pgf@y \northeast \pgf@xb=\pgf@x \pgf@yb=\pgf@y \pgfpathmoveto{\pgfqpoint{\pgf@xa}{\pgf@ya}} \pgfpathlineto{\pgfqpoint{\pgf@xb}{\pgf@yb}} } } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{knot vert} % \begin{macrocode} \pgfdeclareshape{knot vert} { \inheritsavedanchors[from=rectangle] % this is nearly a circle \inheritanchorborder[from=rectangle] \inheritanchor[from=rectangle]{north} \inheritanchor[from=rectangle]{north west} \inheritanchor[from=rectangle]{north east} \inheritanchor[from=rectangle]{center} \inheritanchor[from=rectangle]{west} \inheritanchor[from=rectangle]{east} \inheritanchor[from=rectangle]{mid} \inheritanchor[from=rectangle]{mid west} \inheritanchor[from=rectangle]{mid east} \inheritanchor[from=rectangle]{base} \inheritanchor[from=rectangle]{base west} \inheritanchor[from=rectangle]{base east} \inheritanchor[from=rectangle]{south} \inheritanchor[from=rectangle]{south west} \inheritanchor[from=rectangle]{south east} \inheritanchorborder[from=rectangle] \backgroundpath{ % store lower right in xa/ya and upper right in xb/yb \southwest \pgf@xa=\pgf@x \pgf@ya=\pgf@y \northeast \pgf@xb=\pgf@x \pgf@yb=\pgf@y \pgfpathmoveto{\pgfqpoint{\pgf@xa}{\pgf@ya}} \pgfpathlineto{\pgfqpoint{\pgf@xa}{\pgf@yb}} \pgfpathmoveto{\pgfqpoint{\pgf@xb}{\pgf@yb}} \pgfpathlineto{\pgfqpoint{\pgf@xb}{\pgf@ya}} } } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{knot horiz} % \begin{macrocode} \pgfdeclareshape{knot horiz} { \inheritsavedanchors[from=rectangle] % this is nearly a circle \inheritanchorborder[from=rectangle] \inheritanchor[from=rectangle]{north} \inheritanchor[from=rectangle]{north west} \inheritanchor[from=rectangle]{north east} \inheritanchor[from=rectangle]{center} \inheritanchor[from=rectangle]{west} \inheritanchor[from=rectangle]{east} \inheritanchor[from=rectangle]{mid} \inheritanchor[from=rectangle]{mid west} \inheritanchor[from=rectangle]{mid east} \inheritanchor[from=rectangle]{base} \inheritanchor[from=rectangle]{base west} \inheritanchor[from=rectangle]{base east} \inheritanchor[from=rectangle]{south} \inheritanchor[from=rectangle]{south west} \inheritanchor[from=rectangle]{south east} \inheritanchorborder[from=rectangle] \foregroundpath{ % store lower right in xa/ya and upper right in xb/yb \southwest \pgf@xa=\pgf@x \pgf@ya=\pgf@y \northeast \pgf@xb=\pgf@x \pgf@yb=\pgf@y \pgfpathmoveto{\pgfqpoint{\pgf@xa}{\pgf@ya}} \pgfpathlineto{\pgfqpoint{\pgf@xb}{\pgf@ya}} \pgfpathmoveto{\pgfqpoint{\pgf@xa}{\pgf@yb}} \pgfpathlineto{\pgfqpoint{\pgf@xb}{\pgf@yb}} } } % \end{macrocode} % \end{macro} % % \iffalse % % \fi %\Finale \endinput