% \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{10189} % % \CharacterTable % {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z % Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z % Digits \0\1\2\3\4\5\6\7\8\9 % Exclamation \! Double quote \" Hash (number) \# % Dollar \$ Percent \% Ampersand \& % Acute accent \' Left paren \( Right paren \) % Asterisk \* Plus \+ Comma \, % Minus \- Point \. Solidus \/ % Colon \: Semicolon \; Less than \< % Equals \= Greater than \> Question mark \? % Commercial at \@ Left bracket \[ Backslash \\ % Right bracket \] Circumflex \^ Underscore \_ % Grave accent \` Left brace \{ Vertical bar \| % Right brace \} Tilde \~} % % % \changes{1.0}{2011/05/03}{Converted to DTX file} % \changes{1.1}{2016/02/19}{Fixes due to changes in LaTeX3} % \changes{1.2}{2019/02/12}{More fixes due to changes in LaTeX3} % \changes{1.4}{2020/12/18}{A fair amount of code reimplementation and reorganisation, together with defining TikZ keys to make functions available for use.} % \changes{2.0}{2021/01/19}{Refactored the code to remove the OO approach and make it functional, introduced the spath3 TikZ library to provide a user interface.} % \changes{2.2}{2021/02/05}{Bugfixes and improvements, mainly with regard to the intersection and splitting routines.} % \changes{2.4}{2021/02/21}{Rejigged how the routines for using paths were implemented, added some more routines for joining paths.} % \changes{2.6}{2021/11/23}{Modified core routines to cope with a "true rectangle" path; added routines for splitting at a parametrised point.} % \changes{2.7}{2022/08/24}{Bug fixes} % % \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} {2022/08/24} {2.7} {Functions for manipulating PGF soft paths} \RequirePackage{xparse} % \end{macrocode} % % Utilities copied from \url{https://github.com/loopspace/LaTeX3-Utilities} for adding something in braces to a token list. % I find I use this quite a lot in my packages. % \begin{macrocode} \cs_new_protected:Nn \@@_tl_put_right_braced:Nn { \tl_put_right:Nn #1 { { #2 } } } \cs_generate_variant:Nn \@@_tl_put_right_braced:Nn { NV, cV, cv, Nx, cx } \cs_new_protected:Nn \@@_tl_gput_right_braced:Nn { \tl_gput_right:Nn #1 { { #2 } } } \cs_generate_variant:Nn \@@_tl_gput_right_braced:Nn { NV, cV, cv, Nx, cx } \cs_new_protected:Nn \@@_tl_put_left_braced:Nn { \tl_put_left:Nn #1 { { #2 } } } \cs_generate_variant:Nn \@@_tl_put_left_braced:Nn { NV, cV, cv, Nx, cx } \cs_new_protected:Nn \@@_tl_gput_left_braced:Nn { \tl_gput_left:Nn #1 { { #2 } } } \cs_generate_variant:Nn \@@_tl_gput_left_braced:Nn { NV, cV, cv, Nx, cx } % \end{macrocode} % % % I had to think a bit about how to get \TeX\ to work the way I wanted. % I'm really defining \emph{functions} but \TeX\ doesn't really have that concept, even with all the amazing \LaTeX3 stuff. % The main issue I had was with scoping and return values. % By default, \TeX\ functions aren't scoped -- they work on the same level as the calling functions. % To protect the internals from being overwritten, each core function works inside a group. % But then I have to work to get the answer out of it. % So each of my core functions finishes by storing its return value in an appropriate \Verb+output+ variable. % The core functions are then wrapped in a more user friendly interface that will take that output and assign it to a variable. % This also means that I can deal with local and global versions without duplicating code. % % \begin{macrocode} \tl_new:N \g_@@_output_tl \int_new:N \g_@@_output_int \seq_new:N \g_@@_output_seq \bool_new:N \g_@@_output_bool % \end{macrocode} % % To avoid creating vast numbers of variables, we provide ourselves with a few that we reuse frequently. % For that reason, most of them don't have very exciting names. % % These are general purpose variables. % \begin{macrocode} \tl_new:N \l_@@_tmpa_tl \tl_new:N \l_@@_tmpb_tl \tl_new:N \l_@@_tmpc_tl \tl_new:N \l_@@_tmpd_tl \tl_new:N \l_@@_tmpe_tl \tl_new:N \l_@@_tmpf_tl \tl_new:N \l_@@_tmpg_tl \tl_new:N \l_@@_tmph_tl \tl_new:N \l_@@_tmpi_tl \seq_new:N \l_@@_tmpa_seq \seq_new:N \l_@@_tmpb_seq \seq_new:N \l_@@_tmpc_seq \dim_new:N \l_@@_tmpa_dim \dim_new:N \l_@@_tmpb_dim \fp_new:N \l_@@_tmpa_fp \fp_new:N \l_@@_tmpb_fp \fp_new:N \l_@@_tmpc_fp \fp_new:N \l_@@_tmpd_fp \fp_new:N \l_@@_tmpe_fp \fp_new:N \l_@@_tmpf_fp \int_new:N \l_@@_tmpa_int \int_new:N \l_@@_tmpb_int \bool_new:N \l_@@_tmpa_bool % \end{macrocode} % % Whenever I need more than two \Verb+dim+ variables it is because I need to remember the position of a move. % % \begin{macrocode} \dim_new:N \l_@@_move_x_dim \dim_new:N \l_@@_move_y_dim % \end{macrocode} % % Closed paths often need special handling. % When it's needed, this will say whether the path is closed or not. % % \begin{macrocode} \bool_new:N \l_@@_closed_bool % \end{macrocode} % % True rectangles are rare, but need special handling. % They are specified by two tokens, the first specifies the lower left corner which can be handled pretty much as other tokens but the second specifies the width and height meaning that it transforms differently. % So when encountering on, the coordinates of the lower left corner are useful to remember. % % \begin{macrocode} \dim_new:N \l_@@_rectx_dim \dim_new:N \l_@@_recty_dim \bool_new:N \l_@@_rect_bool % \end{macrocode} % % When restoring a path we need to know whether to update the stored moveto. % % \begin{macrocode} \bool_new:N \l_spath_movetorelevant_bool % \end{macrocode} % % The intersection routine can't happen inside a group so we need two token lists to hold the paths that we'll intersect. % % \begin{macrocode} \tl_new:N \l_@@_intersecta_tl \tl_new:N \l_@@_intersectb_tl % \end{macrocode} % % We need to be able to compare against the macros that can occur in a soft path so these token lists contain them. % These are global constants so that they can be used in other packages. % \begin{macrocode} \tl_const:Nn \c_spath_moveto_tl {\pgfsyssoftpath@movetotoken} \tl_const:Nn \c_spath_lineto_tl {\pgfsyssoftpath@linetotoken} \tl_const:Nn \c_spath_curveto_tl {\pgfsyssoftpath@curvetotoken} \tl_const:Nn \c_spath_curvetoa_tl {\pgfsyssoftpath@curvetosupportatoken} \tl_const:Nn \c_spath_curvetob_tl {\pgfsyssoftpath@curvetosupportbtoken} \tl_const:Nn \c_spath_closepath_tl {\pgfsyssoftpath@closepathtoken} \tl_const:Nn \c_spath_rectcorner_tl {\pgfsyssoftpath@rectcornertoken} \tl_const:Nn \c_spath_rectsize_tl {\pgfsyssoftpath@rectsizetoken} % \end{macrocode} % % We will want to be able to use anonymous spaths internally, so we create a global counter that we can use to refer to them. % \begin{macrocode} \int_new:N \g_@@_anon_int \int_gzero:N \g_@@_anon_int % \end{macrocode} % % \begin{macro}[internal]{ % \spath_anonymous:N, % \spath_ganonymous:N % } % Set the token list to the next anonymous name. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_anonymous:N #1 { \tl_set:Nx #1 {anonymous_\int_use:N \g_@@_anon_int} \int_gincr:N \g_@@_anon_int } \cs_new_protected_nopar:Npn \spath_ganonymous:N #1 { \tl_gset:Nx #1 {anonymous_\int_use:N \g_@@_anon_int} \int_gincr:N \g_@@_anon_int } \cs_generate_variant:Nn \spath_anonymous:N {c} \cs_generate_variant:Nn \spath_ganonymous:N {c} % \end{macrocode} % \end{macro} % % And some error messages % \begin{macrocode} \msg_new:nnn { spath3 } { unknown path construction } { The~ path~ construction~ element~ #1~ is~ not~ currently~ supported.} % \end{macrocode} % % % \subsection{Functional Implementation} % % In the functional approach, we start with a token list containing a soft path and do something to it (either calculate some information or manipulate it in some fashion). % We then store that information, or the manipulated path, in an appropriate macro. % The macro to store it in is the first argument. % These functions occur in two versions, the one with the \texttt{g} makes the assignment global. % % \begin{macro}[internal]{ % \spath_segments_to_seq:Nn, % \spath_segments_gto_seq:Nn %} % Splits a soft path into \emph{segments}, storing the result in a sequence. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_segments_to_seq:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_clear:N \l_@@_tmpb_tl \seq_clear:N \l_@@_tmpa_seq \dim_zero:N \l_@@_tmpa_dim \dim_zero:N \l_@@_tmpb_dim \bool_until_do:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \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_rectcorner_tl { \tl_set_eq:NN \l_@@_tmpb_tl \c_spath_rectcorner_tl \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpb_tl {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}} \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}} \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \c_spath_closepath_tl { \tl_set_eq:NN \l_@@_tmpb_tl \c_spath_moveto_tl \tl_put_right:Nx \l_@@_tmpb_tl { {\dim_use:N \l_@@_tmpa_dim} {\dim_use:N \l_@@_tmpb_dim} } \tl_put_right:NV \l_@@_tmpb_tl \c_spath_lineto_tl \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}} \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}} \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } } { \tl_set_eq:NN \l_@@_tmpb_tl \l_@@_tmpc_tl \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}} \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpb_tl {{\tl_head:N \l_@@_tmpa_tl}} \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \tl_if_empty:NF \l_@@_tmpb_tl { \seq_put_right:NV \l_@@_tmpa_seq \l_@@_tmpb_tl } \tl_clear:N \l_@@_tmpb_tl } \seq_gclear:N \g_@@_output_seq \seq_gset_eq:NN \g_@@_output_seq \l_@@_tmpa_seq \group_end: } \cs_new_protected_nopar:Npn \spath_segments_to_seq:Nn #1#2 { \@@_segments_to_seq:n {#2} \seq_clear_new:N #1 \seq_set_eq:NN #1 \g_@@_output_seq \seq_gclear:N \g_@@_output_seq } \cs_generate_variant:Nn \spath_segments_to_seq:Nn {NV, cn, cV, Nv, cv} \cs_new_protected_nopar:Npn \spath_segments_gto_seq:Nn #1#2 { \@@_segments_to_seq:n {#2} \seq_clear_new:N #1 \seq_gset_eq:NN #1 \g_@@_output_seq \seq_gclear:N \g_@@_output_seq } \cs_generate_variant:Nn \spath_segments_gto_seq:Nn {NV, cn, cV, Nv, cv} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_components_to_seq:Nn, % \spath_components_gto_seq:Nn, % \spath_components_to_clist:Nn, % \spath_components_gto_clist:Nn, % } % Splits a soft path into \emph{components}, storing the result in a sequence or a clist. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_components_to_seq:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \seq_clear:N \l_@@_tmpa_seq \tl_set:Nx \l_@@_tmpb_tl {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:NV \l_@@_tmpa_tl \c_spath_moveto_tl \bool_do_until:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl} \tl_if_eq:NNT \l_@@_tmpc_tl \c_spath_moveto_tl { \seq_put_right:NV \l_@@_tmpa_seq \l_@@_tmpb_tl \tl_clear:N \l_@@_tmpb_tl } \tl_if_eq:NNT \l_@@_tmpc_tl \c_spath_rectcorner_tl { \seq_put_right:NV \l_@@_tmpa_seq \l_@@_tmpb_tl \tl_clear:N \l_@@_tmpb_tl } \tl_if_single:NTF \l_@@_tmpc_tl { \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpc_tl } { \tl_put_right:Nx \l_@@_tmpb_tl {{\l_@@_tmpc_tl}} } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \seq_gclear:N \g_@@_output_seq \seq_gset_eq:NN \g_@@_output_seq \l_@@_tmpa_seq \group_end: } \cs_new_protected_nopar:Npn \spath_components_to_seq:Nn #1#2 { \@@_components_to_seq:n {#2} \seq_clear_new:N #1 \seq_set_eq:NN #1 \g_@@_output_seq \seq_gclear:N \g_@@_output_seq } \cs_generate_variant:Nn \spath_components_to_seq:Nn {NV, cn, cV, cv, Nv} \cs_new_protected_nopar:Npn \spath_components_gto_seq:Nn #1#2 { \@@_components_to_seq:n {#2} \seq_clear_new:N #1 \seq_gset_eq:NN #1 \g_@@_output_seq \seq_gclear:N \g_@@_output_seq } \cs_generate_variant:Nn \spath_components_gto_seq:Nn {NV, cn, cV, cv, Nv} \cs_new_protected_nopar:Npn \spath_components_to_clist:Nn #1#2 { \@@_components_to_seq:n {#2} \clist_clear_new:N #1 \clist_set_from_seq:NN #1 \g_@@_output_seq \seq_gclear:N \g_@@_output_seq } \cs_generate_variant:Nn \spath_components_to_clist:Nn {NV, cn, cV, cv, Nv} \cs_new_protected_nopar:Npn \spath_components_gto_clist:Nn #1#2 { \@@_components_to_seq:n {#2} \clist_clear_new:N #1 \clist_gset_from_seq:NN #1 \g_@@_output_seq \seq_gclear:N \g_@@_output_seq } \cs_generate_variant:Nn \spath_components_gto_clist:Nn {NV, cn, cV, cv, Nv} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{\spath_length:n} % Counts the number of triples in the path. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_length:n #1 { \int_eval:n {\tl_count:n {#1} / 3} } \cs_generate_variant:Nn \spath_length:n {V} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_reallength:Nn, % \spath_greallength:Nn % } % The real length of a path is the number of triples that actually draw something (that is, the number of lines, curves, rectangles, and closepaths). % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_reallength:n #1 { \group_begin: \int_set:Nn \l_@@_tmpa_int {0} \tl_map_inline:nn {#1} { \tl_set:Nn \l_@@_tmpa_tl {##1} \tl_case:NnT \l_@@_tmpa_tl { \c_spath_lineto_tl {} \c_spath_curveto_tl {} \c_spath_closepath_tl {} \c_spath_rectsize_tl {} } { \int_incr:N \l_@@_tmpa_int } } \int_gzero:N \g_@@_output_int \int_gset_eq:NN \g_@@_output_int \l_@@_tmpa_int \group_end: } \cs_new_protected_nopar:Npn \spath_reallength:Nn #1#2 { \@@_reallength:n {#2} \int_set_eq:NN #1 \g_@@_output_int \int_gzero:N \g_@@_output_int } \cs_generate_variant:Nn \spath_reallength:Nn {NV, cn, cV, Nv, cv} \cs_new_protected_nopar:Npn \spath_greallength:Nn #1#2 { \@@_reallength:n {#2} \int_gset_eq:NN #1 \g_@@_output_int \int_gzero:N \g_@@_output_int } \cs_generate_variant:Nn \spath_greallength:Nn {NV, cn, cV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_numberofcomponents:Nn, % \spath_gnumberofcomponents:Nn % } % A component is a continuous segment of the path, separated by moves. % Successive moves are not collapsed, and zero length moves count. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_numberofcomponents:n #1 { \group_begin: \int_set:Nn \l_@@_tmpa_int {0} \tl_map_inline:nn {#1} { \tl_set:Nn \l_@@_tmpa_tl {##1} \tl_case:Nn \l_@@_tmpa_tl { \c_spath_moveto_tl { \int_incr:N \l_@@_tmpa_int } \c_spath_rectcorner_tl { \int_incr:N \l_@@_tmpa_int } } } \int_gzero:N \g_@@_output_int \int_gset_eq:NN \g_@@_output_int \l_@@_tmpa_int \group_end: } \cs_new_protected_nopar:Npn \spath_numberofcomponents:Nn #1#2 { \@@_numberofcomponents:n {#2} \int_set_eq:NN #1 \g_@@_output_int \int_gzero:N \g_@@_output_int } \cs_generate_variant:Nn \spath_numberofcomponents:Nn {NV, cn, cV, Nv} \cs_new_protected_nopar:Npn \spath_gnumberofcomponents:Nn #1#2 { \@@_numberofcomponents:n {#2} \int_gset_eq:NN #1 \g_@@_output_int \int_gzero:N \g_@@_output_int } \cs_generate_variant:Nn \spath_gnumberofcomponents:Nn {NV, cn, cV, Nv} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_initialpoint:Nn, % \spath_ginitialpoint:Nn % } % The starting point of the path. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_initialpoint:n #1 { \group_begin: \tl_clear:N \l_@@_tmpa_tl \tl_set:Nx \l_@@_tmpa_tl { { \tl_item:nn {#1} {2} } { \tl_item:nn {#1} {3} } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_new_protected_nopar:Npn \spath_initialpoint:Nn #1#2 { \@@_initialpoint:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_initialpoint:Nn {NV, cn, cV, Nv} \cs_new_protected_nopar:Npn \spath_ginitialpoint:Nn #1#2 { \@@_initialpoint:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_ginitialpoint:Nn {NV, cn, cV, Nv} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_finalpoint:Nn, % \spath_gfinalpoint:Nn, % } % The final point of the path. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_finalpoint:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_reverse:N \l_@@_tmpa_tl \tl_clear:N \l_@@_tmpb_tl \tl_set:Nx \l_@@_tmpc_tl {\tl_item:Nn \l_@@_tmpa_tl {3}} \tl_if_eq:NNTF \l_@@_tmpc_tl \c_spath_rectsize_tl { \tl_set:Nx \l_@@_tmpb_tl { { \dim_eval:n { \tl_item:Nn \l_@@_tmpa_tl {2} + \tl_item:Nn \l_@@_tmpa_tl {5} } } { \dim_eval:n { \tl_item:Nn \l_@@_tmpa_tl {1} + \tl_item:Nn \l_@@_tmpa_tl {4} } } } } { \tl_set:Nx \l_@@_tmpb_tl { { \tl_item:Nn \l_@@_tmpa_tl {2} } { \tl_item:Nn \l_@@_tmpa_tl {1} } } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_finalpoint:Nn #1#2 { \@@_finalpoint:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_finalpoint:Nn {NV, cn, cV, Nv} \cs_new_protected_nopar:Npn \spath_gfinalpoint:Nn #1#2 { \@@_finalpoint:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gfinalpoint:Nn {NV, cn, cV, Nv} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_finalmovepoint:Nn, % \spath_gfinalmovepoint:Nn % } % Get the last move on the path. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_finalmovepoint:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpc_tl { {0pt} {0pt} } \tl_set:Nn \l_@@_tmpa_tl {#1} \bool_do_until:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_set:Nx \l_@@_tmpb_tl {\tl_head:N \l_@@_tmpa_tl} \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_rectcorner_tl { \tl_set:Nx \l_@@_tmpc_tl { { \tl_item:Nn \l_@@_tmpa_tl {2} } { \tl_item:Nn \l_@@_tmpa_tl {3} } } } } \prg_replicate:nn {3} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpc_tl \group_end: } \cs_new_protected_nopar:Npn \spath_finalmovepoint:Nn #1#2 { \@@_finalmovepoint:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_finalmovepoint:Nn {NV, cn, cV} \cs_new_protected_nopar:Npn \spath_gfinalmovepoint:Nn #1#2 { \@@_finalmovepoint:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gfinalmovepoint:Nn {NV, cn, cV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_initialtangent:Nn, % \spath_ginitialtangent:Nn % } % The starting tangent of the path. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_initialtangent:n #1 { \group_begin: \tl_set:Nx \l_@@_tmpb_tl {\tl_item:nn {#1} {4}} \tl_if_eq:NNTF \l_@@_tmpb_tl \c_spath_curvetoa_tl { \fp_set:Nn \l_@@_tmpa_fp {3} } { \fp_set:Nn \l_@@_tmpa_fp {1} } \tl_clear:N \l_@@_tmpa_tl \tl_set:Nx \l_@@_tmpa_tl { { \fp_to_dim:n { \l_@@_tmpa_fp * ( \tl_item:nn {#1} {5} - \tl_item:nn {#1} {2} ) } } { \fp_to_dim:n { \l_@@_tmpa_fp * ( \tl_item:nn {#1} {6} - \tl_item:nn {#1} {3} ) } } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_new_protected_nopar:Npn \spath_initialtangent:Nn #1#2 { \@@_initialtangent:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_initialtangent:Nn {NV, cn, cV, Nv} \cs_new_protected_nopar:Npn \spath_ginitialtangent:Nn #1#2 { \@@_initialtangent:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_ginitialtangent:Nn {NV, cn, cV, Nv} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_finaltangent:Nn, % \spath_gfinaltangent:Nn, % } % The final tangent of the path. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_finaltangent:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_reverse:N \l_@@_tmpa_tl \tl_set:Nx \l_@@_tmpb_tl {\tl_item:nn {#1} {6}} \tl_if_eq:NNTF \l_@@_tmpb_tl \c_spath_curveto_tl { \fp_set:Nn \l_@@_tmpa_fp {3} } { \fp_set:Nn \l_@@_tmpa_fp {1} } \tl_clear:N \l_@@_tmpb_tl \tl_set:Nx \l_@@_tmpb_tl { { \fp_to_dim:n { \l_@@_tmpa_fp * ( \tl_item:Nn \l_@@_tmpa_tl {2} - \tl_item:Nn \l_@@_tmpa_tl {5} ) } } { \fp_to_dim:n { \l_@@_tmpa_fp * ( \tl_item:Nn \l_@@_tmpa_tl {1} - \tl_item:Nn \l_@@_tmpa_tl {4} ) } } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_finaltangent:Nn #1#2 { \@@_finaltangent:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_finaltangent:Nn {NV, cn, cV, Nv} \cs_new_protected_nopar:Npn \spath_gfinaltangent:Nn #1#2 { \@@_finaltangent:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gfinaltangent:Nn {NV, cn, cV, Nv} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \spath_finalmovetangent:Nn, % \spath_gfinalmovetangent:Nn % } % Get the last move on the path. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_finalmovetangent:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpc_tl { {0pt} {0pt} } \tl_set:Nn \l_@@_tmpa_tl {#1} \bool_do_until:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_set:Nx \l_@@_tmpb_tl {\tl_head:N \l_@@_tmpa_tl} \tl_case:Nn \l_@@_tmpb_tl { \c_spath_moveto_tl { \tl_set:Nx \l_@@_tmpd_tl { \tl_item:Nn \l_@@_tmpa_tl {4} } \tl_if_eq:NNTF \l_@@_tmpd_tl \c_spath_curveto_tl { \fp_set:Nn \l_@@_tmpa_fp {3} } { \fp_set:Nn \l_@@_tmpa_fp {1} } \tl_set:Nx \l_@@_tmpc_tl { { \fp_to_dim:n { \l_@@_tmpa_fp * ( \tl_item:Nn \l_@@_tmpa_tl {5} - \tl_item:Nn \l_@@_tmpa_tl {2} ) } } { \fp_to_dim:n { \l_@@_tmpa_fp * ( \tl_item:Nn \l_@@_tmpa_tl {6} - \tl_item:Nn \l_@@_tmpa_tl {3} ) } } } } } \prg_replicate:nn {3} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpc_tl \group_end: } \cs_new_protected_nopar:Npn \spath_finalmovetangent:Nn #1#2 { \@@_finalmovetangent:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_finalmovetangent:Nn {NV, cn, cV} \cs_new_protected_nopar:Npn \spath_gfinalmovetangent:Nn #1#2 { \@@_finalmovetangent:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gfinalmovetangent:Nn {NV, cn, cV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_reverse:Nn, % \spath_greverse:Nn % } % This computes the reverse of the path. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_reverse:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_clear:N \l_@@_tmpb_tl \tl_clear:N \l_@@_tmpd_tl \bool_set_false:N \l_@@_rect_bool \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl} \bool_while_do:nn { \tl_if_eq_p:NN \l_@@_tmpc_tl \c_spath_rectcorner_tl } { \tl_put_left:Nx \l_@@_tmpd_tl { \tl_item:Nn \l_@@_tmpa_tl {1} {\tl_item:Nn \l_@@_tmpa_tl {2}} {\tl_item:Nn \l_@@_tmpa_tl {3}} \tl_item:Nn \l_@@_tmpa_tl {4} {\tl_item:Nn \l_@@_tmpa_tl {5}} {\tl_item:Nn \l_@@_tmpa_tl {6}} } \prg_replicate:nn {6} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl} \bool_set_true:N \l_@@_rect_bool } \tl_if_empty:NF \l_@@_tmpa_tl { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_left:Nx \l_@@_tmpd_tl { {\dim_use:N \l_@@_tmpa_dim} {\dim_use:N \l_@@_tmpb_dim} } \bool_set_false:N \l_@@_closed_bool \bool_until_do:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl} \bool_set_false:N \l_@@_rect_bool \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_rectcorner_tl { \bool_if:NT \l_@@_closed_bool { \tl_put_right:NV \l_@@_tmpd_tl \c_spath_closepath_tl \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpd_tl} \tl_put_right:Nx \l_@@_tmpd_tl { { \tl_head:N \l_@@_tmpd_tl } { \tl_head:N \l_@@_tmpe_tl } } } \bool_set_false:N \l_@@_closed_bool \tl_put_left:NV \l_@@_tmpd_tl \c_spath_moveto_tl \tl_put_left:NV \l_@@_tmpb_tl \l_@@_tmpd_tl \tl_clear:N \l_@@_tmpd_tl \bool_while_do:nn { \tl_if_eq_p:NN \l_@@_tmpc_tl \c_spath_rectcorner_tl } { \tl_put_left:Nx \l_@@_tmpb_tl { \tl_item:Nn \l_@@_tmpa_tl {1} {\tl_item:Nn \l_@@_tmpa_tl {2}} {\tl_item:Nn \l_@@_tmpa_tl {3}} \tl_item:Nn \l_@@_tmpa_tl {4} {\tl_item:Nn \l_@@_tmpa_tl {5}} {\tl_item:Nn \l_@@_tmpa_tl {6}} } \prg_replicate:nn {6} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl} } \bool_set_true:N \l_@@_rect_bool } \c_spath_lineto_tl { \tl_put_left:NV \l_@@_tmpd_tl \c_spath_lineto_tl } \c_spath_curveto_tl { \tl_put_left:NV \l_@@_tmpd_tl \c_spath_curvetoa_tl } \c_spath_curvetoa_tl { \tl_put_left:NV \l_@@_tmpd_tl \c_spath_curveto_tl } \c_spath_curvetob_tl { \tl_put_left:NV \l_@@_tmpd_tl \c_spath_curvetob_tl } } { \tl_if_empty:NF \l_@@_tmpa_tl { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_left:Nx \l_@@_tmpd_tl { {\dim_use:N \l_@@_tmpa_dim} {\dim_use:N \l_@@_tmpb_dim} } } } { \tl_if_eq:NNTF \l_@@_tmpc_tl \c_spath_closepath_tl { \bool_set_true:N \l_@@_closed_bool } { \msg_warning:nnx { spath3 } { unknown path construction } { \l_@@_tmpc_tl } } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } } \bool_if:NT \l_@@_closed_bool { \tl_put_right:NV \l_@@_tmpd_tl \c_spath_closepath_tl \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpd_tl} \tl_put_right:Nx \l_@@_tmpd_tl { { \tl_head:N \l_@@_tmpd_tl } { \tl_head:N \l_@@_tmpe_tl } } } \bool_set_false:N \l_@@_closed_bool \bool_if:NF \l_@@_rect_bool { \tl_put_left:NV \l_@@_tmpd_tl \c_spath_moveto_tl } } \tl_put_left:NV \l_@@_tmpb_tl \l_@@_tmpd_tl \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_reverse:Nn #1#2 { \@@_reverse:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_reverse:Nn {NV, cn, cV, Nv} \cs_new_protected_nopar:Npn \spath_reverse:N #1 { \spath_reverse:NV #1#1 } \cs_generate_variant:Nn \spath_reverse:N {c} \cs_new_protected_nopar:Npn \spath_greverse:Nn #1#2 { \@@_reverse:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_greverse:Nn {NV, cn, cV, Nv} \cs_new_protected_nopar:Npn \spath_greverse:N #1 { \spath_greverse:NV #1#1 } \cs_generate_variant:Nn \spath_greverse:N {c} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_initialaction:Nn, % \spath_ginitialaction:Nn, % } % This is the first thing that the path does (after the initial move). % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_initialaction:n #1 { \group_begin: \tl_clear:N \l_@@_tmpa_tl \tl_set:Nx \l_@@_tmpb_tl {\tl_head:n {#1}} \tl_if_eq:NNTF \l_@@_tmpb_tl \c_spath_rectcorner_tl { \tl_set_eq:NN \l_@@_tmpa_tl \c_spath_rectcorner_tl } { \int_compare:nT { \tl_count:n {#1} > 3 } { \tl_set:Nx \l_@@_tmpa_tl { \tl_item:Nn {#1} {4} } } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_new_protected_nopar:Npn \spath_initialaction:Nn #1#2 { \@@_initialaction:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_initialaction:Nn {NV} \cs_new_protected_nopar:Npn \spath_ginitialaction:Nn #1#2 { \@@_initialaction:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_ginitialaction:Nn {NV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_finalaction:Nn, % \spath_gfinalaction:Nn % } % This is the last thing that the path does. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_finalaction:n #1 { \group_begin: \tl_clear:N \l_@@_tmpb_tl \int_compare:nT { \tl_count:n {#1} > 3 } { \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_reverse:N \l_@@_tmpa_tl \tl_set:Nx \l_@@_tmpb_tl { \tl_item:Nn \l_@@_tmpa_tl {3} } \tl_if_eq:NNT \l_@@_tmpb_tl \c_spath_curvetoa_tl { \tl_set_eq:NN \l_@@_tmpb_tl \c_spath_curveto_tl } \tl_if_eq:NNT \l_@@_tmpb_tl \c_spath_rectsize_tl { \tl_set_eq:NN \l_@@_tmpb_tl \c_spath_rectcorner_tl } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_finalaction:Nn #1#2 { \@@_finalaction:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_finalaction:Nn {NV} \cs_new_protected_nopar:Npn \spath_gfinalaction:Nn #1#2 { \@@_finalaction:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gfinalaction:Nn {NV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_minbb:Nn, % \spath_gminbb:Nn % } % This computes the minimum (bottom left) of the bounding box of the path. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_minbb:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_if_eq:NNT \l_@@_tmpc_tl \c_spath_rectcorner_tl { \dim_set_eq:NN \l_@@_rectx_dim \l_@@_tmpa_dim \dim_set_eq:NN \l_@@_recty_dim \l_@@_tmpb_dim } \bool_until_do:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_if_eq:NNTF \l_@@_tmpc_tl \c_spath_rectsize_tl { \dim_set:Nn \l_@@_tmpa_dim {\dim_min:nn {\tl_head:N \l_@@_tmpa_tl + \l_@@_rectx_dim} {\l_@@_tmpa_dim} } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpb_dim {\dim_min:nn {\tl_head:N \l_@@_tmpa_tl + \l_@@_recty_dim} {\l_@@_tmpb_dim} } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } { \dim_set:Nn \l_@@_tmpa_dim {\dim_min:nn {\tl_head:N \l_@@_tmpa_tl} {\l_@@_tmpa_dim}} \tl_if_eq:NNT \l_@@_tmpc_tl \c_spath_rectcorner_tl { \dim_set:Nn \l_@@_rectx_dim {\tl_head:N \l_@@_tmpa_tl} } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpb_dim {\dim_min:nn {\tl_head:N \l_@@_tmpa_tl} {\l_@@_tmpb_dim}} \tl_if_eq:NNT \l_@@_tmpc_tl \c_spath_rectcorner_tl { \dim_set:Nn \l_@@_recty_dim {\tl_head:N \l_@@_tmpa_tl} } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } } \tl_clear:N \l_@@_tmpb_tl \tl_put_right:Nx \l_@@_tmpb_tl { {\dim_use:N \l_@@_tmpa_dim} {\dim_use:N \l_@@_tmpb_dim} } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_minbb:Nn #1#2 { \@@_minbb:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_minbb:Nn {NV, cn, cV} \cs_new_protected_nopar:Npn \spath_gminbb:Nn #1#2 { \@@_minbb:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gminbb:Nn {NV, cn, cV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_maxbb:Nn, % \spath_gmaxbb:Nn, % } % This computes the maximum (top right) of the bounding box of the path. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_maxbb:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_if_eq:NNT \l_@@_tmpc_tl \c_spath_rectcorner_tl { \dim_set_eq:NN \l_@@_rectx_dim \l_@@_tmpa_dim \dim_set_eq:NN \l_@@_recty_dim \l_@@_tmpb_dim } \bool_until_do:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_if_eq:NNTF \l_@@_tmpc_tl \c_spath_rectsize_tl { \dim_set:Nn \l_@@_tmpa_dim {\dim_max:nn {\tl_head:N \l_@@_tmpa_tl + \l_@@_rectx_dim} {\l_@@_tmpa_dim} } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpb_dim {\dim_max:nn {\tl_head:N \l_@@_tmpa_tl + \l_@@_recty_dim} {\l_@@_tmpb_dim} } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } { \dim_set:Nn \l_@@_tmpa_dim {\dim_max:nn {\tl_head:N \l_@@_tmpa_tl} {\l_@@_tmpa_dim}} \tl_if_eq:NNT \l_@@_tmpc_tl \c_spath_rectcorner_tl { \dim_set:Nn \l_@@_rectx_dim {\tl_head:N \l_@@_tmpa_tl} } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \dim_set:Nn \l_@@_tmpb_dim {\dim_max:nn {\tl_head:N \l_@@_tmpa_tl} {\l_@@_tmpb_dim}} \tl_if_eq:NNT \l_@@_tmpc_tl \c_spath_rectcorner_tl { \dim_set:Nn \l_@@_recty_dim {\tl_head:N \l_@@_tmpa_tl} } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } } \tl_clear:N \l_@@_tmpb_tl \tl_set:Nx \l_@@_tmpb_tl { {\dim_use:N \l_@@_tmpa_dim} {\dim_use:N \l_@@_tmpb_dim} } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_maxbb:Nn #1#2 { \@@_maxbb:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_maxbb:Nn {NV, cn, cV} \cs_new_protected_nopar:Npn \spath_gmaxbb:Nn #1#2 { \@@_maxbb:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gmaxbb:Nn {NV, cn, cV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_save_to_aux:Nn, % \spath_save_to_aux:N % } % This saves a soft path to the auxfile. % The first argument is the macro that will be assigned to the soft path when the aux file is read back in. % \begin{macrocode} \int_set:Nn \l_@@_tmpa_int {\char_value_catcode:n {`@}} \char_set_catcode_letter:N @ \cs_new_protected_nopar:Npn \spath_save_to_aux:Nn #1#2 { \tl_if_empty:nF {#2} { \tl_clear:N \l_@@_tmpa_tl \tl_put_right:Nn \l_@@_tmpa_tl { \ExplSyntaxOn \tl_clear_new: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 } } \cs_generate_variant:Nn \spath_save_to_aux:N {c} % \end{macrocode} % \end{macro} % % % \subsection{Path Manipulation} % % These functions all manipulate a soft path. % They come with a variety of different argument specifications. % As a general rule, the first argument is the macro in which to store the modified path, the second is the path to manipulate, and the rest are the information about what to do. % There is always a variant in which the path is specified by a macro and restored back in that same macro. % % % \begin{macro}[internal]{ % \spath_translate:Nnnn, % \spath_translate:Nnn, % \spath_gtranslate:Nnnn, % \spath_gtranslate:Nnn % } % Translates a path by an amount. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_translate:nnn #1#2#3 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_clear:N \l_@@_tmpb_tl \bool_until_do:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpb_tl {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_if_eq:NNTF \l_@@_tmpc_tl \c_spath_rectsize_tl { \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl} } { \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpa_tl + #2} } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_if_eq:NNTF \l_@@_tmpc_tl \c_spath_rectsize_tl { \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl} } { \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpa_tl + #3} } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpb_tl { {\dim_use:N \l_@@_tmpa_dim} {\dim_use:N \l_@@_tmpb_dim} } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_generate_variant:Nn \@@_translate:nnn {nVV} \cs_new_protected_nopar:Npn \spath_translate:Nnnn #1#2#3#4 { \@@_translate:nnn {#2}{#3}{#4} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_translate:Nnnn {NVxx, NVVV, NVnn} \cs_new_protected_nopar:Npn \spath_translate:Nnn #1#2#3 { \spath_translate:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \spath_translate:Nnn {NVV, cnn, cVV} \cs_new_protected_nopar:Npn \spath_gtranslate:Nnnn #1#2#3#4 { \@@_translate:nnn {#2}{#3}{#4} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gtranslate:Nnnn {NVxx, NVVV, NVnn} \cs_new_protected_nopar:Npn \spath_gtranslate:Nnn #1#2#3 { \spath_gtranslate:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \spath_gtranslate:Nnn {NVV, cnn, cVV} % \end{macrocode} % % This variant allows for passing the coordinates as a single braced group as it strips off the outer braces of the second argument. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_translate:Nn #1#2 { \spath_translate:Nnn #1 #2 } \cs_generate_variant:Nn \spath_translate:Nn {NV} \cs_new_protected_nopar:Npn \spath_gtranslate:Nn #1#2 { \spath_gtranslate:Nnn #1 #2 } \cs_generate_variant:Nn \spath_gtranslate:Nn {NV} % \end{macrocode} % \end{macro} % % % % \begin{macro}[internal]{ % \spath_translate_to:Nnnn, % \spath_translate_to:Nnn, % \spath_gtranslate_to:Nnnn, % \spath_gtranslate_to:Nnn % } % Translates a path so that it starts at a point. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_translate_to:nnn #1#2#3 { \group_begin: \spath_initialpoint:Nn \l_@@_tmpa_tl {#1} \dim_set:Nn \l_@@_tmpa_dim { #2 - \tl_item:Nn \l_@@_tmpa_tl {1} } \dim_set:Nn \l_@@_tmpb_dim { #3 - \tl_item:Nn \l_@@_tmpa_tl {2} } \@@_translate:nVV {#1} \l_@@_tmpa_dim \l_@@_tmpb_dim \group_end: } \cs_new_protected_nopar:Npn \spath_translate_to:Nnnn #1#2#3#4 { \@@_translate_to:nnn {#2}{#3}{#4} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_translate_to:Nnnn {NVxx, NVVV, NVnn} \cs_new_protected_nopar:Npn \spath_translate_to:Nnn #1#2#3 { \spath_translate_to:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \spath_translate_to:Nnn {NVV, cnn, cVV} \cs_new_protected_nopar:Npn \spath_gtranslate_to:Nnnn #1#2#3#4 { \@@_translate_to:nnn {#2}{#3}{#4} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gtranslate_to:Nnnn {NVxx, NVVV, NVnn} \cs_new_protected_nopar:Npn \spath_gtranslate_to:Nnn #1#2#3 { \spath_gtranslate_to:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \spath_gtranslate_to:Nnn {NVV, cnn, cVV} % \end{macrocode} % % This variant allows for passing the coordinates as a single braced group as it strips off the outer braces of the second argument. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_translate_to:Nn #1#2 { \spath_translate_to:Nnn #1 #2 } \cs_generate_variant:Nn \spath_translate_to:Nn {NV} \cs_new_protected_nopar:Npn \spath_gtranslate_to:Nn #1#2 { \spath_gtranslate_to:Nnn #1 #2 } \cs_generate_variant:Nn \spath_gtranslate_to:Nn {NV} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \spath_scale:Nnnn, % \spath_scale:Nnn, % \spath_gscale:Nnnn, % \spath_gscale:Nnn % } % Scale a path. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_scale:nnn #1#2#3 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_clear:N \l_@@_tmpb_tl \bool_until_do:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_put_right:Nx \l_@@_tmpb_tl {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \fp_set:Nn \l_@@_tmpa_fp {\tl_head:N \l_@@_tmpa_tl * #2} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \fp_set:Nn \l_@@_tmpb_fp {\tl_head:N \l_@@_tmpa_tl * #3} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpb_tl { {\fp_to_dim:N \l_@@_tmpa_fp} {\fp_to_dim:N \l_@@_tmpb_fp} } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_scale:Nnnn #1#2#3#4 { \@@_scale:nnn {#2}{#3}{#4} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_scale:Nnnn {NVnn, Nnxx} \cs_new_protected_nopar:Npn \spath_scale:Nnn #1#2#3 { \spath_scale:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \spath_scale:Nnn {cnn, cVV, NVV} \cs_new_protected_nopar:Npn \spath_gscale:Nnnn #1#2#3#4 { \@@_scale:nnn {#2}{#3}{#4} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gscale:Nnnn {NVnn, Nnxx} \cs_new_protected_nopar:Npn \spath_gscale:Nnn #1#2#3 { \spath_gscale:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \spath_gscale:Nnn {cnn, cVV, NVV} % \end{macrocode} % % This variant allows for passing the coordinates as a single braced group as it strips off the outer braces of the second argument. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_scale:Nn #1#2 { \spath_scale:Nnn #1 #2 } \cs_generate_variant:Nn \spath_scale:Nn {NV} \cs_new_protected_nopar:Npn \spath_gscale:Nn #1#2 { \spath_gscale:Nnn #1 #2 } \cs_generate_variant:Nn \spath_gscale:Nn {NV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_transform:Nnnnnnnn, % \spath_transform:Nnnnnnn, % \spath_gtransform:Nnnnnnnn, % \spath_gtransform:Nnnnnnn, % } % Applies an affine (matrix and vector) transformation to path. % The matrix is specified in rows first. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_transform:nnnnnnn #1#2#3#4#5#6#7 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_clear:N \l_@@_tmpb_tl \bool_until_do:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_set:Nx \l_@@_tmpe_tl {\tl_head:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpb_tl {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpd_tl {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_if_eq:NNTF \l_@@_tmpe_tl \c_spath_rectsize_tl { \fp_set:Nn \l_@@_tmpa_fp {\l_@@_tmpc_tl * #2 + \l_@@_tmpd_tl * #4} \fp_set:Nn \l_@@_tmpb_fp {\l_@@_tmpc_tl * #3 + \l_@@_tmpd_tl * #5} } { \fp_set:Nn \l_@@_tmpa_fp {\l_@@_tmpc_tl * #2 + \l_@@_tmpd_tl * #4 + #6} \fp_set:Nn \l_@@_tmpb_fp {\l_@@_tmpc_tl * #3 + \l_@@_tmpd_tl * #5 + #7} } \tl_put_right:Nx \l_@@_tmpb_tl { {\fp_to_dim:N \l_@@_tmpa_fp} {\fp_to_dim:N \l_@@_tmpb_fp} } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_transform:Nnnnnnnn #1#2#3#4#5#6#7#8 { \@@_transform:nnnnnnn {#2}{#3}{#4}{#5}{#6}{#7}{#8} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_transform:Nnnnnnnn {NVnnnnnn, Nnxxxxxx, cnnnnnnn} \cs_new_protected_nopar:Npn \spath_transform:Nnnnnnn #1#2#3#4#5#6#7 { \spath_transform:NVnnnnnn #1#1{#2}{#3}{#4}{#5}{#6}{#7} } \cs_generate_variant:Nn \spath_transform:Nnnnnnn {cnnnnnn} \cs_new_protected_nopar:Npn \spath_transform:Nnn #1#2#3 { \spath_transform:Nnnnnnnn #1{#2}#3 } \cs_generate_variant:Nn \spath_transform:Nnn {cnn, cVn, NVn, NnV} \cs_new_protected_nopar:Npn \spath_transform:Nn #1#2 { \spath_transform:NVnnnnnn #1#1#2 } \cs_generate_variant:Nn \spath_transform:Nn {cn, cV, NV} \cs_new_protected_nopar:Npn \spath_gtransform:Nnnnnnnn #1#2#3#4#5#6#7#8 { \@@_transform:nnnnnnn {#2}{#3}{#4}{#5}{#6}{#7}{#8} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gtransform:Nnnnnnnn {NVnnnnnn, Nnxxxxxx, cnnnnnnn} \cs_new_protected_nopar:Npn \spath_gtransform:Nnnnnnn #1#2#3#4#5#6#7 { \spath_gtransform:NVnnnnnn #1#1{#2}{#3}{#4}{#5}{#6}{#7} } \cs_generate_variant:Nn \spath_gtransform:Nnnnnnn {cnnnnnn} \cs_new_protected_nopar:Npn \spath_gtransform:Nnn #1#2#3 { \spath_gtransform:Nnnnnnnn #1{#2}#3 } \cs_generate_variant:Nn \spath_gtransform:Nnn {cnn, cVn, NVn, NnV} \cs_new_protected_nopar:Npn \spath_gtransform:Nn #1#2 { \spath_gtransform:NVnnnnnn #1#1#2 } \cs_generate_variant:Nn \spath_gtransform:Nn {cn, cV, NV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_span:Nnnn, % \spath_span:Nnn, % \spath_gspan:Nnnn, % \spath_gspan:Nnn, % \spath_normalise:Nn, % \spath_normalise:N, % \spath_gnormalise:Nn, % \spath_gnormalise:N % } % The \Verb+span+ functions transform a path to start and end at specified points. % The \Verb+normalise+ functions transform it to start at the origin and end at \((1\mathrm{pt},0\mathrm{pt})\). % % If the path starts and ends at the same point then it is translated to the specified point (or origin) but not otherwise changed. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_span:nnn #1#2#3 { \group_begin: \spath_initialpoint:Nn \l_@@_tmpa_tl {#1} \spath_finalpoint:Nn \l_@@_tmpb_tl {#1} \fp_set:Nn \l_@@_tmpa_fp { (\tl_item:Nn \l_@@_tmpb_tl {1}) - (\tl_item:Nn \l_@@_tmpa_tl {1}) } \fp_set:Nn \l_@@_tmpb_fp { (\tl_item:Nn \l_@@_tmpb_tl {2}) - (\tl_item:Nn \l_@@_tmpa_tl {2}) } \fp_set:Nn \l_@@_tmpc_fp { (\l_@@_tmpa_fp) * (\l_@@_tmpa_fp) + (\l_@@_tmpb_fp * \l_@@_tmpb_fp) } \fp_compare:nTF { \l_@@_tmpc_fp < 0.001 } { \spath_translate_to:Nnnn \l_@@_tmpd_tl {#1} #2 } { \fp_set:Nn \l_@@_tmpa_fp { ( ((\tl_item:nn {#3} {1}) - (\tl_item:nn {#2} {1})) * ((\tl_item:Nn \l_@@_tmpb_tl {1}) - (\tl_item:Nn \l_@@_tmpa_tl {1})) + ((\tl_item:nn {#3} {2}) - (\tl_item:nn {#2} {2})) * ((\tl_item:Nn \l_@@_tmpb_tl {2}) - (\tl_item:Nn \l_@@_tmpa_tl {2})) ) / \l_@@_tmpc_fp } \fp_set:Nn \l_@@_tmpb_fp { ( ((\tl_item:nn {#3} {2}) - (\tl_item:nn {#2} {2})) * ((\tl_item:Nn \l_@@_tmpb_tl {1}) - (\tl_item:Nn \l_@@_tmpa_tl {1})) - ((\tl_item:nn {#3} {1}) - (\tl_item:nn {#2} {1})) * ((\tl_item:Nn \l_@@_tmpb_tl {2}) - (\tl_item:Nn \l_@@_tmpa_tl {2})) ) / \l_@@_tmpc_fp } \tl_set:Nx \l_@@_tmpc_tl { { \fp_to_decimal:N \l_@@_tmpa_fp } { \fp_to_decimal:N \l_@@_tmpb_fp } { \fp_eval:n { - \l_@@_tmpb_fp } } { \fp_to_decimal:N \l_@@_tmpa_fp } { \fp_to_dim:n { \tl_item:nn {#2} {1} - \l_@@_tmpa_fp * (\tl_item:Nn \l_@@_tmpa_tl {1}) + \l_@@_tmpb_fp * (\tl_item:Nn \l_@@_tmpa_tl {2}) } } { \fp_to_dim:n { \tl_item:nn {#2} {2} - \l_@@_tmpb_fp * (\tl_item:Nn \l_@@_tmpa_tl {1}) - \l_@@_tmpa_fp * (\tl_item:Nn \l_@@_tmpa_tl {2}) } } } \spath_transform:NnV \l_@@_tmpd_tl {#1} \l_@@_tmpc_tl } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpd_tl \group_end: } \cs_new_protected_nopar:Npn \spath_span:Nnnn #1#2#3#4 { \@@_span:nnn {#2}{#3}{#4} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_span:Nnnn {NVnn, NVVV, NnVV} \cs_new_protected_nopar:Npn \spath_span:Nnn #1#2#3 { \spath_span:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \spath_span:Nnn {NVV, cnn, cvv, cVV} \cs_new_protected_nopar:Npn \spath_gspan:Nnnn #1#2#3#4 { \@@_span:nnn {#2}{#3}{#4} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gspan:Nnnn {NVnn, NVVV} \cs_new_protected_nopar:Npn \spath_gspan:Nnn #1#2#3 { \spath_gspan:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \spath_gspan:Nnn {NVV, cnn, cvv, cVV} \cs_new_protected_nopar:Npn \@@_normalise:n #1 { \@@_span:nnn {#1}{{0pt}{0pt}}{{1pt}{0pt}} } \cs_new_protected_nopar:Npn \spath_normalise:Nn #1#2 { \@@_normalise:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_normalise:Nn {cn,NV, cV, cv} \cs_new_protected_nopar:Npn \spath_normalise:N #1 { \spath_normalise:NV #1#1 } \cs_generate_variant:Nn \spath_normalise:N {c} \cs_new_protected_nopar:Npn \spath_gnormalise:Nn #1#2 { \@@_normalise:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gnormalise:Nn {cn,NV, cV, cv} \cs_new_protected_nopar:Npn \spath_gnormalise:N #1 { \spath_gnormalise:NV #1#1 } \cs_generate_variant:Nn \spath_gnormalise:N {c} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \spath_splice_between:Nnnn, % \spath_splice_between:Nnn, % \spath_gsplice_between:Nnnn, % \spath_gsplice_between:Nnn % } % This takes three paths and returns a single path in which the middle one is adjusted (and welded) so that it joins the first path to the third. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_splice_between:nnn #1#2#3 { \group_begin: \spath_finalpoint:Nn \l_@@_tmpd_tl {#1} \spath_initialpoint:Nn \l_@@_tmpe_tl {#3} \spath_span:NnVV \l_@@_tmpb_tl {#2} \l_@@_tmpd_tl \l_@@_tmpe_tl \spath_append_no_move:NnV \l_@@_tmpa_tl {#1} \l_@@_tmpb_tl \spath_append_no_move:Nn \l_@@_tmpa_tl {#3} \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_new_protected_nopar:Npn \spath_splice_between:Nnnn #1#2#3#4 { \@@_splice_between:nnn {#2}{#3}{#4} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_splice_between:Nnnn {NVnn, NVVV} \cs_new_protected_nopar:Npn \spath_splice_between:Nnn #1#2#3 { \spath_splice_between:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \spath_splice_between:Nnn {NVV, cnn, cvv, Nvn, NVn} \cs_new_protected_nopar:Npn \spath_gsplice_between:Nnnn #1#2#3#4 { \@@_splice_between:nnn {#2}{#3}{#4} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplice_between:Nnnn {NVnn, NVVV} \cs_new_protected_nopar:Npn \spath_gsplice_between:Nnn #1#2#3 { \spath_gsplice_between:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \spath_gsplice_between:Nnn {NVV, cnn, cvv, Nvn, NVn} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_hobby_curve:Nnnnn % } % Construct the curve from Hobby's algorithm given the start, end, and tangent directions. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_hobby_curve:nnnn #1#2#3#4 { \group_begin: % \end{macrocode} % First tangent vector projected onto vector between endpoints % % Something a bit weird here as the components are opposite to how I thought they should be ... % \begin{macrocode} \fp_set:Nn \l_@@_tmpa_fp { ( (\tl_item:nn {#2} {1}) * (\tl_item:nn {#4} {1} - \tl_item:nn {#1} {1}) + (\tl_item:nn {#2} {2}) * (\tl_item:nn {#4} {2} - \tl_item:nn {#1} {2}) ) / sqrt ( ( (\tl_item:nn {#2} {1})^2 + (\tl_item:nn {#2} {2})^2 ) * ( (\tl_item:nn {#4} {1} - \tl_item:nn {#1} {1})^2 + (\tl_item:nn {#4} {2} - \tl_item:nn {#1} {2})^2 ) ) } \fp_set:Nn \l_@@_tmpb_fp { ( - (\tl_item:nn {#2} {1}) * (\tl_item:nn {#4} {2} - \tl_item:nn {#1} {2}) + (\tl_item:nn {#2} {2}) * (\tl_item:nn {#4} {1} - \tl_item:nn {#1} {1}) ) / sqrt ( ( (\tl_item:nn {#2} {1})^2 + (\tl_item:nn {#2} {2})^2 ) * ( (\tl_item:nn {#4} {1} - \tl_item:nn {#1} {1})^2 + (\tl_item:nn {#4} {2} - \tl_item:nn {#1} {2})^2 ) ) } % \end{macrocode} % Second tangent vector projected onto vector between endpoints % \begin{macrocode} \fp_set:Nn \l_@@_tmpc_fp { ( (\tl_item:nn {#3} {1}) * (\tl_item:nn {#4} {1} - \tl_item:nn {#1} {1}) + (\tl_item:nn {#3} {2}) * (\tl_item:nn {#4} {2} - \tl_item:nn {#1} {2}) ) / sqrt ( ( (\tl_item:nn {#3} {1})^2 + (\tl_item:nn {#3} {2})^2 ) * ( (\tl_item:nn {#4} {1} - \tl_item:nn {#1} {1})^2 + (\tl_item:nn {#4} {2} - \tl_item:nn {#1} {2})^2 ) ) } \fp_set:Nn \l_@@_tmpd_fp { ( (\tl_item:nn {#3} {1}) * (\tl_item:nn {#4} {2} - \tl_item:nn {#1} {2}) - (\tl_item:nn {#3} {2}) * (\tl_item:nn {#4} {1} - \tl_item:nn {#1} {1}) ) / sqrt ( ( (\tl_item:nn {#3} {1})^2 + (\tl_item:nn {#3} {2})^2 ) * ( (\tl_item:nn {#4} {1} - \tl_item:nn {#1} {1})^2 + (\tl_item:nn {#4} {2} - \tl_item:nn {#1} {2})^2 ) ) } \fp_set:Nn \l_@@_tmpe_fp { ( 2 + sqrt(2) * (\l_@@_tmpb_fp - 1/16 * \l_@@_tmpd_fp) * (\l_@@_tmpd_fp - 1/16 * \l_@@_tmpb_fp) * (\l_@@_tmpa_fp - \l_@@_tmpc_fp) ) / ( 1 + (1 - (3 - sqrt(5))/2) * \l_@@_tmpa_fp + (3 - sqrt(5))/2 * \l_@@_tmpc_fp ) * sqrt ( ( (\tl_item:nn {#4} {1} - \tl_item:nn {#1} {1})^2 + (\tl_item:nn {#4} {2} - \tl_item:nn {#1} {2})^2 ) / ( (\tl_item:nn {#2} {1})^2 + (\tl_item:nn {#2} {2})^2 ) ) /3 } \fp_set:Nn \l_@@_tmpf_fp { ( 2 - sqrt(2) * (\l_@@_tmpb_fp - 1/16 * \l_@@_tmpd_fp) * (\l_@@_tmpd_fp - 1/16 * \l_@@_tmpb_fp) * (\l_@@_tmpa_fp - \l_@@_tmpc_fp) ) / ( 1 + (1 - (3 - sqrt(5))/2) * \l_@@_tmpc_fp + (3 - sqrt(5))/2 * \l_@@_tmpa_fp ) * sqrt ( ( (\tl_item:nn {#4} {1} - \tl_item:nn {#1} {1})^2 + (\tl_item:nn {#4} {2} - \tl_item:nn {#1} {2})^2 ) / ( (\tl_item:nn {#3} {1})^2 + (\tl_item:nn {#3} {2})^2 ) ) /3 } \tl_set:Nx \l_@@_tmpa_tl { { \fp_to_dim:n { \tl_item:nn {#1} {1} + \l_@@_tmpe_fp * (\tl_item:nn {#2} {1}) } } { \fp_to_dim:n { \tl_item:nn {#1} {2} + \l_@@_tmpe_fp * (\tl_item:nn {#2} {2}) } } } \tl_set:Nx \l_@@_tmpb_tl { { \fp_to_dim:n { \tl_item:nn {#4} {1} - \l_@@_tmpf_fp * (\tl_item:nn {#3} {1}) } } { \fp_to_dim:n { \tl_item:nn {#4} {2} - \l_@@_tmpf_fp * (\tl_item:nn {#3} {2}) } } } \tl_clear:N \l_@@_tmpc_tl \tl_set:NV \l_@@_tmpc_tl \c_spath_moveto_tl \tl_put_right:Nn \l_@@_tmpc_tl {#1} \tl_put_right:NV \l_@@_tmpc_tl \c_spath_curvetoa_tl \tl_put_right:NV \l_@@_tmpc_tl \l_@@_tmpa_tl \tl_put_right:NV \l_@@_tmpc_tl \c_spath_curvetob_tl \tl_put_right:NV \l_@@_tmpc_tl \l_@@_tmpb_tl \tl_put_right:NV \l_@@_tmpc_tl \c_spath_curveto_tl \tl_put_right:Nn \l_@@_tmpc_tl {#4} \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpc_tl \group_end: } \cs_new_protected_nopar:Npn \spath_hobby_curve:Nnnnn #1#2#3#4#5 { \@@_hobby_curve:nnnn {#2}{#3}{#4}{#5} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_hobby_curve:Nnnnn {NVVVV} \cs_new_protected_nopar:Npn \spath_ghobby_curve:Nnnnn #1#2#3#4#5 { \@@_hobby_curve:nnnn {#2}{#3}{#4}{#5} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_ghobby_curve:Nnnnn {NVVVV} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \spath_curve_between:Nnn, % \spath_curve_between:Nn, % \spath_gcurve_between:Nnn, % \spath_gcurve_between:Nn % } % This takes two paths and returns a single path formed by joining the two paths by a curve. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_curve_between:nn #1#2 { \group_begin: \spath_finalpoint:Nn \l_@@_tmpa_tl {#1} \spath_finaltangent:Nn \l_@@_tmpb_tl {#1} \spath_initialpoint:Nn \l_@@_tmpc_tl {#2} \spath_initialtangent:Nn \l_@@_tmpd_tl {#2} \spath_hobby_curve:NVVVV \l_@@_tmpe_tl \l_@@_tmpa_tl \l_@@_tmpb_tl \l_@@_tmpd_tl \l_@@_tmpc_tl \tl_set:Nn \l_@@_tmpa_tl {#1} \spath_append_no_move:NV \l_@@_tmpa_tl \l_@@_tmpe_tl \spath_append_no_move:Nn \l_@@_tmpa_tl {#2} \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_new_protected_nopar:Npn \spath_curve_between:Nnn #1#2#3 { \@@_curve_between:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_curve_between:Nnn {NVn, NVV} \cs_new_protected_nopar:Npn \spath_curve_between:Nn #1#2 { \spath_curve_between:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_curve_between:Nn {NV, cn, cv} \cs_new_protected_nopar:Npn \spath_gcurve_between:Nnn #1#2#3 { \@@_curve_between:nn {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gcurve_between:Nnn {NVn, NVV} \cs_new_protected_nopar:Npn \spath_gcurve_between:Nn #1#2 { \spath_gcurve_between:NVnn #1#1{#2} } \cs_generate_variant:Nn \spath_gcurve_between:Nn {NV, cn, cv} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_close_with:Nn, % \spath_gclose_with:Nn % } % Closes the first path by splicing in the second. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_close_with:nn #1#2 { \group_begin: \spath_finalmovepoint:Nn \l_@@_tmpa_tl {#1} \spath_finalpoint:Nn \l_@@_tmpb_tl {#1} \dim_compare:nTF { \dim_abs:n { \tl_item:Nn \l_@@_tmpa_tl {1} - \tl_item:Nn \l_@@_tmpb_tl {1} } + \dim_abs:n { \tl_item:Nn \l_@@_tmpa_tl {2} - \tl_item:Nn \l_@@_tmpb_tl {2} } < 0.01pt } { \@@_close:n {#1} } { \spath_span:NnVV \l_@@_tmpc_tl {#2} \l_@@_tmpb_tl \l_@@_tmpa_tl \spath_append_no_move:NnV \l_@@_tmpd_tl {#1} \l_@@_tmpc_tl \@@_close:V \l_@@_tmpd_tl } \group_end: } \cs_new_protected_nopar:Npn \spath_close_with:Nnn #1#2#3 { \@@_close_with:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_close_with:Nnn {cnn, cVV, cvv, NVn} \cs_new_protected_nopar:Npn \spath_close_with:Nn #1#2 { \spath_close_with:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_close_with:Nn {cn, cV, cv, NV} \cs_new_protected_nopar:Npn \spath_gclose_with:Nnn #1#2#3 { \@@_close_with:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gclose_with:Nnn {cnn, cVV, cvv, NVn} \cs_new_protected_nopar:Npn \spath_gclose_with:Nn #1#2 { \spath_gclose_with:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gclose_with:Nn {cn, cV, cv, NV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_close_with_curve:N, % \spath_gclose_with_curve:N % } % Closes the path with a curve. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_close_with_curve:n #1 { \group_begin: \spath_finalpoint:Nn \l_@@_tmpa_tl {#1} \spath_finaltangent:Nn \l_@@_tmpb_tl {#1} \spath_finalmovepoint:Nn \l_@@_tmpc_tl {#1} \spath_finalmovetangent:Nn \l_@@_tmpd_tl {#1} \dim_compare:nTF { \dim_abs:n { \tl_item:Nn \l_@@_tmpa_tl {1} - \tl_item:Nn \l_@@_tmpc_tl {1} } + \dim_abs:n { \tl_item:Nn \l_@@_tmpa_tl {2} - \tl_item:Nn \l_@@_tmpc_tl {2} } < 0.01pt } { \@@_close:n {#1} } { \spath_hobby_curve:NVVVV \l_@@_tmpe_tl \l_@@_tmpa_tl \l_@@_tmpb_tl \l_@@_tmpd_tl \l_@@_tmpc_tl \tl_set:Nn \l_@@_tmpa_tl {#1} \spath_append_no_move:NV \l_@@_tmpa_tl \l_@@_tmpe_tl \@@_close:V \l_@@_tmpa_tl } \group_end: } \cs_new_protected_nopar:Npn \spath_close_with_curve:Nn #1#2 { \@@_close_with_curve:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_close_with_curve:Nn {cn, cV, cv, NV} \cs_new_protected_nopar:Npn \spath_close_with_curve:N #1 { \spath_close_with_curve:NV #1#1 } \cs_generate_variant:Nn \spath_close_with_curve:N {c} \cs_new_protected_nopar:Npn \spath_gclose_with_curve:Nn #1#2 { \@@_close_with_curve:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gclose_with_curve:Nn {cn, cV, cv, NV} \cs_new_protected_nopar:Npn \spath_gclose_with_curve:N #1 { \spath_gclose_with_curve:NV #1#1 } \cs_generate_variant:Nn \spath_gclose_with_curve:N {c} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_weld:Nnn, % \spath_weld:Nn, % \spath_gweld:Nnn, % \spath_gweld:Nn % } % This welds one path to another, moving the second so that its initial point coincides with the first's final point. % It is called a \emph{weld} because the initial move of the second path is removed. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_weld:nn #1#2 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_set:Nn \l_@@_tmpb_tl {#2} \spath_finalpoint:Nn \l_@@_tmpc_tl {#1} \spath_translate_to:NV \l_@@_tmpb_tl \l_@@_tmpc_tl \@@_append_no_move:VV \l_@@_tmpa_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_weld:Nnn #1#2#3 { \@@_weld:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_weld:Nnn {NVV,NVn} \cs_new_protected_nopar:Npn \spath_weld:Nn #1#2 { \spath_weld:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_weld:Nn {NV, Nv, cV, cv} \cs_new_protected_nopar:Npn \spath_gweld:Nnn #1#2#3 { \@@_weld:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gweld:Nnn {NVV, NVn} \cs_new_protected_nopar:Npn \spath_gweld:Nn #1#2 { \spath_gweld:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gweld:Nn {NV, Nv, cV, cv} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \spath_append_no_move:Nnn, % \spath_append_no_move:Nn, % \spath_gappend_no_move:Nnn, % \spath_gappend_no_move:Nn, % } % Append the path from the second \Verb+spath+ to the first, removing % the adjoining move if neither path has a rectangle either side of the join or if the first path isn't closed. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_append_no_move:nn #1#2 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_set:Nn \l_@@_tmpb_tl {#2} \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpb_tl} \spath_finalaction:Nn \l_@@_tmpd_tl {#1} \bool_if:nT { ! \tl_if_eq_p:NN \l_@@_tmpd_tl \c_spath_closepath_tl && ! \tl_if_eq_p:NN \l_@@_tmpd_tl \c_spath_rectcorner_tl && \tl_if_eq_p:NN \l_@@_tmpc_tl \c_spath_moveto_tl } { \tl_set:Nx \l_@@_tmpb_tl {\tl_tail:N \l_@@_tmpb_tl} \tl_set:Nx \l_@@_tmpb_tl {\tl_tail:N \l_@@_tmpb_tl} \tl_set:Nx \l_@@_tmpb_tl {\tl_tail:N \l_@@_tmpb_tl} } \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpb_tl \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_generate_variant:Nn \@@_append_no_move:nn {VV} \cs_new_protected_nopar:Npn \spath_append_no_move:Nnn #1#2#3 { \@@_append_no_move:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_append_no_move:Nnn {NVV, NVn, NnV} \cs_new_protected_nopar:Npn \spath_append_no_move:Nn #1#2 { \spath_append_no_move:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_append_no_move:Nn {NV, cv, Nv, cV} \cs_new_protected_nopar:Npn \spath_gappend_no_move:Nnn #1#2#3 { \@@_append_no_move:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gappend_no_move:Nnn {NVV, NVn} \cs_new_protected_nopar:Npn \spath_gappend_no_move:Nn #1#2 { \spath_gappend_no_move:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gappend_no_move:Nn {NV, cv, Nv, cV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_append:Nnn, % \spath_append:Nn, % \spath_gappend:Nnn, % \spath_gappend:Nn, % } % Prepend the path from the second \Verb+spath+ to the first. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_append:Nnn #1#2#3 { \tl_set:Nn #1 {#2} \tl_put_right:Nn #1 {#3} } \cs_generate_variant:Nn \spath_append:Nnn {NVV, NVn} \cs_new_protected_nopar:Npn \spath_append:Nn #1#2 { \spath_append:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_append:Nn {NV, Nv, cv, cV} \cs_new_protected_nopar:Npn \spath_gappend:Nnn #1#2#3 { \tl_gset:Nn #1 {#2} \tl_gput_right:Nn #1 {#3} } \cs_generate_variant:Nn \spath_gappend:Nnn {NVV, NVn} \cs_new_protected_nopar:Npn \spath_gappend:Nn #1#2 { \spath_gappend:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gappend:Nn {NV, Nv, cv, cV} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \spath_prepend_no_move:Nnn, % \spath_prepend_no_move:Nn, % \spath_gprepend_no_move:Nnn, % \spath_gprepend_no_move:Nn, % } % Prepend the path from the second \Verb+spath+ to the first, removing % the adjoining move. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_prepend_no_move:Nnn #1#2#3 { \spath_append_no_move:Nnn #1{#3}{#2} } \cs_generate_variant:Nn \spath_prepend_no_move:Nnn {NVV, NVn} \cs_new_protected_nopar:Npn \spath_prepend_no_move:Nn #1#2 { \spath_prepend_no_move:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_prepend_no_move:Nn {NV, cv} \cs_new_protected_nopar:Npn \spath_gprepend_no_move:Nnn #1#2#3 { \spath_gappend_no_move:Nnn #1{#3}{#2} } \cs_generate_variant:Nn \spath_gprepend_no_move:Nnn {NVV, NVn} \cs_new_protected_nopar:Npn \spath_gprepend_no_move:Nn #1#2 { \spath_gprepend_no_move:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gprepend_no_move:Nn {NV, cv} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_prepend:Nnn, % \spath_prepend:Nn, % \spath_gprepend:Nnn, % \spath_gprepend:Nn % } % Prepend the path from the second \Verb+spath+ to the first. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_prepend:Nnn #1#2#3 { \spath_append:Nnn #1{#3}{#2} } \cs_generate_variant:Nn \spath_prepend:Nnn {NVV, NVn} \cs_new_protected_nopar:Npn \spath_prepend:Nn #1#2 { \spath_prepend:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_prepend:Nn {NV} \cs_new_protected_nopar:Npn \spath_gprepend:Nnn #1#2#3 { \spath_gappend:Nnn #1{#3}{#2} } \cs_generate_variant:Nn \spath_gprepend:Nnn {NVV, NVn} \cs_new_protected_nopar:Npn \spath_gprepend:Nn #1#2 { \spath_gprepend:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gprepend:Nn {NV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_bake_round:Nn, % \spath_bake_round:N, % \spath_gbake_round:Nn, % \spath_gbake_round:N % } % % The corner rounding routine is applied quite late in the process of building a soft path so this ensures that it is done. % % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_bake_round:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \pgf@@@@processround \l_@@_tmpa_tl\l_@@_tmpb_tl \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_bake_round:Nn #1#2 { \@@_bake_round:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_bake_round:Nn {NV} \cs_new_protected_nopar:Npn \spath_bake_round:N #1 { \spath_bake_round:NV #1#1 } \cs_generate_variant:Nn \spath_bake_round:N {c} \cs_new_protected_nopar:Npn \spath_gbake_round:Nn #1#2 { \@@_bake_round:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gbake_round:Nn {NV} \cs_new_protected_nopar:Npn \spath_gbake_round:N #1 { \spath_gbake_round:NV #1#1 } \cs_generate_variant:Nn \spath_gbake_round:N {c} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_bake_shorten:Nn, % \spath_bake_shorten:N, % \spath_gbake_shorten:Nn, % \spath_gbake_shorten:N % } % % The shortening routine is applied quite late in the process of building a soft path so this ensures that it is done. % % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_bake_shorten:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \pgfsyssoftpath@getcurrentpath\l_@@_tmpb_tl \pgfsyssoftpath@setcurrentpath\l_@@_tmpa_tl \pgf@prepare@end@of@path \pgf@prepare@start@of@path \pgfsyssoftpath@getcurrentpath\l_@@_tmpa_tl \pgfsyssoftpath@setcurrentpath\l_@@_tmpb_tl \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_new_protected_nopar:Npn \spath_bake_shorten:Nn #1#2 { \@@_bake_shorten:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_bake_shorten:Nn {NV} \cs_new_protected_nopar:Npn \spath_bake_shorten:N #1 { \spath_bake_shorten:NV #1#1 } \cs_generate_variant:Nn \spath_bake_shorten:N {c} \cs_new_protected_nopar:Npn \spath_gbake_shorten:Nn #1#2 { \@@_bake_shorten:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gbake_shorten:Nn {NV} \cs_new_protected_nopar:Npn \spath_gbake_shorten:N #1 { \spath_gbake_shorten:NV #1#1 } \cs_generate_variant:Nn \spath_gbake_shorten:N {c} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_close:Nn, % \spath_close:N, % \spath_gclose:Nn, % \spath_gclose:N % } % Appends a close path to the end of the path. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_close:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \spath_finalmovepoint:NV \l_@@_tmpb_tl \l_@@_tmpa_tl \tl_put_right:NV \l_@@_tmpa_tl \c_spath_closepath_tl \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpb_tl \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_generate_variant:Nn \@@_close:n {V} \cs_new_protected_nopar:Npn \spath_close:Nn #1#2 { \@@_close:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_close:Nn {NV} \cs_new_protected_nopar:Npn \spath_close:N #1 { \spath_close:NV #1#1 } \cs_generate_variant:Nn \spath_close:N {c} \cs_new_protected_nopar:Npn \spath_gclose:Nn #1#2 { \@@_close:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gclose:Nn {NV} \cs_new_protected_nopar:Npn \spath_gclose:N #1 { \spath_gclose:NV #1#1 } \cs_generate_variant:Nn \spath_gclose:N {c} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_adjust_close:Nn, % \spath_adjust_close:N, % \spath_adjust_gclose:Nn, % \spath_adjust_gclose:N % } % This closes a path and adjusts the end point to be where the final move point (so where the close points to) is. % The intention is that this should be used if the two points are visually the same point but mathematically different. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_adjust_close:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \spath_finalmovepoint:NV \l_@@_tmpb_tl \l_@@_tmpa_tl \spath_finalpoint:NV \l_@@_tmpc_tl \l_@@_tmpa_tl \tl_reverse:N \l_@@_tmpa_tl \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpd_tl {\tl_head:N \l_@@_tmpa_tl} \tl_if_eq:NNT \l_@@_tmpd_tl \c_spath_curveto_tl { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_clear:N \l_@@_tmpe_tl \tl_set:Nx \l_@@_tmpe_tl { { \dim_eval:n { \tl_item:Nn \l_@@_tmpa_tl {1} - \tl_item:Nn \l_@@_tmpc_tl {2} + \tl_item:Nn \l_@@_tmpb_tl {2} } } { \dim_eval:n { \tl_item:Nn \l_@@_tmpa_tl {2} - \tl_item:Nn \l_@@_tmpc_tl {1} + \tl_item:Nn \l_@@_tmpb_tl {1} } } } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_left:NV \l_@@_tmpa_tl \l_@@_tmpe_tl \tl_put_left:NV \l_@@_tmpa_tl \l_@@_tmpd_tl } \tl_reverse:N \l_@@_tmpa_tl \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpb_tl \tl_put_right:NV \l_@@_tmpa_tl \c_spath_closepath_tl \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpb_tl \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_generate_variant:Nn \@@_adjust_close:n {V} \cs_new_protected_nopar:Npn \spath_adjust_close:Nn #1#2 { \@@_adjust_close:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_adjust_close:Nn {NV} \cs_new_protected_nopar:Npn \spath_adjust_close:N #1 { \spath_adjust_close:NV #1#1 } \cs_generate_variant:Nn \spath_adjust_close:N {c} \cs_new_protected_nopar:Npn \spath_adjust_gclose:Nn #1#2 { \@@_adjust_close:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_adjust_gclose:Nn {NV} \cs_new_protected_nopar:Npn \spath_adjust_gclose:N #1 { \spath_adjust_gclose:NV #1#1 } \cs_generate_variant:Nn \spath_adjust_gclose:N {c} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \spath_open:Nn, % \spath_open:N, % \spath_gopen:Nn, % \spath_gopen:N % } % Removes all close paths from the path, replacing them by \Verb+lineto+ if they move any distance. % Rectangles are replaced by lines with the start/end at the lower left corner. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_open:n #1 { \group_begin: \spath_replace_rectangles:Nn \l_@@_tmpa_tl {#1} \tl_clear:N \l_@@_tmpb_tl \bool_until_do:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl} \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \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_generate_variant:Nn \@@_open:n {V} \cs_new_protected_nopar:Npn \spath_open:Nn #1#2 { \@@_open:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_open:Nn {NV} \cs_new_protected_nopar:Npn \spath_open:N #1 { \spath_open:NV #1#1 } \cs_new_protected_nopar:Npn \spath_gopen:Nn #1#2 { \@@_open:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gopen:Nn {NV} \cs_new_protected_nopar:Npn \spath_gopen:N #1 { \spath_gopen:NV #1#1 } \cs_generate_variant:Nn \spath_open:N {c} \cs_generate_variant:Nn \spath_gopen:N {c} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_replace_lines:Nn, % \spath_replace_lines:Nn, % \spath_replace_lines:Nn, % \spath_replace_lines:Nn, % } % Replace any line segments by B\'ezier curves. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_replace_lines:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_clear:N \l_@@_tmpb_tl \dim_set:Nn \l_@@_tmpa_dim {0pt} \dim_set:Nn \l_@@_tmpb_dim {0pt} \bool_do_until:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_set:Nx \l_@@_tmpc_tl {\tl_item:Nn \l_@@_tmpa_tl {1}} \tl_set:Nx \l_@@_tmpd_tl {\tl_item:Nn \l_@@_tmpa_tl {2}} \tl_set:Nx \l_@@_tmpe_tl {\tl_item:Nn \l_@@_tmpa_tl {3}} \tl_if_eq:NNTF \l_@@_tmpc_tl \c_spath_lineto_tl { \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curvetoa_tl \tl_put_right:Nx \l_@@_tmpb_tl { { \fp_to_dim:n { 2/3 * (\l_@@_tmpa_dim) + 1/3 * (\l_@@_tmpd_tl) } } } \tl_put_right:Nx \l_@@_tmpb_tl { { \fp_to_dim:n { 2/3 * (\l_@@_tmpb_dim) + 1/3 * (\l_@@_tmpe_tl) } } } \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curvetob_tl \tl_put_right:Nx \l_@@_tmpb_tl { { \fp_to_dim:n { 1/3 * (\l_@@_tmpa_dim) + 2/3 * (\l_@@_tmpd_tl) } } } \tl_put_right:Nx \l_@@_tmpb_tl { { \fp_to_dim:n { 1/3 * (\l_@@_tmpb_dim) + 2/3 * (\l_@@_tmpe_tl) } } } \tl_put_right:NV \l_@@_tmpb_tl \c_spath_curveto_tl \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpd_tl \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpe_tl } { \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpc_tl \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpd_tl \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpe_tl } \dim_set:Nn \l_@@_tmpa_dim {\l_@@_tmpd_tl} \dim_set:Nn \l_@@_tmpb_dim {\l_@@_tmpe_tl} \prg_replicate:nn {3} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_generate_variant:Nn \@@_replace_lines:n {V} \cs_new_protected_nopar:Npn \spath_replace_lines:Nn #1#2 { \@@_replace_lines:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_replace_lines:Nn {NV, cV, cv, Nv} \cs_new_protected_nopar:Npn \spath_replace_lines:N #1 { \spath_replace_lines:NV #1#1 } \cs_generate_variant:Nn \spath_replace_lines:N {c} \cs_new_protected_nopar:Npn \spath_greplace_lines:Nn #1#2 { \@@_replace_lines:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_greplace_lines:Nn {NV, cV, cv, Nv} \cs_new_protected_nopar:Npn \spath_greplace_lines:N #1 { \spath_greplace_lines:NV #1#1 } \cs_generate_variant:Nn \spath_greplace_lines:N {c} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_replace_rectangles:Nn, % \spath_replace_rectangles:Nn, % \spath_replace_rectangles:Nn, % \spath_replace_rectangles:Nn, % } % Replace any rectangle components by lines. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_replace_rectangles:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_clear:N \l_@@_tmpb_tl \bool_do_until:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl } \tl_set:Nx \l_@@_tmpd_tl {\tl_head:N \l_@@_tmpa_tl } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl } \tl_set:Nx \l_@@_tmpe_tl {\tl_head:N \l_@@_tmpa_tl } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl } \tl_if_eq:NNTF \l_@@_tmpc_tl \c_spath_rectcorner_tl { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl } \dim_set:Nn \l_@@_tmpa_dim { \tl_item:Nn \l_@@_tmpa_tl {1} } \dim_set:Nn \l_@@_tmpb_dim { \tl_item:Nn \l_@@_tmpa_tl {2} } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl } \tl_put_right:NV \l_@@_tmpb_tl \c_spath_moveto_tl \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpd_tl \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpe_tl \tl_put_right:NV \l_@@_tmpb_tl \c_spath_lineto_tl \tl_put_right:Nx \l_@@_tmpb_tl { { \fp_to_dim:n { \l_@@_tmpd_tl + \l_@@_tmpa_dim } } } \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpe_tl \tl_put_right:NV \l_@@_tmpb_tl \c_spath_lineto_tl \tl_put_right:Nx \l_@@_tmpb_tl { { \fp_to_dim:n { \l_@@_tmpd_tl + \l_@@_tmpa_dim } } } \tl_put_right:Nx \l_@@_tmpb_tl { { \fp_to_dim:n { \l_@@_tmpe_tl + \l_@@_tmpb_dim } } } \tl_put_right:NV \l_@@_tmpb_tl \c_spath_lineto_tl \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpd_tl \tl_put_right:Nx \l_@@_tmpb_tl { { \fp_to_dim:n { \l_@@_tmpe_tl + \l_@@_tmpb_dim } } } \tl_put_right:NV \l_@@_tmpb_tl \c_spath_closepath_tl \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpd_tl \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpe_tl } { \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpc_tl \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpd_tl \@@_tl_put_right_braced:NV \l_@@_tmpb_tl \l_@@_tmpe_tl } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_generate_variant:Nn \@@_replace_rectangles:n {V} \cs_new_protected_nopar:Npn \spath_replace_rectangles:Nn #1#2 { \@@_replace_rectangles:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_replace_rectangles:Nn {NV, cV, cv, Nv} \cs_new_protected_nopar:Npn \spath_replace_rectangles:N #1 { \spath_replace_rectangles:NV #1#1 } \cs_generate_variant:Nn \spath_replace_rectangles:N {c} \cs_new_protected_nopar:Npn \spath_greplace_rectangles:Nn #1#2 { \@@_replace_rectangles:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_greplace_rectangles:Nn {NV, cV, cv, Nv} \cs_new_protected_nopar:Npn \spath_greplace_rectangles:N #1 { \spath_greplace_rectangles:NV #1#1 } \cs_generate_variant:Nn \spath_greplace_rectangles:N {c} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_remove_empty_components:Nn, % \spath_remove_empty_components:N, % \spath_gremove_empty_components:Nn, % \spath_gremove_empty_components:N % } % Remove any component that is simply a moveto. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_remove_empty_components:n #1 { \group_begin: \spath_components_to_seq:Nn \l_@@_tmpa_seq {#1} \tl_clear:N \l_@@_tmpa_tl \seq_map_inline:Nn \l_@@_tmpa_seq { \int_compare:nF { \tl_count:n {##1} == 3 } { \tl_put_right:Nn \l_@@_tmpa_tl {##1} } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_new_protected_nopar:Npn \spath_remove_empty_components:Nn #1#2 { \@@_remove_empty_components:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_remove_empty_components:Nn {NV} \cs_new_protected_nopar:Npn \spath_remove_empty_components:N #1 { \spath_remove_empty_components:NV #1#1 } \cs_generate_variant:Nn \spath_remove_empty_components:N {c} \cs_new_protected_nopar:Npn \spath_gremove_empty_components:Nn #1#2 { \@@_remove_empty_components:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gremove_empty_components:Nn {NV} \cs_new_protected_nopar:Npn \spath_gremove_empty_components:N #1 { \spath_gremove_empty_components:NV #1#1 } \cs_generate_variant:Nn \spath_gremove_empty_components:N {c} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\spath_if_eq:nn} % Test if two soft paths are equal, we allow a little tolerance on the calculations. % \begin{macrocode} \prg_new_protected_conditional:Npnn \spath_if_eq:nn #1#2 { T, F, TF } { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_set:Nn \l_@@_tmpb_tl {#2} \bool_gset_true:N \g_@@_tmpa_bool \int_compare:nNnTF {\tl_count:N \l_@@_tmpa_tl} = {\tl_count:N \l_@@_tmpb_tl} { \int_step_inline:nnnn {1} {3} {\tl_count:N \l_@@_tmpa_tl} { \tl_set:Nx \l_@@_tmpc_tl {\tl_item:Nn \l_@@_tmpa_tl {##1}} \tl_set:Nx \l_@@_tmpd_tl {\tl_item:Nn \l_@@_tmpb_tl {##1}} \tl_if_eq:NNF \l_@@_tmpc_tl \l_@@_tmpd_tl { \bool_gset_false:N \g_@@_tmpa_bool } \dim_set:Nn \l_@@_tmpa_dim {\tl_item:Nn \l_@@_tmpa_tl {##1+1}} \dim_set:Nn \l_@@_tmpb_dim {\tl_item:Nn \l_@@_tmpb_tl {##1+1}} \dim_compare:nF { \dim_abs:n { \l_@@_tmpa_dim - \l_@@_tmpb_dim } < 0.001pt } { \bool_gset_false:N \g_@@_tmpa_bool } \dim_set:Nn \l_@@_tmpa_dim {\tl_item:Nn \l_@@_tmpa_tl {##1+2}} \dim_set:Nn \l_@@_tmpb_dim {\tl_item:Nn \l_@@_tmpb_tl {##1+2}} \dim_compare:nF { \dim_abs:n { \l_@@_tmpa_dim - \l_@@_tmpb_dim } < 0.001pt } { \bool_gset_false:N \g_@@_tmpa_bool } } } { \bool_gset_false:N \g_@@_tmpa_bool } \group_end: \bool_if:NTF \g_@@_tmpa_bool { \prg_return_true: } { \prg_return_false: } } \prg_generate_conditional_variant:Nnn \spath_if_eq:nn {VV, Vn, nV, vv} {TF, T, F} % \end{macrocode} % \end{macro} % % \subsection{Splitting Commands} % % \begin{macro}[internal]{ % \spath_split_curve:NNnn, % \spath_gsplit_curve:NNnn % } % Splits a Bezier cubic into pieces, storing the pieces in the first two arguments. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_split_curve:nn #1#2 { \group_begin: \tl_set_eq:NN \l_@@_tmpa_tl \c_spath_moveto_tl \tl_put_right:Nx \l_@@_tmpa_tl { {\tl_item:nn {#1} {2}} {\tl_item:nn {#1} {3}} } \tl_put_right:NV \l_@@_tmpa_tl \c_spath_curvetoa_tl \tl_put_right:Nx \l_@@_tmpa_tl { {\fp_to_dim:n { (1 - #2) * \tl_item:nn {#1} {2} + (#2) * \tl_item:nn {#1} {5} }} {\fp_to_dim:n { (1 - #2) * \tl_item:nn {#1} {3} + (#2) * \tl_item:nn {#1} {6} }} } \tl_put_right:NV \l_@@_tmpa_tl \c_spath_curvetob_tl \tl_put_right:Nx \l_@@_tmpa_tl { {\fp_to_dim:n { (1 - #2)^2 * \tl_item:nn {#1} {2} + 2 * (1 - #2) * (#2) * \tl_item:nn {#1} {5} + (#2)^2 * \tl_item:nn {#1} {8} }} {\fp_to_dim:n { (1 - #2)^2 * \tl_item:nn {#1} {3} + 2 * (1 - #2) * (#2) * \tl_item:nn {#1} {6} + (#2)^2 * \tl_item:nn {#1} {9} }} } \tl_put_right:NV \l_@@_tmpa_tl \c_spath_curveto_tl \tl_put_right:Nx \l_@@_tmpa_tl { {\fp_to_dim:n { (1 - #2)^3 * \tl_item:nn {#1} {2} + 3 * (1 - #2)^2 * (#2) * \tl_item:nn {#1} {5} + 3 * (1 - #2) * (#2)^2 * \tl_item:nn {#1} {8} + (#2)^3 * \tl_item:nn {#1} {11} }} {\fp_to_dim:n { (1 - #2)^3 * \tl_item:nn {#1} {3} + 3 * (1 - #2)^2 * (#2) * \tl_item:nn {#1} {6} + 3 * (1 - #2) * (#2)^2 * \tl_item:nn {#1} {9} + (#2)^3 * \tl_item:nn {#1} {12} }} } \tl_gclear:N \g_@@_output_tl \@@_tl_gput_right_braced:NV \g_@@_output_tl \l_@@_tmpa_tl \tl_clear:N \l_@@_tmpa_tl \tl_set_eq:NN \l_@@_tmpa_tl \c_spath_moveto_tl \tl_put_right:Nx \l_@@_tmpa_tl { {\fp_to_dim:n { (1 - #2)^3 * \tl_item:nn {#1} {2} + 3 * (1 - #2)^2 * (#2) * \tl_item:nn {#1} {5} + 3 * (1 - #2) * (#2)^2 * \tl_item:nn {#1} {8} + (#2)^3 * \tl_item:nn {#1} {11} }} {\fp_to_dim:n { (1 - #2)^3 * \tl_item:nn {#1} {3} + 3 * (1 - #2)^2 * (#2) * \tl_item:nn {#1} {6} + 3 * (1 - #2) * (#2)^2 * \tl_item:nn {#1} {9} + (#2)^3 * \tl_item:nn {#1} {12} }} } \tl_put_right:NV \l_@@_tmpa_tl \c_spath_curvetoa_tl \tl_put_right:Nx \l_@@_tmpa_tl { {\fp_to_dim:n { (1 - #2)^2 * \tl_item:nn {#1} {5} + 2 * (1 - #2) * (#2) * \tl_item:nn {#1} {8} + (#2)^2 * \tl_item:nn {#1} {11} }} {\fp_to_dim:n { (1 - #2)^2 * \tl_item:nn {#1} {6} + 2 * (1 - #2) * (#2) * \tl_item:nn {#1} {9} + (#2)^2 * \tl_item:nn {#1} {12} }} } \tl_put_right:NV \l_@@_tmpa_tl \c_spath_curvetob_tl \tl_put_right:Nx \l_@@_tmpa_tl { {\fp_to_dim:n { (1 - #2) * \tl_item:nn {#1} {8} + (#2) * \tl_item:nn {#1} {11} }} {\fp_to_dim:n { (1 - #2) * \tl_item:nn {#1} {9} + (#2) * \tl_item:nn {#1} {12} }} } \tl_put_right:NV \l_@@_tmpa_tl \c_spath_curveto_tl \tl_put_right:Nx \l_@@_tmpa_tl { {\tl_item:nn {#1} {11}} {\tl_item:nn {#1} {12}} } \@@_tl_gput_right_braced:NV \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_generate_variant:Nn \@@_split_curve:nn {nv, nV} \cs_new_protected_nopar:Npn \spath_split_curve:NNnn #1#2#3#4 { \@@_split_curve:nn {#3}{#4} \tl_set:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}} \tl_set:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_curve:NNnn {NNnV, NNVn, NNVV} \cs_new_protected_nopar:Npn \spath_gsplit_curve:NNnn #1#2#3#4 { \@@_split_curve:nn {#3}{#4} \tl_gset:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}} \tl_gset:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_curve:NNnn {NNnV, NNVn, NNVV} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \spath_maybe_split_curve:Nn, % \spath_maybe_gsplit_curve:Nn, % } % Possibly splits a bezier curve to ensure that the pieces don't self-intersect. % Figuring out whether a Bezier cubic self intersects is apparently a difficult problem so we don't bother. % We compute a point such that if there is an intersection then it lies on either side of the point. % I don't recall where the formula came from! % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_maybe_split_curve:n #1 { \group_begin: \fp_set:Nn \l_@@_tmpa_fp { ( \tl_item:nn {#1} {3} - 3 * \tl_item:nn {#1} {6} + 3 * \tl_item:nn {#1} {9} - \tl_item:nn {#1} {12} ) * (3 * \tl_item:nn {#1} {8} - 3 * \tl_item:nn {#1} {11}) - ( \tl_item:nn {#1} {2} - 3 * \tl_item:nn {#1} {5} + 3 * \tl_item:nn {#1} {8} - \tl_item:nn {#1} {11} ) * (3 * \tl_item:nn {#1} {9} - 3 * \tl_item:nn {#1} {12}) } \fp_set:Nn \l_@@_tmpb_fp { ( \tl_item:nn {#1} {2} - 3 * \tl_item:nn {#1} {5} + 3 * \tl_item:nn {#1} {8} - \tl_item:nn {#1} {11} ) * ( 3 * \tl_item:nn {#1} {6} - 6 * \tl_item:nn {#1} {9} + 3 * \tl_item:nn {#1} {12} ) - ( \tl_item:nn {#1} {3} - 3 * \tl_item:nn {#1} {6} + 3 * \tl_item:nn {#1} {9} - \tl_item:nn {#1} {12} ) * ( 3 * \tl_item:nn {#1} {5} - 6 * \tl_item:nn {#1} {8} + 3 * \tl_item:nn {#1} {11} ) } \fp_compare:nTF { \l_@@_tmpb_fp != 0 } { \fp_set:Nn \l_@@_tmpa_fp {.5 * \l_@@_tmpa_fp / \l_@@_tmpb_fp} \bool_if:nTF { \fp_compare_p:n {0 < \l_@@_tmpa_fp} && \fp_compare_p:n {\l_@@_tmpa_fp < 1} } { \@@_split_curve:nV {#1} \l_@@_tmpa_fp } { \tl_gset:Nn \g_@@_output_tl { {#1} {} } } } { \tl_gset:Nn \g_@@_output_tl { {#1} {} } } \group_end: } \cs_new_protected_nopar:Npn \spath_maybe_split_curve:NNn #1#2#3 { \@@_maybe_split_curve:n {#3} \tl_set:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}} \tl_set:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_maybe_split_curve:NNn {NNn, NNV } \cs_new_protected_nopar:Npn \spath_maybe_gsplit_curve:NNn #1#2#3 { \@@_maybe_split_curve:n {#3} \tl_gset:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}} \tl_gset:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_maybe_gsplit_curve:NNn {NNn, NNV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_split_curves:Nn, % \spath_gsplit_curves:Nn, % } % Slurp through the path ensuring that beziers don't self-intersect. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_split_curves:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_clear:N \l_@@_tmpb_tl \tl_clear:N \l_@@_tmpc_tl \bool_do_until:nn { \tl_if_empty_p:N \l_@@_tmpa_tl } { \tl_set:Nx \l_@@_tmpc_tl {\tl_head:N \l_@@_tmpa_tl} \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_rectangle:Nnn, % \spath_gsplit_rectangle:Nnn % } % Cuts a rectangle at a point. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_split_rectangle:nn #1#2 { \group_begin: \spath_open:Nn \l_@@_tmpa_tl {#1} \fp_set:Nn \l_@@_tmpa_fp {4*(#2)} \spath_split_at:NNVV \l_@@_tmpa_tl \l_@@_tmpb_tl \l_@@_tmpa_tl \l_@@_tmpa_fp \@@_append_no_move:VV \l_@@_tmpb_tl \l_@@_tmpa_tl \group_end: } \cs_new_protected_nopar:Npn \spath_split_rectangle:Nnn #1#2#3 { \@@_split_rectangle:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_rectangle:Nnn {NnV, NVn, NVV} \cs_new_protected_nopar:Npn \spath_gsplit_rectangle:Nnn #1#2#3 { \@@_split_rectangle:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_rectangle:Nnn {NnV, NVn, NVV} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \spath_split_at:NNnn, % \spath_split_at:Nnn, % \spath_split_at:Nn, % \spath_gsplit_at:NNnn % \spath_gsplit_at:Nnn, % \spath_gsplit_at:Nn, % \spath_split_at_keep_start:Nnn, % \spath_split_at_keep_start:Nn, % \spath_gsplit_at_keep_start:Nnn, % \spath_gsplit_at_keep_start:Nn, % \spath_split_at_keep_end:Nnn, % \spath_split_at_keep_end:Nn, % \spath_gsplit_at_keep_end:Nnn, % \spath_gsplit_at_keep_end:Nn, % \spath_split_at_normalised:NNnn, % \spath_split_at_normalised:Nnn, % \spath_split_at_normalised:Nn, % \spath_gsplit_at_normalised:NNnn % \spath_gsplit_at_normalised:Nnn, % \spath_gsplit_at_normalised:Nn, % \spath_split_at_normalised_keep_start:Nnn, % \spath_split_at_normalised_keep_start:Nn, % \spath_gsplit_at_normalised_keep_start:Nnn, % \spath_gsplit_at_normalised_keep_start:Nn, % \spath_split_at_normalised_keep_end:Nnn, % \spath_split_at_normalised_keep_end:Nn, % \spath_gsplit_at_normalised_keep_end:Nnn, % \spath_gsplit_at_normalised_keep_end:Nn, % } % Split a path according to the parameter generated by the intersection routine. % The versions with two \texttt{N} arguments stores the two parts in two macros, the version with a single \texttt{N} joins them back into a single path (as separate components). % The \texttt{keep} versions throw away the other part of the curve. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_split_at:nn #1#2 { \group_begin: \int_set:Nn \l_@@_tmpa_int {\fp_to_int:n {floor(#2) + 1}} \fp_set:Nn \l_@@_tmpa_fp {#2 - floor(#2)} % Is split point near one end or other of a component? \fp_compare:nT { \l_@@_tmpa_fp < 0.01 } { % Near the start, so we'll place it at the start \fp_set:Nn \l_@@_tmpa_fp {0} } \fp_compare:nT { \l_@@_tmpa_fp > 0.99 } { % Near the end, so we'll place it at the end \fp_set:Nn \l_@@_tmpa_fp {0} \int_incr:N \l_@@_tmpa_int } \int_zero:N \l_@@_tmpb_int \bool_set_true:N \l_@@_tmpa_bool \tl_set:Nn \l_@@_tmpe_tl {#1} \dim_zero:N \l_@@_tmpa_dim \dim_zero:N \l_@@_tmpb_dim % Remember if the component is closed \spath_finalaction:NV \l_@@_tmpa_tl \l_@@_tmpe_tl \bool_set:Nn \l_@@_closed_bool { \tl_if_eq_p:NN \l_@@_tmpa_tl \c_spath_closepath_tl || \tl_if_eq_p:NN \l_@@_tmpa_tl \c_spath_rectcorner_tl } % Open it \spath_open:N \l_@@_tmpe_tl \tl_clear:N \l_@@_tmpa_tl \tl_clear:N \l_@@_tmpb_tl \tl_clear:N \l_@@_tmpc_tl \tl_clear:N \l_@@_tmpd_tl \bool_until_do:nn { \tl_if_empty_p:N \l_@@_tmpe_tl || \int_compare_p:n { \l_@@_tmpa_int == \l_@@_tmpb_int } } { \tl_set:Nx \l_@@_tmpf_tl {\tl_head:N \l_@@_tmpe_tl} \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl } \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 } \c_spath_rectcorner_tl { \int_incr:N \l_@@_tmpb_int } } \int_compare:nT { \l_@@_tmpb_int < \l_@@_tmpa_int } { \tl_put_right:NV \l_@@_tmpc_tl \l_@@_tmpf_tl \tl_put_right:Nx \l_@@_tmpc_tl {{ \tl_head:N \l_@@_tmpe_tl }} \dim_set:Nn \l_@@_tmpa_dim {\tl_head:N \l_@@_tmpe_tl} \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl } \tl_put_right:Nx \l_@@_tmpc_tl {{ \tl_head:N \l_@@_tmpe_tl }} \dim_set:Nn \l_@@_tmpb_dim {\tl_head:N \l_@@_tmpe_tl} \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl } } } \tl_clear:N \l_@@_tmpd_tl \tl_put_right:NV \l_@@_tmpd_tl \c_spath_moveto_tl \tl_put_right:Nx \l_@@_tmpd_tl { {\dim_use:N \l_@@_tmpa_dim} {\dim_use:N \l_@@_tmpb_dim} } \fp_compare:nTF { \l_@@_tmpa_fp == 0 } { \tl_set_eq:NN \l_@@_tmpb_tl \l_@@_tmpd_tl \tl_if_empty:NF \l_@@_tmpe_tl { \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpf_tl \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpe_tl } } { \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 } \c_spath_rectcorner_tl { \tl_clear:N \l_@@_tmpd_tl \tl_put_right:NV \l_@@_tmpd_tl \l_@@_tmpf_tl \tl_put_right:Nx \l_@@_tmpd_tl {{\tl_head:N \l_@@_tmpe_tl}} \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl} \tl_put_right:Nx \l_@@_tmpd_tl {{\tl_head:N \l_@@_tmpe_tl}} \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl} \tl_put_right:Nx \l_@@_tmpd_tl {\tl_head:N \l_@@_tmpe_tl} \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl} \tl_put_right:Nx \l_@@_tmpd_tl {{\tl_head:N \l_@@_tmpe_tl}} \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl} \tl_put_right:Nx \l_@@_tmpd_tl {{\tl_head:N \l_@@_tmpe_tl}} \tl_set:Nx \l_@@_tmpe_tl {\tl_tail:N \l_@@_tmpe_tl} \spath_split_rectangle:NVV \l_@@_tmpa_tl \l_@@_tmpd_tl \l_@@_tmpa_fp \tl_put_right:NV \l_@@_tmpc_tl \l_@@_tmpa_tl \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpe_tl } } } \bool_if:NT \l_@@_closed_bool { \prg_replicate:nn {3} { \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} } \tl_put_right:NV \l_@@_tmpb_tl \l_@@_tmpc_tl \tl_set_eq:NN \l_@@_tmpc_tl \l_@@_tmpb_tl \tl_clear:N \l_@@_tmpb_tl } \tl_gclear:N \g_@@_output_tl \@@_tl_gput_right_braced:NV \g_@@_output_tl \l_@@_tmpc_tl \@@_tl_gput_right_braced:NV \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_generate_variant:Nn \@@_split_at:nn {nV, VV} \cs_new_protected_nopar:Npn \@@_split_at_normalised:nn #1#2 { \group_begin: \spath_reallength:Nn \l_@@_tmpa_int {#1} \tl_set:Nx \l_@@_tmpa_tl {\fp_to_decimal:n {(#2) * (\l_@@_tmpa_int)}} \@@_split_at:nV {#1} \l_@@_tmpa_tl \group_end: } \cs_generate_variant:Nn \@@_split_at_normalised:nn {nV} \cs_new_protected_nopar:Npn \spath_split_at:NNnn #1#2#3#4 { \@@_split_at:nn {#3}{#4} \tl_set:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}} \tl_set:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_at:NNnn {NNVn, NNVV, NNnV} \cs_new_protected_nopar:Npn \spath_gsplit_at:NNnn #1#2#3#4 { \@@_split_at:nn {#3}{#4} \tl_gset:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}} \tl_gset:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_at:NNnn {NNVn, NNVV, NNnV} \cs_new_protected_nopar:Npn \spath_split_at_keep_start:Nnn #1#2#3 { \@@_split_at:nn {#2}{#3} \tl_set:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_at_keep_start:Nnn {NVn} \cs_new_protected_nopar:Npn \spath_split_at_keep_start:Nn #1#2 { \spath_split_at_keep_start:NVn #1#1{#2} } \cs_new_protected_nopar:Npn \spath_gsplit_at_keep_start:Nnn #1#2#3 { \@@_split_at:nn {#2}{#3} \tl_gset:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_at_keep_start:Nnn {NVn} \cs_new_protected_nopar:Npn \spath_gsplit_at_keep_start:Nn #1#2 { \spath_gsplit_at_keep_start:NVn #1#1{#2} } \cs_new_protected_nopar:Npn \spath_split_at_keep_end:Nnn #1#2#3 { \@@_split_at:nn {#2}{#3} \tl_set:Nx #1 {\tl_item:Nn \g_@@_output_tl {2}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_at_keep_end:Nnn {NVn} \cs_new_protected_nopar:Npn \spath_split_at_keep_end:Nn #1#2 { \spath_split_at_keep_end:NVn #1#1{#2} } \cs_new_protected_nopar:Npn \spath_gsplit_at_keep_end:Nnn #1#2#3 { \@@_split_at:nn {#2}{#3} \tl_gset:Nx #1 {\tl_item:Nn \g_@@_output_tl {2}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_at_keep_end:Nnn {NVn} \cs_new_protected_nopar:Npn \spath_gsplit_at_keep_end:Nn #1#2 { \spath_gsplit_at_keep_end:NVn #1#1{#2} } \cs_new_protected_nopar:Npn \spath_split_at_normalised:NNnn #1#2#3#4 { \@@_split_at_normalised:nn {#3}{#4} \tl_set:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}} \tl_set:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_at_normalised:NNnn {NNVn, NNVV, NNnV, ccvn} \cs_new_protected_nopar:Npn \spath_gsplit_at_normalised:NNnn #1#2#3#4 { \@@_split_at_normalised:nn {#3}{#4} \tl_gset:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}} \tl_gset:Nx #2 {\tl_item:Nn \g_@@_output_tl {2}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_at_normalised:NNnn {NNVn, NNVV, NNnV, ccvn} \cs_new_protected_nopar:Npn \spath_split_at_normalised_keep_start:Nnn #1#2#3 { \@@_split_at_normalised:nn {#2}{#3} \tl_set:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_at_normalised_keep_start:Nnn {NVn} \cs_new_protected_nopar:Npn \spath_split_at_normalised_keep_start:Nn #1#2 { \spath_split_at_normalised_keep_start:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_split_at_normalised_keep_start:Nn {cn} \cs_new_protected_nopar:Npn \spath_gsplit_at_normalised_keep_start:Nnn #1#2#3 { \@@_split_at_normalised:nn {#2}{#3} \tl_gset:Nx #1 {\tl_item:Nn \g_@@_output_tl {1}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_at_normalised_keep_start:Nnn {NVn} \cs_new_protected_nopar:Npn \spath_gsplit_at_normalised_keep_start:Nn #1#2 { \spath_gsplit_at_normalised_keep_start:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gsplit_at_normalised_keep_start:Nn {cn} \cs_new_protected_nopar:Npn \spath_split_at_normalised_keep_end:Nnn #1#2#3 { \@@_split_at_normalised:nn {#2}{#3} \tl_set:Nx #1 {\tl_item:Nn \g_@@_output_tl {2}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_at_normalised_keep_end:Nnn {NVn} \cs_new_protected_nopar:Npn \spath_split_at_normalised_keep_end:Nn #1#2 { \spath_split_at_normalised_keep_end:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_split_at_normalised_keep_end:Nn {cn} \cs_new_protected_nopar:Npn \spath_gsplit_at_normalised_keep_end:Nnn #1#2#3 { \@@_split_at_normalised:nn {#2}{#3} \tl_gset:Nx #1 {\tl_item:Nn \g_@@_output_tl {2}} \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_at_normalised_keep_end:Nnn {NVn} \cs_new_protected_nopar:Npn \spath_gsplit_at_normalised_keep_end:Nn #1#2 { \spath_gsplit_at_normalised_keep_end:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gsplit_at_normalised_keep_end:Nn {cn} % \end{macrocode} % % % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_split_at:Nnn #1#2#3 { \@@_split_at:nn {#2}{#3} \tl_set:Nx #1 { \tl_item:Nn \g_@@_output_tl {1} \tl_item:Nn \g_@@_output_tl {2} } \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_at:Nnn {NVn, NVV} \cs_new_protected_nopar:Npn \spath_split_at:Nn #1#2 { \spath_split_at:NVn #1#1{#2} } \cs_new_protected_nopar:Npn \spath_gsplit_at:Nnn #1#2#3 { \@@_split_at:nn {#2}{#3} \tl_gset:Nx #1 { \tl_item:Nn \g_@@_output_tl {1} \tl_item:Nn \g_@@_output_tl {2} } \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_at:Nnn {NVn, NVV} \cs_new_protected_nopar:Npn \spath_gsplit_at:Nn #1#2 { \spath_gsplit_at:NVn #1#1{#2} } \cs_new_protected_nopar:Npn \spath_split_at_normalised:Nnn #1#2#3 { \@@_split_at_normalised:nn {#2}{#3} \tl_set:Nx #1 { \tl_item:Nn \g_@@_output_tl {1} \tl_item:Nn \g_@@_output_tl {2} } \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_at_normalised:Nnn {NVn, NVV} \cs_new_protected_nopar:Npn \spath_split_at_normalised:Nn #1#2 { \spath_split_at_normalised:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_split_at_normalised:Nn {cn} \cs_new_protected_nopar:Npn \spath_gsplit_at_normalised:Nnn #1#2#3 { \@@_split_at_normalised:nn {#2}{#3} \tl_gset:Nx #1 { \tl_item:Nn \g_@@_output_tl {1} \tl_item:Nn \g_@@_output_tl {2} } \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_at_normalised:Nnn {NVn, NVV} \cs_new_protected_nopar:Npn \spath_gsplit_at_normalised:Nn #1#2 { \spath_gsplit_at_normalised:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gsplit_at_normalised:Nn {cn} % \end{macrocode} % \end{macro} % % % \subsection{Shortening Paths} % % This code relates to shortening paths. % For curved paths, the routine uses the derivative at the end to figure out how far back to shorten. % This means that the actual length that it shortens by is approximate, but it is guaranteed to be along its length. % % As in the previous section, there are various versions. % In particular, there are versions where the path can be specified by a macro and is saved back into that macro. % % \begin{macro}[internal]{ % \spath_shorten_at_end:Nnn % } % This macro shortens a path from the end by a dimension. % % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_shorten_at_end:nn #1#2 { \int_compare:nTF { \tl_count:n {#1} > 3 } { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_reverse:N \l_@@_tmpa_tl \tl_set:Nx \l_@@_tmpb_tl {\tl_item:Nn \l_@@_tmpa_tl {3}} \tl_clear:N \l_@@_tmpe_tl \tl_if_eq:NNTF \l_@@_tmpb_tl \c_spath_curveto_tl { \int_set:Nn \l_@@_tmpa_int {3} } { \int_set:Nn \l_@@_tmpa_int {1} } \prg_replicate:nn { \l_@@_tmpa_int } { \tl_put_right:Nx \l_@@_tmpe_tl { {\tl_head:N \l_@@_tmpa_tl} } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpe_tl { {\tl_head:N \l_@@_tmpa_tl} } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} \tl_put_right:Nx \l_@@_tmpe_tl { \tl_head:N \l_@@_tmpa_tl } \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \tl_put_right:Nx \l_@@_tmpe_tl { {\tl_item:Nn \l_@@_tmpa_tl {1}} {\tl_item:Nn \l_@@_tmpa_tl {2}} } \tl_put_right:NV \l_@@_tmpe_tl \c_spath_moveto_tl \tl_reverse:N \l_@@_tmpa_tl \fp_set:Nn \l_@@_tmpa_fp { \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {4}} - \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {1}} } \fp_set:Nn \l_@@_tmpb_fp { \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {5}} - \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {2}} } \fp_set:Nn \l_@@_tmpc_fp { sqrt( \l_@@_tmpa_fp * \l_@@_tmpa_fp + \l_@@_tmpb_fp * \l_@@_tmpb_fp ) * \l_@@_tmpa_int } \fp_compare:nTF { \l_@@_tmpc_fp > #2 } { \fp_set:Nn \l_@@_tmpc_fp { (\l_@@_tmpc_fp - #2)/ \l_@@_tmpc_fp } \tl_reverse:N \l_@@_tmpe_tl \tl_if_eq:NNTF \l_@@_tmpb_tl \c_spath_curveto_tl { \spath_split_curve:NNVV \l_@@_tmpc_tl \l_@@_tmpd_tl \l_@@_tmpe_tl \l_@@_tmpc_fp } { \spath_split_line:NNVV \l_@@_tmpc_tl \l_@@_tmpd_tl \l_@@_tmpe_tl \l_@@_tmpc_fp } \prg_replicate:nn {3} { \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} } \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpc_tl } { \int_compare:nT { \tl_count:N \l_@@_tmpa_tl > 3 } { \dim_set:Nn \l_@@_tmpa_dim {\fp_to_dim:n {#2 - \l_@@_tmpc_fp } } \spath_shorten_at_end:NV \l_@@_tmpa_tl \l_@@_tmpa_dim } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } { \tl_gset:Nn \g_@@_output_tl {#1} } } \cs_new_protected_nopar:Npn \spath_shorten_at_end:Nnn #1#2#3 { \@@_shorten_at_end:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_shorten_at_end:Nnn {NVV, cnn, cVV, NVn} \cs_new_protected_nopar:Npn \spath_shorten_at_end:Nn #1#2 { \spath_shorten_at_end:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_shorten_at_end:Nn {cn, cV, NV} \cs_new_protected_nopar:Npn \spath_gshorten_at_end:Nnn #1#2#3 { \@@_shorten_at_end:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gshorten_at_end:Nnn {NVV, cnn, cVV, NVn} \cs_new_protected_nopar:Npn \spath_gshorten_at_end:Nn #1#2 { \spath_gshorten_at_end:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gshorten_at_end:Nn {cn, cV, NV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_shorten_at_start:Nnn, % \spath_shorten_at_start:Nn, % \spath_gshorten_at_start:Nnn, % \spath_gshorten_at_start:Nn % } % % This macro shortens a path from the start by a dimension. % % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_shorten_at_start:nn #1#2 { \int_compare:nTF { \tl_count:n {#1} > 3 } { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_set:Nx \l_@@_tmpb_tl {\tl_item:Nn \l_@@_tmpa_tl {4}} \tl_clear:N \l_@@_tmpe_tl \tl_if_eq:NNTF \l_@@_tmpb_tl \c_spath_curvetoa_tl { \int_set:Nn \l_@@_tmpa_int {3} } { \int_set:Nn \l_@@_tmpa_int {1} } \tl_set_eq:NN \l_@@_tmpe_tl \c_spath_moveto_tl \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl } \prg_replicate:nn { \l_@@_tmpa_int } { \@@_tl_put_right_braced:Nx \l_@@_tmpe_tl {\tl_item:Nn \l_@@_tmpa_tl {1}} \@@_tl_put_right_braced:Nx \l_@@_tmpe_tl {\tl_item:Nn \l_@@_tmpa_tl {2}} \tl_put_right:Nx \l_@@_tmpe_tl {\tl_item:Nn \l_@@_tmpa_tl {3}} \prg_replicate:nn {3} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl } } } \@@_tl_put_right_braced:Nx \l_@@_tmpe_tl {\tl_item:Nn \l_@@_tmpa_tl {1}} \@@_tl_put_right_braced:Nx \l_@@_tmpe_tl {\tl_item:Nn \l_@@_tmpa_tl {2}} \fp_set:Nn \l_@@_tmpa_fp { \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {5}} - \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {2}} } \fp_set:Nn \l_@@_tmpb_fp { \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {6}} - \dim_to_fp:n {\tl_item:Nn \l_@@_tmpe_tl {3}} } \fp_set:Nn \l_@@_tmpc_fp { sqrt( \l_@@_tmpa_fp * \l_@@_tmpa_fp + \l_@@_tmpb_fp * \l_@@_tmpb_fp ) * \l_@@_tmpa_int } \fp_compare:nTF { \l_@@_tmpc_fp > #2 } { \fp_set:Nn \l_@@_tmpc_fp { #2/ \l_@@_tmpc_fp } \tl_if_eq:NNTF \l_@@_tmpb_tl \c_spath_curvetoa_tl { \spath_split_curve:NNVV \l_@@_tmpc_tl \l_@@_tmpd_tl \l_@@_tmpe_tl \l_@@_tmpc_fp } { \spath_split_line:NNVV \l_@@_tmpc_tl \l_@@_tmpd_tl \l_@@_tmpe_tl \l_@@_tmpc_fp } \prg_replicate:nn {2} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \tl_put_left:NV \l_@@_tmpa_tl \l_@@_tmpd_tl } { \tl_put_left:NV \l_@@_tmpa_tl \c_spath_moveto_tl \int_compare:nT { \tl_count:N \l_@@_tmpa_tl > 3 } { \dim_set:Nn \l_@@_tmpa_dim {\fp_to_dim:n {#2 - \l_@@_tmpc_fp } } \spath_shorten_at_start:NV \l_@@_tmpa_tl \l_@@_tmpa_dim } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } { \tl_gset:Nn \g_@@_output_tl {#1} } } \cs_new_protected_nopar:Npn \spath_shorten_at_start:Nnn #1#2#3 { \@@_shorten_at_start:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_shorten_at_start:Nnn {NVV, cnn, cVV, NVn} \cs_new_protected_nopar:Npn \spath_shorten_at_start:Nn #1#2 { \spath_shorten_at_start:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_shorten_at_start:Nn {cn, cV, NV} \cs_new_protected_nopar:Npn \spath_gshorten_at_start:Nnn #1#2#3 { \@@_shorten_at_start:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gshorten_at_start:Nnn {NVV, cnn, cVV, NVn} \cs_new_protected_nopar:Npn \spath_gshorten_at_start:Nn #1#2 { \spath_gshorten_at_start:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gshorten_at_start:Nn {cn, cV, NV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_shorten_at_both_ends:Nnn, % \spath_shorten_at_both_ends:Nn, % \spath_gshorten_at_both_ends:Nnn, % \spath_gshorten_at_both_ends:Nn % } % % This macro shortens a path from the start by a dimension. % % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_shorten_at_both_ends:Nnn #1#2#3 { \spath_shorten_at_start:Nnn #1{#2}{#3} \spath_shorten_at_end:Nnn #1{#2}{#3} } \cs_new_protected_nopar:Npn \spath_shorten_at_both_ends:Nn #1#2 { \spath_shorten_at_start:Nn #1{#2} \spath_shorten_at_end:Nn #1{#2} } \cs_generate_variant:Nn \spath_shorten_at_both_ends:Nn {cn, cV, NV} \cs_new_protected_nopar:Npn \spath_gshorten_at_both_ends:Nnn #1#2#3 { \spath_gshorten_at_start:Nnn #1{#2}{#3} \spath_gshorten_at_end:Nnn #1{#2}{#3} } \cs_new_protected_nopar:Npn \spath_gshorten_at_both_ends:Nn #1#2 { \spath_gshorten_at_start:Nn #1{#2} \spath_gshorten_at_end:Nn #1{#2} } \cs_generate_variant:Nn \spath_gshorten_at_both_ends:Nn {cn, cV, NV} % \end{macrocode} % \end{macro} % % \subsection{Points on a Path} % % \begin{macro}[internal]{ % \spath_point_at:Nnn, % \spath_gpoint_at:Nnn, % } % % Get the location of a point on a path, using the same location specification as the intersection library. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_point_at:nn #1#2 { \group_begin: \int_set:Nn \l_@@_tmpa_int {\fp_to_int:n {floor(#2) + 1}} \fp_set:Nn \l_@@_tmpa_fp {#2 - floor(#2)} \spath_segments_to_seq:Nn \l_@@_tmpa_seq {#1} \int_compare:nTF { \l_@@_tmpa_int < 1 } { \spath_initialpoint:Nn \l_@@_tmpc_tl {#1} } { \int_compare:nTF { \l_@@_tmpa_int > \seq_count:N \l_@@_tmpa_seq } { \spath_finalpoint:Nn \l_@@_tmpc_tl {#1} } { \tl_set:Nx \l_@@_tmpa_tl {\seq_item:Nn \l_@@_tmpa_seq { \l_@@_tmpa_int} } \int_compare:nTF { \tl_count:N \l_@@_tmpa_tl > 3 } { \tl_set:Nx \l_@@_tmpb_tl {\tl_item:Nn \l_@@_tmpa_tl {4}} } { \tl_set:Nx \l_@@_tmpb_tl {\tl_item:Nn \l_@@_tmpa_tl {1}} } \tl_clear:N \l_@@_tmpc_tl \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_rectsize_tl { \fp_compare:nTF { \l_@@_tmpa_fp <= .25 } { \tl_set:Nx \l_@@_tmpc_tl { {\fp_to_dim:n { ( \tl_item:Nn \l_@@_tmpa_tl {2} ) + 4 * \l_@@_tmpa_fp * ( \tl_item:Nn \l_@@_tmpa_tl {5} ) } } {\fp_to_dim:n {\tl_item:Nn \l_@@_tmpa_tl {3} } } } } { \fp_compare:nTF { \l_@@_tmpa_fp <= .5 } { \tl_set:Nx \l_@@_tmpc_tl { {\fp_to_dim:n { ( \tl_item:Nn \l_@@_tmpa_tl {2} ) + ( \tl_item:Nn \l_@@_tmpa_tl {5} ) } } {\fp_to_dim:n { ( \tl_item:Nn \l_@@_tmpa_tl {3} ) + (4 * (\l_@@_tmpa_fp) - 1) * ( \tl_item:Nn \l_@@_tmpa_tl {6} ) } } } } { \fp_compare:nTF { \l_@@_tmpa_fp <= .75 } { \tl_set:Nx \l_@@_tmpc_tl { {\fp_to_dim:n { ( \tl_item:Nn \l_@@_tmpa_tl {2} ) + (3 - 4 * (\l_@@_tmpa_fp)) *( \tl_item:Nn \l_@@_tmpa_tl {5} ) } } {\fp_to_dim:n { ( \tl_item:Nn \l_@@_tmpa_tl {3} ) + ( \tl_item:Nn \l_@@_tmpa_tl {6} ) } } } } { \tl_set:Nx \l_@@_tmpc_tl { {\fp_to_dim:n { ( \tl_item:Nn \l_@@_tmpa_tl {2} ) } } {\fp_to_dim:n { ( \tl_item:Nn \l_@@_tmpa_tl {3} ) + (4 - 4 *(\l_@@_tmpa_fp)) * ( \tl_item:Nn \l_@@_tmpa_tl {6} ) } } } } } } } \c_spath_closepath_tl { \tl_set:Nx \l_@@_tmpc_tl { {\fp_to_dim:n { (1 - \l_@@_tmpa_fp) * ( \tl_item:Nn \l_@@_tmpa_tl {2} ) + \l_@@_tmpa_fp * ( \tl_item:Nn \l_@@_tmpa_tl {5} ) } } {\fp_to_dim:n { (1 - \l_@@_tmpa_fp) * ( \tl_item:Nn \l_@@_tmpa_tl {3} ) + \l_@@_tmpa_fp * ( \tl_item:Nn \l_@@_tmpa_tl {6} ) } } } } \c_spath_curvetoa_tl { \tl_set:Nx \l_@@_tmpc_tl { {\fp_to_dim:n { (1 - \l_@@_tmpa_fp)^3 * \tl_item:Nn \l_@@_tmpa_tl {2} + 3 * (1 - \l_@@_tmpa_fp)^2 * (\l_@@_tmpa_fp) * \tl_item:Nn \l_@@_tmpa_tl {5} + 3 * (1 - \l_@@_tmpa_fp) * (\l_@@_tmpa_fp)^2 * \tl_item:Nn \l_@@_tmpa_tl {8} + (\l_@@_tmpa_fp)^3 * \tl_item:Nn \l_@@_tmpa_tl {11} }} {\fp_to_dim:n { (1 - \l_@@_tmpa_fp)^3 * \tl_item:Nn \l_@@_tmpa_tl {3} + 3 * (1 - \l_@@_tmpa_fp)^2 * (\l_@@_tmpa_fp) * \tl_item:Nn \l_@@_tmpa_tl {6} + 3 * (1 - \l_@@_tmpa_fp) * (\l_@@_tmpa_fp)^2 * \tl_item:Nn \l_@@_tmpa_tl {9} + (\l_@@_tmpa_fp)^3 * \tl_item:Nn \l_@@_tmpa_tl {12} }} } } } } } \tl_gclear:N \g_@@_output_tl \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpc_tl \group_end: } \cs_new_protected_nopar:Npn \spath_point_at:Nnn #1#2#3 { \@@_point_at:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_point_at:Nnn {NVn, NVV, NnV} \cs_new_protected_nopar:Npn \spath_gpoint_at:Nnn #1#2#3 { \@@_point_at:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gpoint_at:Nnn {NVn, NVV, NnV} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \spath_tangent_at:Nnn, % \spath_gtangent_at:Nnn, % } % % Get the tangent at a point on a path, using the same location specification as the intersection library. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_tangent_at:nn #1#2 { \group_begin: \int_set:Nn \l_@@_tmpa_int {\fp_to_int:n {floor(#2) + 1}} \fp_set:Nn \l_@@_tmpa_fp {#2 - floor(#2)} \spath_segments_to_seq:Nn \l_@@_tmpa_seq {#1} \int_compare:nTF { \l_@@_tmpa_int < 1 } { \spath_initialpoint:Nn \l_@@_tmpc_tl {#1} } { \int_compare:nTF { \l_@@_tmpa_int > \seq_count:N \l_@@_tmpa_seq } { \spath_finalpoint:Nn \l_@@_tmpc_tl {#1} } { \tl_set:Nx \l_@@_tmpa_tl {\seq_item:Nn \l_@@_tmpa_seq { \l_@@_tmpa_int} } \int_compare:nTF { \tl_count:N \l_@@_tmpa_tl > 3 } { \tl_set:Nx \l_@@_tmpb_tl {\tl_item:Nn \l_@@_tmpa_tl {4}} } { \tl_set:Nx \l_@@_tmpb_tl {\tl_item:Nn \l_@@_tmpa_tl {1}} } \tl_clear:N \l_@@_tmpc_tl \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_rectsize_tl { \fp_compare:nTF { \l_@@_tmpa_fp <= .25 } { \tl_set:Nx \l_@@_tmpc_tl { {\fp_to_dim:n { \tl_item:Nn \l_@@_tmpa_tl {5} } } {0pt} } } { \fp_compare:nTF { \l_@@_tmpa_fp <= .5 } { \tl_set:Nx \l_@@_tmpc_tl { {0pt} {\fp_to_dim:n { ( \tl_item:Nn \l_@@_tmpa_tl {6} ) } } } } { \fp_compare:nTF { \l_@@_tmpa_fp <= .75 } { \tl_set:Nx \l_@@_tmpc_tl { {\fp_to_dim:n { -( \tl_item:Nn \l_@@_tmpa_tl {5} ) } } {0pt} } } { \tl_set:Nx \l_@@_tmpc_tl { {0pt} {\fp_to_dim:n { - ( \tl_item:Nn \l_@@_tmpa_tl {6} ) } } } } } } } \c_spath_closepath_tl { \tl_set:Nx \l_@@_tmpc_tl { {\fp_to_dim:n { ( \tl_item:Nn \l_@@_tmpa_tl {5} ) - ( \tl_item:Nn \l_@@_tmpa_tl {2} ) } } {\fp_to_dim:n { ( \tl_item:Nn \l_@@_tmpa_tl {6} ) - ( \tl_item:Nn \l_@@_tmpa_tl {3} ) } } } } \c_spath_curvetoa_tl { \tl_set:Nx \l_@@_tmpc_tl { {\fp_to_dim:n { 3*(1 - \l_@@_tmpa_fp)^2 * (\tl_item:Nn \l_@@_tmpa_tl {5} - \tl_item:Nn \l_@@_tmpa_tl {2}) + 6 * (1 - \l_@@_tmpa_fp) * (\l_@@_tmpa_fp) * (\tl_item:Nn \l_@@_tmpa_tl {8} - \tl_item:Nn \l_@@_tmpa_tl {5}) + 3*(\l_@@_tmpa_fp)^2 * (\tl_item:Nn \l_@@_tmpa_tl {11} - \tl_item:Nn \l_@@_tmpa_tl {8}) } } {\fp_to_dim:n { 3*(1 - \l_@@_tmpa_fp)^2 * (\tl_item:Nn \l_@@_tmpa_tl {6} - \tl_item:Nn \l_@@_tmpa_tl {3}) + 6 * (1 - \l_@@_tmpa_fp) * (\l_@@_tmpa_fp) * (\tl_item:Nn \l_@@_tmpa_tl {9} - \tl_item:Nn \l_@@_tmpa_tl {6}) + 3*(\l_@@_tmpa_fp)^2 * (\tl_item:Nn \l_@@_tmpa_tl {12} - \tl_item:Nn \l_@@_tmpa_tl {9}) }} } } } } } \tl_gclear:N \g_@@_output_tl \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpc_tl \group_end: } \cs_new_protected_nopar:Npn \spath_tangent_at:Nnn #1#2#3 { \@@_tangent_at:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_tangent_at:Nnn {NVn, NVV, NnV} \cs_new_protected_nopar:Npn \spath_gtangent_at:Nnn #1#2#3 { \@@_tangent_at:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gtangent_at:Nnn {NVn, NVV, NnV} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \spath_transformation_at:Nnn, % \spath_gtransformation_at:Nnn % } % Gets a transformation that will align to a point on the path with the x-axis along the path. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_transformation_at:nn #1#2 { \group_begin: \tl_clear:N \l_@@_tmpa_tl \@@_tangent_at:nn {#1}{#2} \tl_set_eq:NN \l_@@_tmpb_tl \g_@@_output_tl \fp_set:Nn \l_@@_tmpa_fp { sqrt( (\tl_item:Nn \l_@@_tmpb_tl {1})^2 + (\tl_item:Nn \l_@@_tmpb_tl {2})^2 ) } \fp_compare:nTF {\l_@@_tmpa_fp = 0} { \fp_set:Nn \l_@@_tmpa_fp {1} \fp_set:Nn \l_@@_tmpb_fp {0} } { \fp_set:Nn \l_@@_tmpb_fp { (\tl_item:Nn \l_@@_tmpb_tl {2}) / \l_@@_tmpa_fp } \fp_set:Nn \l_@@_tmpa_fp { (\tl_item:Nn \l_@@_tmpb_tl {1}) / \l_@@_tmpa_fp } } \tl_set:Nx \l_@@_tmpa_tl { { \fp_to_decimal:n { \l_@@_tmpa_fp } } { \fp_to_decimal:n { \l_@@_tmpb_fp } } { \fp_to_decimal:n {- \l_@@_tmpb_fp } } { \fp_to_decimal:n { \l_@@_tmpa_fp } } } \@@_point_at:nn {#1}{#2} \tl_put_right:NV \l_@@_tmpa_tl \g_@@_output_tl \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_new_protected_nopar:Npn \spath_transformation_at:Nnn #1#2#3 { \@@_transformation_at:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_transformation_at:Nnn {NVn, NVV, NnV, NvV} \cs_new_protected_nopar:Npn \spath_gtransformation_at:Nnn #1#2#3 { \@@_transformation_at:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gtransformation_at:Nnn {NVn, NVV, NnV} % \end{macrocode} % \end{macro} % % \subsection{Intersection Routines} % % Note: I'm not consistent with number schemes. % The intersection library is 0-based, but the user interface is 1-based (since if we ``count'' in a \Verb+\foreach+ then it starts at 1). % This should be more consistent. % % \begin{macro}[internal]{ % \spath_intersect:NN, % \spath_intersect:nn, % } % Pass two spaths to pgf's intersection routine. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_intersect:NN #1#2 { \pgfintersectionofpaths% {% \pgfsetpath #1 }{% \pgfsetpath #2 } } \cs_new_protected_nopar:Npn \spath_intersect:nn #1#2 { \tl_set:Nn \l_@@_intersecta_tl {#1} \tl_set:Nn \l_@@_intersectb_tl {#2} \spath_intersect:NN \l_@@_intersecta_tl \l_@@_intersectb_tl } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_split_component_at_intersections:Nnn % } % Split a component where it intersects a path. % Key assumption is that the first path is a single component, so if it is closed then the end joins up to the beginning. % The component is modified but the path is not. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_split_component_at_intersections:nn #1#2 { \group_begin: \tl_clear:N \l_@@_tmpe_tl \seq_clear:N \l_@@_tmpb_seq % Find the intersections of these segments \tl_set:Nn \l_@@_tmpb_tl {#1} \tl_set:Nn \l_@@_tmpc_tl {#2} % Remember if the component is closed \spath_finalaction:NV \l_@@_tmpa_tl \l_@@_tmpb_tl \bool_set:Nn \l_@@_closed_bool { \tl_if_eq_p:NN \l_@@_tmpa_tl \c_spath_closepath_tl || \tl_if_eq_p:NN \l_@@_tmpa_tl \c_spath_rectcorner_tl } % Open it \spath_open:N \l_@@_tmpb_tl \spath_reallength:NV \l_@@_tmpa_int \l_@@_tmpb_tl % Sort intersections along the component \pgfintersectionsortbyfirstpath \spath_intersect:NN \l_@@_tmpb_tl \l_@@_tmpc_tl % If we get intersections \int_compare:nT {\pgfintersectionsolutions > 0} { % Find the times of the intersections on the component \int_step_inline:nnnn {1} {1} {\pgfintersectionsolutions} { \pgfintersectiongetsolutiontimes{##1}{\l_@@_tmph_tl}{\l_@@_tmpi_tl} \seq_put_left:NV \l_@@_tmpb_seq \l_@@_tmph_tl } \seq_get_left:NN \l_@@_tmpb_seq \l_@@_tmpa_tl \fp_compare:nT { \l_@@_tmpa_tl > \l_@@_tmpa_int - .01 } { \bool_set_false:N \l_@@_closed_bool } \seq_get_right:NN \l_@@_tmpb_seq \l_@@_tmpa_tl \fp_compare:nT { \l_@@_tmpa_tl < .01 } { \bool_set_false:N \l_@@_closed_bool } \tl_set:Nn \l_@@_tmpg_tl {-1} \seq_map_inline:Nn \l_@@_tmpb_seq { \tl_set:Nn \l_@@_tmph_tl {##1} \tl_set_eq:NN \l_@@_tmpa_tl \l_@@_tmph_tl \int_compare:nT { \fp_to_int:n {floor( \l_@@_tmph_tl) } = \fp_to_int:n {floor( \l_@@_tmpg_tl) } } { \tl_set:Nx \l_@@_tmph_tl { \fp_eval:n { floor( \l_@@_tmph_tl ) + ( \l_@@_tmph_tl - floor( \l_@@_tmph_tl) ) / ( \l_@@_tmpg_tl - floor( \l_@@_tmpg_tl) ) } } } \tl_set_eq:NN \l_@@_tmpg_tl \l_@@_tmpa_tl \spath_split_at:NNVV \l_@@_tmpd_tl \l_@@_tmpf_tl \l_@@_tmpb_tl \l_@@_tmph_tl \tl_put_left:NV \l_@@_tmpe_tl \l_@@_tmpf_tl \tl_set_eq:NN \l_@@_tmpb_tl \l_@@_tmpd_tl } \tl_put_left:NV \l_@@_tmpe_tl \l_@@_tmpb_tl \spath_remove_empty_components:N \l_@@_tmpe_tl \tl_set_eq:NN \l_@@_tmpb_tl \l_@@_tmpe_tl } \bool_if:NT \l_@@_closed_bool { \spath_join_component:Nn \l_@@_tmpb_tl {1} } \tl_gclear:N \g_@@_output_tl \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpb_tl \group_end: } \cs_new_protected_nopar:Npn \spath_split_component_at_intersections:Nnn #1#2#3 { \@@_split_component_at_intersections:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_component_at_intersections:Nnn {NVn, NVV} \cs_new_protected_nopar:Npn \spath_split_component_at_intersections:Nn #1#2 { \spath_split_component_at_intersections:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_split_component_at_intersections:Nn {cn, cv} \cs_new_protected_nopar:Npn \spath_gsplit_component_at_intersections:Nnn #1#2#3 { \@@_split_component_at_intersections:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_component_at_intersections:Nnn {NVn, NVV} \cs_new_protected_nopar:Npn \spath_gsplit_component_at_intersections:Nn #1#2 { \spath_gsplit_component_at_intersections:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gsplit_component_at_intersections:Nn {cn, cv} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \spath_split_path_at_intersections:Nnn, % \spath_split_path_at_intersections:Nn, % \spath_gsplit_path_at_intersections:Nnn, % \spath_gsplit_path_at_intersections:Nn, % \spath_split_at_intersections:NNnn, % \spath_split_at_intersections:NN, % \spath_gsplit_at_intersections:NNnn, % \spath_gsplit_at_intersections:NN, % } % Split paths at their intersections. % The \Verb+path+ versions only split the first path. % The others split both paths. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_split_path_at_intersections:nn #1#2 { \group_begin: \seq_clear:N \l_@@_tmpa_seq \seq_clear:N \l_@@_tmpb_seq \spath_components_to_seq:Nn \l_@@_tmpa_seq {#1} \seq_map_inline:Nn \l_@@_tmpa_seq { \spath_split_component_at_intersections:Nnn \l_@@_tmpa_tl {##1} {#2} \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpa_tl } \tl_gclear:N \g_@@_output_tl \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpb_seq {} } \group_end: } \cs_new_protected_nopar:Npn \spath_split_path_at_intersections:Nnn #1#2#3 { \@@_split_path_at_intersections:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_path_at_intersections:Nnn {NVn, NVV, cVn, cVV, cvn, cvv} \cs_new_protected_nopar:Npn \spath_split_path_at_intersections:Nn #1#2 { \spath_split_path_at_intersections:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_split_path_at_intersections:Nn {cv, NV} \cs_new_protected_nopar:Npn \spath_gsplit_path_at_intersections:Nnn #1#2#3 { \@@_split_path_at_intersections:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_path_at_intersections:Nnn {NVn, NVV, cVn, cVV, cvn, cvv} \cs_new_protected_nopar:Npn \spath_gsplit_path_at_intersections:Nn #1#2 { \spath_gsplit_path_at_intersections:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gsplit_path_at_intersections:Nn {cv, NV} \cs_new_protected_nopar:Npn \spath_split_at_intersections:NNnn #1#2#3#4 { \@@_split_path_at_intersections:nn {#3}{#4} \tl_set_eq:NN #1 \g_@@_output_tl \@@_split_path_at_intersections:nn {#4}{#3} \tl_set_eq:NN #2 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_at_intersections:NNnn {NNVn, NNVV, ccVn, ccVV, ccvn, ccvv} \cs_new_protected_nopar:Npn \spath_split_at_intersections:NN #1#2 { \spath_split_at_intersections:NNVV #1#2#1#2 } \cs_generate_variant:Nn \spath_split_at_intersections:NN {cc} \cs_new_protected_nopar:Npn \spath_gsplit_at_intersections:NNnn #1#2#3#4 { \@@_split_path_at_intersections:nn {#3}{#4} \tl_gset_eq:NN #1 \g_@@_output_tl \@@_split_path_at_intersections:nn {#4}{#3} \tl_gset_eq:NN #2 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_at_intersections:NNnn {NNVn, NNVV, ccVn, ccVV, ccvn, ccvv} \cs_new_protected_nopar:Npn \spath_gsplit_at_intersections:NN #1#2 { \spath_gsplit_at_intersections:NNVV #1#2#1#2 } \cs_generate_variant:Nn \spath_gsplit_at_intersections:NN {cc} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \spath_split_component_at_self_intersections:Nn, % \spath_split_component_at_self_intersections:N, % \spath_gsplit_component_at_self_intersections:Nn, % \spath_gsplit_component_at_self_intersections:N, % } % Given a component of a path, split it at points where it self-intersects. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_split_component_at_self_intersections:n #1 { \group_begin: \tl_set:Nn \l_@@_tmpe_tl {#1} % Remember if the component is closed \spath_finalaction:NV \l_@@_tmpa_tl \l_@@_tmpe_tl \bool_set:Nn \l_@@_closed_bool { \tl_if_eq_p:NN \l_@@_tmpa_tl \c_spath_closepath_tl } % Copy the path \tl_set:Nn \l_@@_tmpe_tl {#1} % Open the path \spath_open:N \l_@@_tmpe_tl % Ensure beziers don't self-intersect \spath_split_curves:N \l_@@_tmpe_tl % Make a copy for later \tl_set_eq:NN \l_@@_tmpg_tl \l_@@_tmpe_tl % Clear some token lists and sequences \tl_clear:N \l_@@_tmpd_tl \seq_clear:N \l_@@_tmpb_seq \int_zero:N \l_@@_tmpa_int \pgfintersectionsortbyfirstpath % Split the path into a sequence of segments \spath_segments_to_seq:NV \l_@@_tmpa_seq \l_@@_tmpe_tl \seq_map_indexed_inline:Nn \l_@@_tmpa_seq { \seq_map_indexed_inline:Nn \l_@@_tmpa_seq { % Don't intersect a segment with itself \int_compare:nF { ##1 == ####1 } { \spath_intersect:nn {##2} {####2} \int_compare:nT {\pgfintersectionsolutions > 0} { % Find the times of the intersections on each path \int_step_inline:nnnn {1} {1} {\pgfintersectionsolutions} { \pgfintersectiongetsolutiontimes {########1}{\l_@@_tmpb_tl}{\l_@@_tmpc_tl} \bool_if:nT { !( \fp_compare_p:n { \l_@@_tmpb_tl > .99 } && \int_compare_p:n {##1 + 1 == ####1} ) && !( \fp_compare_p:n { \l_@@_tmpb_tl < .01 } && \int_compare_p:n {##1 - 1 == ####1} ) && !( \l_@@_closed_bool && \fp_compare_p:n { \l_@@_tmpb_tl < .01 } && \int_compare_p:n {##1 == 1} && \int_compare_p:n {\seq_count:N \l_@@_tmpa_seq == ####1} ) && !( \l_@@_closed_bool && \fp_compare_p:n { \l_@@_tmpb_tl > .99 } && \int_compare_p:n {####1 == 1} && \int_compare_p:n {\seq_count:N \l_@@_tmpa_seq == ##1} ) } { \tl_set:Nx \l_@@_tmpa_tl {\fp_to_decimal:n {\l_@@_tmpb_tl + ##1 - 1}} \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpa_tl } } } } } } % Sort the sequence by reverse order along the path \seq_sort:Nn \l_@@_tmpb_seq { \fp_compare:nNnTF { ##1 } < { ##2 } { \sort_return_swapped: } { \sort_return_same: } } \seq_get_left:NN \l_@@_tmpb_seq \l_@@_tmpa_tl \fp_compare:nT { \l_@@_tmpa_tl > \seq_count:N \l_@@_tmpa_seq - .01 } { \bool_set_false:N \l_@@_closed_bool } \seq_get_right:NN \l_@@_tmpb_seq \l_@@_tmpa_tl \fp_compare:nT { \l_@@_tmpa_tl < .01 } { \bool_set_false:N \l_@@_closed_bool } % Restore the original copy of the path \tl_set_eq:NN \l_@@_tmpe_tl \l_@@_tmpg_tl % Clear the token lists \tl_clear:N \l_@@_tmpf_tl \tl_clear:N \l_@@_tmph_tl \tl_clear:N \l_@@_tmpg_tl \tl_set:Nn \l_@@_tmpi_tl {-1} \seq_map_inline:Nn \l_@@_tmpb_seq { \tl_set:Nn \l_@@_tmpb_tl {##1} \tl_set_eq:NN \l_@@_tmpa_tl \l_@@_tmpb_tl \int_compare:nT { \fp_to_int:n {floor( \l_@@_tmpb_tl ) } = \fp_to_int:n {floor( \l_@@_tmpi_tl) } } { \tl_set:Nx \l_@@_tmpb_tl { \fp_eval:n { floor( \l_@@_tmpb_tl ) + ( \l_@@_tmpb_tl - floor( \l_@@_tmpb_tl) ) / ( \l_@@_tmpi_tl - floor( \l_@@_tmpi_tl) ) } } } \tl_set_eq:NN \l_@@_tmpi_tl \l_@@_tmpa_tl \spath_split_at:NNVV \l_@@_tmpf_tl \l_@@_tmph_tl \l_@@_tmpe_tl \l_@@_tmpb_tl \tl_put_left:NV \l_@@_tmpg_tl \l_@@_tmph_tl \tl_set_eq:NN \l_@@_tmpe_tl \l_@@_tmpf_tl } \tl_put_left:NV \l_@@_tmpg_tl \l_@@_tmpe_tl \tl_if_empty:NT \l_@@_tmpg_tl { \tl_set_eq:NN \l_@@_tmpg_tl \l_@@_tmpe_tl } \spath_remove_empty_components:N \l_@@_tmpg_tl % Do something with closed \bool_if:NT \l_@@_closed_bool { \spath_join_component:Nn \l_@@_tmpg_tl {1} } \tl_gclear:N \g_@@_output_tl \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpg_tl \group_end: } \cs_new_protected_nopar:Npn \spath_split_component_at_self_intersections:Nn #1#2 { \@@_split_component_at_self_intersections:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_component_at_self_intersections:Nn {NV} \cs_new_protected_nopar:Npn \spath_split_component_at_self_intersections:N #1 { \spath_split_component_at_self_intersections:NV #1#1 } \cs_generate_variant:Nn \spath_split_component_at_self_intersections:N {c} \cs_new_protected_nopar:Npn \spath_gsplit_component_at_self_intersections:Nn #1#2 { \@@_split_component_at_self_intersections:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_component_at_self_intersections:Nn {NV} \cs_new_protected_nopar:Npn \spath_gsplit_component_at_self_intersections:N #1 { \spath_gsplit_component_at_self_intersections:NV #1#1 } \cs_generate_variant:Nn \spath_gsplit_component_at_self_intersections:N {c} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_split_at_self_intersections:Nn, % \spath_split_at_self_intersections:N, % \spath_gsplit_at_self_intersections:Nn, % \spath_gsplit_at_self_intersections:N, % } % Split a path at its self intersections. % We iterate over the components, splitting each where it meets all the others and itself. % To make this more efficient, we split against the components of the original path rather than updating each time. % % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_split_at_self_intersections:n #1 { \group_begin: \spath_components_to_seq:Nn \l_@@_tmpa_seq {#1} \seq_clear:N \l_@@_tmpb_seq \seq_clear:N \l_@@_tmpc_seq % Iterate over the components of the original path. \bool_do_until:nn { \seq_if_empty_p:N \l_@@_tmpa_seq } { % Get the next component \seq_pop_left:NN \l_@@_tmpa_seq \l_@@_tmpa_tl % Copy for later \tl_set_eq:NN \l_@@_tmpc_tl \l_@@_tmpa_tl \int_compare:nT { \tl_count:N \l_@@_tmpa_tl > 3 } { % Split against itself \spath_split_component_at_self_intersections:N \l_@@_tmpa_tl % Grab the rest of the path \tl_set:Nx \l_@@_tmpb_tl { \seq_use:Nn \l_@@_tmpb_seq {} \seq_use:Nn \l_@@_tmpa_seq {} } % Split against the rest of the path \spath_split_path_at_intersections:NV \l_@@_tmpa_tl \l_@@_tmpb_tl } % Save the split path \seq_put_right:NV \l_@@_tmpc_seq \l_@@_tmpa_tl % Add the original copy to the sequence of processed components \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpc_tl } \tl_gclear:N \g_@@_output_tl \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpc_seq {} } \group_end: } \cs_generate_variant:Nn \@@_split_at_self_intersections:n {V, v} \cs_new_protected_nopar:Npn \spath_split_at_self_intersections:Nn #1#2 { \@@_split_at_self_intersections:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_split_at_self_intersections:Nn {NV, cn, cV, cv} \cs_new_protected_nopar:Npn \spath_split_at_self_intersections:N #1 { \spath_split_at_self_intersections:NV #1#1 } \cs_generate_variant:Nn \spath_split_at_self_intersections:N {c} \cs_new_protected_nopar:Npn \spath_gsplit_at_self_intersections:Nn #1#2 { \@@_split_at_self_intersections:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gsplit_at_self_intersections:Nn {NV, cn, cV, cv} \cs_new_protected_nopar:Npn \spath_gsplit_at_self_intersections:N #1 { \spath_gsplit_at_self_intersections:NV #1#1 } \cs_generate_variant:Nn \spath_gsplit_at_self_intersections:N {c} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_join_component:Nnn, % \spath_join_component:Nn, % \spath_gjoin_component:Nnn, % \spath_gjoin_component:Nn, % } % Join the specified component of the spath to its predecessor. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_join_component:nn #1#2 { \group_begin: \spath_numberofcomponents:Nn \l_@@_tmpa_int {#1} \bool_if:nTF { \int_compare_p:n { #2 >= 1 } && \int_compare_p:n { #2 <= \l_@@_tmpa_int } } { \int_compare:nTF { #2 == 1 } { \int_compare:nTF { \l_@@_tmpa_int == 1 } { \tl_set:Nn \l_@@_tmpa_tl {#1} \spath_initialpoint:Nn \l_@@_tmpb_tl {#1} \tl_put_right:NV \l_@@_tmpa_tl \c_spath_closepath_tl \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpb_tl \tl_gclear:N \g_@@_output_tl \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl } { \spath_components_to_seq:Nn \l_@@_tmpa_seq {#1} \seq_pop_left:NN \l_@@_tmpa_seq \l_@@_tmpa_tl \prg_replicate:nn {3} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \seq_put_right:NV \l_@@_tmpa_seq \l_@@_tmpa_tl \tl_gclear:N \g_@@_output_tl \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpa_seq {}} } } { \spath_components_to_seq:Nn \l_@@_tmpa_seq {#1} \seq_clear:N \l_@@_tmpb_seq \seq_map_indexed_inline:Nn \l_@@_tmpa_seq { \tl_set:Nn \l_@@_tmpa_tl {##2} \int_compare:nT {##1 = #2} { \prg_replicate:nn {3} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } } \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpa_tl } \tl_gclear:N \g_@@_output_tl \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpb_seq {}} } } { \tl_gclear:N \g_@@_output_tl \tl_gset:Nn \g_@@_output_tl {#1} } \group_end: } \cs_new_protected_nopar:Npn \spath_join_component:Nnn #1#2#3 { \@@_join_component:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_join_component:Nnn {NVn, NVV} \cs_new_protected_nopar:Npn \spath_join_component:Nn #1#2 { \spath_join_component:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_join_component:Nn {cn, NV, cV} \cs_new_protected_nopar:Npn \spath_gjoin_component:Nnn #1#2#3 { \@@_join_component:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_gjoin_component:Nnn {NVn, NVV} \cs_new_protected_nopar:Npn \spath_gjoin_component:Nn #1#2 { \spath_gjoin_component:NVn #1#1{#2} } \cs_generate_variant:Nn \spath_gjoin_component:Nn {cn, NV, cV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \spath_spot_weld_components:Nn, % \spath_spot_weld_components:N, % \spath_spot_gweld_components:Nn, % \spath_spot_gweld_components:N, % } % Weld together any components where the last point of one is at the start point of the next (within a tolerance). % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_spot_weld_components:n #1 { \group_begin: \dim_zero:N \l_@@_move_x_dim \dim_zero:N \l_@@_move_y_dim \spath_components_to_seq:Nn \l_@@_tmpa_seq {#1} \seq_clear:N \l_@@_tmpb_seq \dim_set:Nn \l_@@_move_x_dim {\tl_item:nn {#1} {2} + 10 pt} \dim_set:Nn \l_@@_move_y_dim {\tl_item:nn {#1} {3} + 10 pt} \int_set:Nn \l_@@_tmpa_int {\seq_count:N \l_@@_tmpa_seq} \seq_map_inline:Nn \l_@@_tmpa_seq { \tl_set:Nn \l_@@_tmpa_tl {##1} \bool_if:nT { \dim_compare_p:n { \dim_abs:n {\l_@@_move_x_dim - \tl_item:Nn \l_@@_tmpa_tl {2} } < 0.01pt } && \dim_compare_p:n { \dim_abs:n {\l_@@_move_y_dim - \tl_item:Nn \l_@@_tmpa_tl {3} } < 0.01pt } } { \prg_replicate:nn {3} { \tl_set:Nx \l_@@_tmpa_tl {\tl_tail:N \l_@@_tmpa_tl} } \int_decr:N \l_@@_tmpa_int } \tl_reverse:N \l_@@_tmpa_tl \dim_set:Nn \l_@@_move_x_dim {\tl_item:Nn \l_@@_tmpa_tl {2}} \dim_set:Nn \l_@@_move_y_dim {\tl_item:Nn \l_@@_tmpa_tl {1}} \tl_reverse:N \l_@@_tmpa_tl \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpa_tl } \tl_set:Nx \l_@@_tmpa_tl {\seq_use:Nn \l_@@_tmpb_seq {} } \spath_components_to_seq:NV \l_@@_tmpb_seq \l_@@_tmpa_tl \spath_initialpoint:Nn \l_@@_tmpa_tl {#1} \spath_finalpoint:Nn \l_@@_tmpb_tl {#1} \bool_if:nT { \dim_compare_p:n { \dim_abs:n { \tl_item:Nn \l_@@_tmpa_tl {1} - \tl_item:Nn \l_@@_tmpb_tl {1} } < 0.01pt } && \dim_compare_p:n { \dim_abs:n { \tl_item:Nn \l_@@_tmpa_tl {2} - \tl_item:Nn \l_@@_tmpb_tl {2} } < 0.01pt } } { \int_compare:nTF { \seq_count:N \l_@@_tmpb_seq > 1 } { \seq_pop_left:NN \l_@@_tmpb_seq \l_@@_tmpb_tl \prg_replicate:nn {3} { \tl_set:Nx \l_@@_tmpb_tl {\tl_tail:N \l_@@_tmpb_tl} } \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpb_tl } { \tl_set:NV \l_@@_tmpb_tl \c_spath_closepath_tl \tl_put_right:Nx \l_@@_tmpb_tl { { \tl_item:Nn \l_@@_tmpa_tl {1} } { \tl_item:Nn \l_@@_tmpa_tl {2} } } \seq_put_right:NV \l_@@_tmpb_seq \l_@@_tmpb_tl } } \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpb_seq {}} \group_end: } \cs_new_protected_nopar:Npn \spath_spot_weld_components:Nn #1#2 { \@@_spot_weld_components:n {#2} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_spot_weld_components:Nn {NV, cV, cn} \cs_new_protected_nopar:Npn \spath_spot_weld_components:N #1 { \spath_spot_weld_components:NV #1#1 } \cs_generate_variant:Nn \spath_spot_weld_components:N {c} \cs_new_protected_nopar:Npn \spath_spot_gweld_components:Nn #1#2 { \@@_spot_weld_components:n {#2} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \spath_spot_gweld_components:Nn {NV, cV, cn} \cs_new_protected_nopar:Npn \spath_spot_gweld_components:N #1 { \spath_spot_gweld_components:NV #1#1 } \cs_generate_variant:Nn \spath_spot_gweld_components:N {c} % \end{macrocode} % \end{macro} % % \subsection{Exporting Commands} % % \begin{macro}[internal]{ % \spath_convert_to_svg:Nn, % \spath_gconvert_to_svg:Nn, % } % Convert the soft path to an SVG document. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_convert_to_svg:n #1 { \group_begin: \tl_clear:N \l_@@_tmpa_tl \tl_put_right:Nn \l_@@_tmpa_tl { \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} % % \subsection{PGF and TikZ Interface Functions} % % Spaths come from PGF so we need some functions that get and set spaths from the pgf system. % % \begin{macro}[internal]{ % \spath_get_current_path:N, % \spath_gget_current_path:N % } % Grab the current soft path from PGF. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_get_current_path:N #1 { \pgfsyssoftpath@getcurrentpath #1 } \cs_generate_variant:Nn \spath_get_current_path:N {c} % \end{macrocode} % % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_gget_current_path:N #1 { \pgfsyssoftpath@getcurrentpath #1 \tl_gset_eq:NN #1 #1 } \cs_generate_variant:Nn \spath_gget_current_path:N {c} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\spath_protocol_path:n} % This feeds the bounding box of the soft path to PGF to ensure that its current bounding box contains the soft path. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_protocol_path:n #1 { \spath_minbb:Nn \l_@@_tmpa_tl {#1} \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 { \tl_if_empty:nF {#2} { \path[#1] \pgfextra{ \spath_set_current_path:n {#2} \tl_put_left:Nn \tikz@preactions {\def\tikz@actions@path{#2}} }; } } \cs_generate_variant:Nn \spath_tikz_path:nn {Vn, VV, nv, Vv, nV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\spath_set_tikz_data:n} % Sets the \Verb+\tikz@lastx+ and other coordinates from the soft path. % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_set_tikz_data:n #1 { \spath_finalpoint:Nn \l_@@_tmpa_tl {#1} \tl_set:Nx \l_@@_tmpa_tl { \exp_not:c {pgf@x}=\tl_item:Nn \l_@@_tmpa_tl {1} \relax \exp_not:c {pgf@y}=\tl_item:Nn \l_@@_tmpa_tl {2} \relax } \use:c {pgf@process}{% \tl_use:N \l_@@_tmpa_tl \pgftransforminvert \use:c {pgf@pos@transform@glob} } \tl_set:Nx \l_@@_tmpa_tl { \exp_not:c {tikz@lastx}=\exp_not:c {pgf@x} \relax \exp_not:c {tikz@lasty}=\exp_not:c {pgf@y} \relax \exp_not:c {tikz@lastxsaved}=\exp_not:c {pgf@x} \relax \exp_not:c {tikz@lastysaved}=\exp_not:c {pgf@y} \relax } \tl_use:N \l_@@_tmpa_tl \spath_finalmovepoint:Nn \l_@@_tmpa_tl {#1} \bool_if:NT \l_spath_movetorelevant_bool { \ifpgfsyssoftpathmovetorelevant% \tl_gset_eq:cN {pgfsyssoftpath@lastmoveto} \l_@@_tmpa_tl \fi } \tl_set:Nx \l_@@_tmpa_tl { \exp_not:c {pgf@x}=\tl_item:Nn \l_@@_tmpa_tl {1} \relax \exp_not:c {pgf@y}=\tl_item:Nn \l_@@_tmpa_tl {2} \relax } \use:c {pgf@process}{% \tl_use:N \l_@@_tmpa_tl \pgftransforminvert \use:c {pgf@pos@transform@glob} } \bool_if:NT \l_spath_movetorelevant_bool { \dim_if_exist:cT {tikz@lastmovetox} { \tl_set:Nx \l_@@_tmpa_tl { \exp_not:c {tikz@lastmovetox}=\exp_not:c {pgf@x} \relax \exp_not:c {tikz@lastmovetoy}=\exp_not:c {pgf@y} \relax } \tl_use:N \l_@@_tmpa_tl } } \tl_clear_new:c {tikz@timer} \tl_set:cn {tikz@timer} { \pgftransformreset \spath_reallength:Nn \l_@@_tmpa_int {#1} \tl_set_eq:Nc \l_@@_tmpb_tl {tikz@time} \tl_set:Nx \l_@@_tmpb_tl {\fp_to_decimal:n {(\l_@@_tmpb_tl) * (\l_@@_tmpa_int)}} \spath_transformation_at:NnV \l_@@_tmpc_tl {#1} \l_@@_tmpb_tl \tl_set:Nx \l_@@_tmpa_tl { \exp_not:N \pgfpoint { \tl_item:Nn \l_@@_tmpc_tl {5} } { \tl_item:Nn \l_@@_tmpc_tl {6} } } \exp_args:NV \pgftransformshift \l_@@_tmpa_tl \ifpgfresetnontranslationattime \pgftransformresetnontranslations \fi \ifpgfslopedattime \tl_set:Nx \l_@@_tmpa_tl { { \tl_item:Nn \l_@@_tmpc_tl {1} } { \tl_item:Nn \l_@@_tmpc_tl {2} } { \tl_item:Nn \l_@@_tmpc_tl {3} } { \tl_item:Nn \l_@@_tmpc_tl {4} } } \ifpgfallowupsidedownattime \else \fp_compare:nT { \tl_item:Nn \l_@@_tmpc_tl {4} < 0} { \tl_set:Nx \l_@@_tmpa_tl { { \fp_eval:n { - (\tl_item:Nn \l_@@_tmpc_tl {1})} } { \fp_eval:n { - (\tl_item:Nn \l_@@_tmpc_tl {2})} } { \fp_eval:n { - (\tl_item:Nn \l_@@_tmpc_tl {3})} } { \fp_eval:n { - (\tl_item:Nn \l_@@_tmpc_tl {4})} } } } \fi \tl_put_right:Nn \l_@@_tmpa_tl {{\pgfpointorigin}} \exp_last_unbraced:NV \pgftransformcm \l_@@_tmpa_tl \fi } } \cs_generate_variant:Nn \spath_set_tikz_data:n {V, v} % \end{macrocode} % \end{macro} % % \iffalse % % \fi % % \section{The TikZ interface} % % \iffalse %<*tikzspath3> % \fi % % \begin{macrocode} %<@@=tikzspath> % \end{macrocode} % % % This provides an interface to the soft path manipulation routines via a series of TikZ keys. % They all live in the \texttt{spath} family. % % % \begin{macrocode} \RequirePackage{spath3} \RequirePackage{expl3} \ExplSyntaxOn \tl_new:N \l_@@_tmpa_tl \tl_new:N \l_@@_tmpb_tl \tl_new:N \l_@@_tmpc_tl \tl_new:N \l_@@_tmpd_tl \tl_new:N \l_@@_tmpe_tl \tl_new:N \l_@@_tmpf_tl \int_new:N \l_@@_tmpa_int \seq_new:N \l_@@_tmpa_seq \seq_new:N \l_@@_tmpb_seq \seq_new:N \l_@@_tmpc_seq \seq_new:N \l_@@_tmpd_seq \tl_new:N \l_@@_current_tl \tl_new:N \l_@@_reverse_tl \tl_new:N \l_@@_prefix_tl \tl_new:N \l_@@_suffix_tl \tl_new:N \g_@@_smuggle_tl \tl_new:N \g_@@_output_tl \tl_new:N \l_@@_check_tl \clist_new:N \g_@@_output_clist \seq_new:N \g_@@_tmpa_seq \seq_new:N \g_@@_tmpb_seq \seq_new:N \g_@@_output_seq \bool_new:N \l_@@_draft_bool % \end{macrocode} % % We surround all the keys with checks to ensure that the soft path under consideration does actually exist, but if it doesn't we should warn the user. % % \begin{macrocode} \msg_new:nnn { spath3 } { missing soft path } { Soft~ path~ #1~ doesn't~ exist~ \msg_line_context:} \msg_new:nnnn { spath3 } { empty soft path } { Soft~ path~ #1~ is~ empty~ \msg_line_context:} {If~ it~ was~ defined~ inside~ a~ group,~ try~ using~ "save~ global". } \msg_new:nnn { spath3 } { load intersections } { You~ need~ to~ load~ the~ "intersections"~ library~ to~ work~ with~ intersections } % \end{macrocode} % % When saving a soft path, by default we use a naming convention that is compatible with the intersections library so that paths saved here and paths saved by the \texttt{name path} facility of the intersections library are mutually exchangeable. % % \begin{macrocode} \tl_set:Nn \l_@@_prefix_tl {tikz@intersect@path@name@} \tl_set:Nn \l_@@_suffix_tl {} % \end{macrocode} % % When a soft path is grabbed from TikZ we're usually deep in a group so I've adapted the code from the intersections library to dig the definition out of the group without making everything global. % % Interestingly, the intersections library doesn't clear its naming code once it is used meaning that it keeps resetting the definition of a path back to its original one every time a path command is called. % % Also, when the hook is restored outside a scope then no check is made to ensure that the inner one was actually invoked. % This can cause issues when the syntax \Verb!\tikz .. ;! is used since the end of the path coincides with the end of the picture. % \begin{macrocode} \tl_new:N \g_@@_tikzfinish_tl \tl_new:N \l_@@_tikzfinish_outside_tl \cs_new_protected_nopar:Npn \spath_at_end_of_path: { \tl_use:N \g_@@_tikzfinish_tl \tl_gclear:N \g_@@_tikzfinish_tl } \tl_put_right:Nn \tikz@finish {\spath_at_end_of_path:} \tikzset{ every~ scope/.append~ style={ execute~ at~ begin~ scope={ \tl_set_eq:NN \l_@@_tikzfinish_outside_tl \g_@@_tikzfinish_tl }, execute~ at~ end~ scope={ \tl_use:N \g_@@_tikzfinish_tl \tl_gclear:N \g_@@_tikzfinish_tl \tl_gset_eq:NN \g_@@_tikzfinish_tl \l_@@_tikzfinish_outside_tl }, }, } % \end{macrocode} % % This is for delaying something until the path is fully constructed (but no later), sometimes useful to be able to specify this in the path options rather than directly at the end of the path. % % \begin{macrocode} \tl_new:N \l_@@_tikzpath_finish_tl \cs_new_protected_nopar:Npn \@@_at_end_of_path_construction: { \tl_use:N \l_@@_tikzpath_finish_tl \tl_clear:N \l_@@_tikzpath_finish_tl } \tl_put_left:Nn \tikz@finish {\@@_at_end_of_path_construction:} % \end{macrocode} % % Code for saving a path % % \begin{macrocode} \cs_new_protected_nopar:Npn \spath_save_path:Nn #1#2 { \tl_if_empty:NF \g_@@_tikzfinish_tl { \tl_use:N \g_@@_tikzfinish_tl } \tl_gput_right:Nn \g_@@_tikzfinish_tl { \tl_clear_new:N #1 \tl_set:Nn #1 {#2} } } \cs_generate_variant:Nn \spath_save_path:Nn {cn, NV, cV} \cs_new_protected_nopar:Npn \spath_gsave_path:Nn #1#2 { \tl_gput_right:Nn \g_@@_tikzfinish_tl { \tl_gclear_new:N #1 \tl_gset:Nn #1 {#2} } } \cs_generate_variant:Nn \spath_gsave_path:Nn {cn, NV, cV} % \end{macrocode} % % \begin{macro}[internal]{ % \@@_process_tikz_point:Nn % } % Process a point via TikZ and store the resulting dimensions. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_process_tikz_point:Nn #1#2 { \group_begin: \use:c {tikz@scan@one@point} \use:n #2 \scan_stop: \tl_gset:Nx \g_@@_output_tl { { \dim_use:c {pgf@x} } { \dim_use:c {pgf@y} } } \group_end: \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \@@_tikzset:n % } % Wrapper around \Verb+\tikzset+ for expansion. % \begin{macrocode} \cs_set_eq:NN \@@_tikzset:n \tikzset \cs_generate_variant:Nn \@@_tikzset:n {V, v} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \@@_check_path:nnn % \@@_check_two_paths:nnnn % \@@_check_three_paths:nnnnn % } % Given a path name as the second argument, check if it exists and is not empty, and if so reinsert it after the first argument. % The third argument is code to be executed in case of a missing or empty path. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_check_path:nnn #1#2#3 { \tl_set:Nn \l_@@_check_tl {#3} \tl_if_exist:cTF {\@@_path_name:n {#2}} { \tl_if_empty:cTF {\@@_path_name:n {#2}} { \msg_warning:nnn { spath3 } { empty soft path } { #2 } } { \tl_set:Nn \l_@@_check_tl { #1 {\@@_path_name:n {#2}} } } } { \msg_warning:nnn { spath3 } { missing soft path } { #2 } } \tl_use:N \l_@@_check_tl } \cs_new_protected_nopar:Npn \@@_check_two_paths:nnnn #1#2#3#4 { \@@_check_path:nnn { \@@_check_path:nnn {#1}{#2}{#4} }{#3}{#4} } \cs_new_protected_nopar:Npn \@@_check_three_paths:nnnnn #1#2#3#4#5 { \@@_check_path:nnn { \@@_check_path:nnn { \@@_check_path:nnn {#1}{#2}{#5} }{#3}{#5} }{#4}{#5} } \cs_generate_variant:Nn \@@_check_path:nnn {nVn} \cs_generate_variant:Nn \@@_check_two_paths:nnnn {nnVn} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \@@_maybe_current_path:nn % \@@_maybe_current_path_reuse:nnn % \@@_maybe_current_two_paths_reuse_both:nnnn % \@@_maybe_current_two_paths_reuse_first:nnnn % \@@_maybe_current_two_paths_reuse_second:nnnn % } % If the named path is ``current'' then get the current path and use that. % The second version puts the resulting path back as the current path. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_maybe_current_path:nn #1#2 { \tl_if_eq:nnT {#2} {current} { \spath_get_current_path:c {\@@_path_name:n {#2}} } #1 {#2} } \cs_new_protected_nopar:Npn \@@_maybe_current_path_reuse:nnn #1#2#3 { \bool_set_true:N \l_spath_movetorelevant_bool \tl_if_eq:nnT {#2} {current} { \spath_get_current_path:c {\@@_path_name:n {#2}} } #1 {#2} #3 \tl_if_eq:nnT {#2} {current} { \tl_if_empty:cF {\@@_path_name:n {#2}} { \spath_set_current_path:c {\@@_path_name:n {#2}} \spath_set_tikz_data:v {\@@_path_name:n {#2}} } } } \cs_new_protected_nopar:Npn \@@_maybe_current_two_paths_reuse_both:nnnn #1#2#3#4 { \bool_set_true:N \l_spath_movetorelevant_bool \tl_if_eq:nnT {#2} {current} { \spath_get_current_path:c {\@@_path_name:n {#2}} } \tl_if_eq:nnT {#3} {current} { \spath_get_current_path:c {\@@_path_name:n {#3}} } #1 {#2} {#3} #4 \tl_if_eq:nnT {#2} {current} { \tl_if_empty:cF {\@@_path_name:n {#2}} { \spath_set_current_path:c {\@@_path_name:n {#2}} \spath_set_tikz_data:v {\@@_path_name:n {#2}} } } \tl_if_eq:nnT {#3} {current} { \tl_if_empty:cF {\@@_path_name:n {#3}} { \spath_set_current_path:c {\@@_path_name:n {#3}} \spath_set_tikz_data:v {\@@_path_name:n {#3}} } } } \cs_new_protected_nopar:Npn \@@_maybe_current_two_paths_reuse_first:nnnn #1#2#3#4 { \bool_set_true:N \l_spath_movetorelevant_bool \tl_if_eq:nnT {#2} {current} { \spath_get_current_path:c {\@@_path_name:n {#2}} } \tl_if_eq:nnT {#3} {current} { \spath_get_current_path:c {\@@_path_name:n {#3}} } #1 {#2} {#3} #4 \tl_if_eq:nnT {#2} {current} { \tl_if_empty:cF {\@@_path_name:n {#2}} { \spath_set_current_path:c {\@@_path_name:n {#2}} \spath_set_tikz_data:v {\@@_path_name:n {#2}} } } } \cs_new_protected_nopar:Npn \@@_maybe_current_two_paths_reuse_second:nnnn #1#2#3#4 { \bool_set_true:N \l_spath_movetorelevant_bool \tl_if_eq:nnT {#2} {current} { \spath_get_current_path:c {\@@_path_name:n {#2}} } \tl_if_eq:nnT {#3} {current} { \spath_get_current_path:c {\@@_path_name:n {#3}} } #1 {#2} {#3} #4 \tl_if_eq:nnT {#3} {current} { \tl_if_empty:cF {\@@_path_name:n {#3}} { \spath_set_current_path:c {\@@_path_name:n {#3}} \spath_set_tikz_data:v {\@@_path_name:n {#3}} } } } \cs_generate_variant:Nn \@@_maybe_current_path:nn {nV} \cs_generate_variant:Nn \@@_maybe_current_path_reuse:nnn {nVn} % \end{macrocode} % \end{macro} % % % % \begin{macro}{ % \@@_seq_from_foreach:NNn % } % % Convert a PGF foreach list, as the third argument, to a sequence. % The second argument is the maximum number on the list. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_seq_from_foreach:Nnn #1#2#3 { \group_begin: \seq_gclear:N \g_@@_output_seq \tl_if_empty:nTF {#3} { \int_step_inline:nnnn {1}{1} {#2} { \seq_gput_right:Nn \g_@@_output_seq {##1} } } { \foreach \l_@@_tmpa_tl in {#3} { \int_compare:nTF { \l_@@_tmpa_tl > 0 } { \seq_gput_right:NV \g_@@_output_seq \l_@@_tmpa_tl } { \seq_gput_right:Nx \g_@@_output_seq {\int_eval:n {#2 - \l_@@_tmpa_tl}} } } \seq_gsort:Nn \g_@@_output_seq { \int_compare:nNnTF {##1} < {##2} { \sort_return_same: } { \sort_return_swapped: } } } \group_end: \seq_set_eq:NN #1 \g_@@_output_seq \seq_gclear:N \g_@@_output_seq } \cs_generate_variant:Nn \@@_seq_from_foreach:Nnn {NVV, NVn} % % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \@@_path_name:n % } % Wrap the argument in the prefix and suffix to generate the proper name. % \begin{macrocode} \cs_new:Npn \@@_path_name:n #1 { \tl_use:N \l_@@_prefix_tl #1 \tl_use:N \l_@@_suffix_tl } \cs_generate_variant:Nn \@@_path_name:n {V} % \end{macrocode} % \end{macro} % % When joining two paths we provide a set of options for how to process the second path. % % \begin{macrocode} \bool_new:N \l_@@_reverse_bool \bool_new:N \l_@@_weld_bool \bool_new:N \l_@@_move_bool \bool_new:N \l_@@_global_bool \bool_new:N \l_@@_current_transformation_bool \tl_new:N \l_@@_joinpath_tl \tl_new:N \l_@@_transformation_tl \cs_new_protected_nopar:Npn \@@_set_bool:Nn #1#2 { \tl_if_eq:nnTF {#2}{false} { \bool_set_false:N #1 } { \bool_set_true:N #1 } } \tikzset { spath/join/.is~ family, spath/join/.cd, reverse/.code = { \@@_set_bool:Nn \l_@@_reverse_bool {#1} }, reverse/.default = true, weld/.code = { \@@_set_bool:Nn \l_@@_weld_bool {#1} }, weld/.default = true, no~ weld/.code = { \@@_set_bool:Nn \l_@@_weld_bool {#1} \bool_set:Nn \l_@@_weld_bool {! \l_@@_weld_bool} }, no~ weld/.default = true, move/.code = { \@@_set_bool:Nn \l_@@_move_bool {#1} }, move/.default = true, no~ move/.code = { \@@_set_bool:Nn \l_@@_move_bool {#1} \bool_set:Nn \l_@@_move_bool {! \l_@@_move_bool} }, no~ move/.default = true, global/.code = { \@@_set_bool:Nn \l_@@_global_bool {#1} }, global/.default = true, use~ current~ transformation/.code={ \@@_set_bool:Nn \l_@@_current_transformation_bool {#1} }, use~ current~ transformation/.default = true, transform/.store~in=\l_@@_transformation_tl, .unknown/.code = { \tl_set_eq:NN \l_@@_joinpath_tl \pgfkeyscurrentname } } % \end{macrocode} % % % When we split a soft path into components, we make it a comma separated list so that it can be fed into a \Verb+\foreach+ loop. % This can also make it possible to extract a single component, but to do this we need a wrapper around \Verb+\clist_item:Nn+ (there doesn't appear to be a PGF way of getting an item of a CS list). % % \begin{macrocode} \cs_set_eq:NN \getComponentOf \clist_item:Nn % \end{macrocode} % % \subsection{Helper Functions} % % \begin{macro}[internal]{ % \@@_use_path:n % } % Use a path, possibly manipulating it first. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_use_path:n #1 { \tl_set:Nn \l_@@_joinpath_tl {#1} \spath_get_current_path:N \l_@@_current_tl \bool_if:NT \l_@@_reverse_bool { \spath_reverse:N \l_@@_joinpath_tl } \bool_if:NT \l_@@_current_transformation_bool { \pgfgettransform \l_@@_tmpa_tl \spath_transform:NV \l_@@_joinpath_tl \l_@@_tmpa_tl } \tl_if_empty:NF \l_@@_transformation_tl { \group_begin: \pgftransformreset \@@_tikzset:V \l_@@_transformation_tl \pgfgettransform \l_@@_tmpa_tl \tl_gset:Nn \g_@@_smuggle_tl { \spath_transform:Nnnnnnn \l_@@_joinpath_tl } \tl_gput_right:NV \g_@@_smuggle_tl \l_@@_tmpa_tl \group_end: \tl_use:N \g_@@_smuggle_tl } \bool_if:NT \l_@@_move_bool { \tl_if_empty:NTF \l_@@_current_tl { \tl_set:Nn \l_@@_tmpc_tl { {0pt} {0pt} } } { \spath_finalpoint:NV \l_@@_tmpc_tl \l_@@_current_tl } \spath_translate_to:NV \l_@@_joinpath_tl \l_@@_tmpc_tl } \tl_if_empty:NTF \l_@@_current_tl { \tl_if_empty:NTF \l_@@_joinpath_tl { \tl_set_eq:NN \l_@@_current_tl \c_spath_moveto_tl \tl_put_right:Nn \l_@@_current_tl {{0pt}{0pt}} } { \tl_set_eq:NN \l_@@_current_tl \l_@@_joinpath_tl } } { \tl_clear:N \l_@@_tmpa_tl \tl_set:Nn \l_@@_tmpa_tl {spath_} \tl_put_right:Nn \l_@@_tmpa_tl {append} \bool_if:NT \l_@@_weld_bool { \tl_put_right:Nn \l_@@_tmpa_tl {_no_move} \spath_numberofcomponents:NV \l_@@_tmpa_int \l_@@_joinpath_tl \int_compare:nT {\l_@@_tmpa_int == 1} { \bool_set_false:N \l_spath_movetorelevant_bool } } \tl_put_right:Nn \l_@@_tmpa_tl {:NV} \use:c {\tl_use:N \l_@@_tmpa_tl } \l_@@_current_tl \l_@@_joinpath_tl } \spath_set_current_path:N \l_@@_current_tl \spath_set_tikz_data:V \l_@@_joinpath_tl } \cs_generate_variant:Nn \@@_use_path:n {V, v} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \@@_join_with:nn % } % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_join_with:Nn #1#2 { \tl_set:Nn \l_@@_joinpath_tl {#2} \bool_if:NT \l_@@_reverse_bool { \spath_reverse:N \l_@@_joinpath_tl } \tl_if_empty:NF \l_@@_transformation_tl { \group_begin: \pgftransformreset \@@_tikzset:V \l_@@_transformation_tl \pgfgettransform \l_@@_tmpa_tl \tl_gset:Nn \g_@@_smuggle_tl { \spath_transform:Nnnnnnn \l_@@_joinpath_tl } \tl_gput_right:NV \g_@@_smuggle_tl \l_@@_tmpa_tl \group_end: \tl_use:N \g_@@_smuggle_tl } \bool_if:NT \l_@@_move_bool { \spath_finalpoint:NV \l_@@_tmpc_tl #1 \spath_translate_to:NV \l_@@_joinpath_tl \l_@@_tmpc_tl } \tl_clear:N \l_@@_tmpa_tl \tl_set:Nn \l_@@_tmpa_tl {spath_} \bool_if:NT \l_@@_global_bool { \tl_put_right:Nn \l_@@_tmpa_tl {g} } \tl_put_right:Nn \l_@@_tmpa_tl {append} \bool_if:NT \l_@@_weld_bool { \tl_put_right:Nn \l_@@_tmpa_tl {_no_move} } \tl_put_right:Nn \l_@@_tmpa_tl {:NV} \cs_if_exist:cF {\tl_use:N \l_@@_tmpa_tl} { \tl_show:N \l_@@_tmpa_tl } \use:c {\tl_use:N \l_@@_tmpa_tl } #1 \l_@@_joinpath_tl } \cs_generate_variant:Nn \@@_join_with:Nn {cv, cn} % \end{macrocode} % \end{macro} % % \begin{macro}{ % \@@_join_components_with:Nnn % \@@_join_components_upright_with:Nnn % } % Join the specified components of the first path by splicing in the second. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_join_components_with_aux:nnn #1#2#3 { \group_begin: \tl_set:Nn \l_@@_tmpc_tl {#1} \tl_if_empty:nT {#3} { \spath_spot_weld_components:N \l_@@_tmpc_tl } \spath_numberofcomponents:NV \l_@@_tmpa_int \l_@@_tmpc_tl \@@_seq_from_foreach:NVn \l_@@_tmpb_seq \l_@@_tmpa_int {#3} \spath_components_to_seq:NV \l_@@_tmpa_seq \l_@@_tmpc_tl \seq_pop_left:NN \l_@@_tmpa_seq \l_@@_tmpa_tl \seq_pop_left:NN \l_@@_tmpb_seq \l_@@_tmpb_tl \seq_map_indexed_inline:Nn \l_@@_tmpa_seq { \int_compare:nTF { ##1 == \l_@@_tmpb_tl } { \seq_pop_left:NNF \l_@@_tmpb_seq \l_@@_tmpb_tl { \tl_set:Nn \l_@@_tmpb_tl {-1} } \spath_splice_between:Nnn \l_@@_tmpa_tl {#2} {##2} } { \tl_put_right:Nn \l_@@_tmpa_tl {##2} } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_new_protected_nopar:Npn \@@_join_components_with:Nnnn #1#2#3#4 { \@@_join_components_with_aux:nnn {#2}{#3}{#4} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \@@_join_components_with:Nnnn {NVnn} \cs_new_protected_nopar:Npn \@@_join_components_with:Nnn #1#2#3 { \@@_join_components_with:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \@@_join_components_with:Nnn {cvV} \cs_new_protected_nopar:Npn \@@_gjoin_components_with:Nnnn #1#2#3#4 { \@@_join_components_with_aux:nnn {#2}{#3}{#4} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \@@_gjoin_components_with:Nnnn {NVnn} \cs_new_protected_nopar:Npn \@@_gjoin_components_with:Nnn #1#2#3 { \@@_gjoin_components_with:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \@@_gjoin_components_with:Nnn {cvV} \cs_new_protected_nopar:Npn \@@_join_components_upright_with_aux:nnn #1#2#3 { \group_begin: \tl_set:Nn \l_@@_tmpc_tl {#1} \tl_if_empty:nT {#3} { \spath_spot_weld_components:N \l_@@_tmpc_tl } \spath_numberofcomponents:NV \l_@@_tmpa_int \l_@@_tmpc_tl \@@_seq_from_foreach:NVn \l_@@_tmpb_seq \l_@@_tmpa_int {#3} \spath_components_to_seq:NV \l_@@_tmpa_seq \l_@@_tmpc_tl \seq_pop_left:NN \l_@@_tmpa_seq \l_@@_tmpa_tl \seq_pop_left:NN \l_@@_tmpb_seq \l_@@_tmpb_tl \tl_set:Nn \l_@@_tmpc_tl {#2} \spath_transform:NVnnnnnn \l_@@_tmpd_tl \l_@@_tmpc_tl {1}{0}{0}{-1}{0pt}{0pt} \seq_map_indexed_inline:Nn \l_@@_tmpa_seq { \int_compare:nTF { ##1 == \l_@@_tmpb_tl } { \seq_pop_left:NNF \l_@@_tmpb_seq \l_@@_tmpb_tl { \tl_set:Nn \l_@@_tmpb_tl {-1} } \spath_finalpoint:NV \l_@@_tmpe_tl \l_@@_tmpa_tl \spath_initialpoint:Nn \l_@@_tmpf_tl {##2} \dim_compare:nTF { \tl_item:Nn \l_@@_tmpe_tl {1} > \tl_item:Nn \l_@@_tmpf_tl {1} } { \spath_splice_between:NVn \l_@@_tmpa_tl \l_@@_tmpd_tl {##2} } { \spath_splice_between:NVn \l_@@_tmpa_tl \l_@@_tmpc_tl {##2} } } { \tl_put_right:Nn \l_@@_tmpa_tl {##2} } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_new_protected_nopar:Npn \@@_join_components_upright_with:Nnnn #1#2#3#4 { \@@_join_components_upright_with_aux:nnn {#2}{#3}{#4} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \@@_join_components_upright_with:Nnnn {NVnn} \cs_new_protected_nopar:Npn \@@_join_components_upright_with:Nnn #1#2#3 { \@@_join_components_upright_with:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \@@_join_components_upright_with:Nnn {cvV} \cs_new_protected_nopar:Npn \@@_gjoin_components_upright_with:Nnnn #1#2#3#4 { \@@_join_components_upright_with_aux:nnn {#2}{#3}{#4} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \@@_gjoin_components_upright_with:Nnnn {NVnn} \cs_new_protected_nopar:Npn \@@_gjoin_components_upright_with:Nnn #1#2#3 { \@@_gjoin_components_upright_with:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \@@_gjoin_components_upright_with:Nnn {cvV} % \end{macrocode} % \end{macro} % % % \begin{macro}{ % \@@_get_components:Nn % } % Get the components of the named path to the token list. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_get_components_aux:n #1 { \clist_gclear_new:N \g_@@_output_clist \spath_components_to_seq:Nn \l_@@_tmpa_seq {#1} \seq_map_inline:Nn \l_@@_tmpa_seq { \spath_anonymous:N \l_@@_tmpa_tl \tl_new:c {\@@_path_name:V \l_@@_tmpa_tl} \tl_set:cn {\@@_path_name:V \l_@@_tmpa_tl} {##1} \clist_gput_right:NV \g_@@_output_clist \l_@@_tmpa_tl } } \cs_new_protected_nopar:Npn \@@_get_components:Nn #1#2 { \clist_clear_new:N #1 \@@_get_components_aux:n {#2} \clist_set_eq:NN #1 \g_@@_output_clist \clist_gclear:N \g_@@_output_clist } \cs_generate_variant:Nn \@@_get_components:Nn {NV, Nv} \cs_new_protected_nopar:Npn \@@_gget_components:Nn #1#2 { \clist_gclear_new:N #1 \@@_get_components_aux:n {#2} \clist_gset_eq:NN #1 \g_@@_output_clist \clist_gclear:N \g_@@_output_clist } \cs_generate_variant:Nn \@@_gget_components:Nn {NV, Nv} % \end{macrocode} % \end{macro} % % % \begin{macro}{ % \@@_render_components:n % } % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_render_components:nn #1#2 { \group_begin: \spath_components_to_seq:Nn \l_@@_tmpa_seq {#2} \seq_map_indexed_inline:Nn \l_@@_tmpa_seq { \spath_tikz_path:nn { every~ spath~ component/.try, spath ~component~ ##1/.try, spath ~component/.try={##1}, every~ #1~ component/.try, #1 ~component~ ##1/.try, #1 ~component/.try={##1}, } { ##2 } } \group_end: } \cs_generate_variant:Nn \@@_render_components:nn {nv} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \@@_insert_gaps_after_components:nn % } % % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_insert_gaps_after_components_aux:nnn #1#2#3 { \group_begin: \spath_numberofcomponents:Nn \l_@@_tmpa_int {#1} \@@_seq_from_foreach:NVn \l_@@_tmpa_seq \l_@@_tmpa_int {#3} \tl_if_empty:nT {#3} { \seq_pop_right:NN \l_@@_tmpa_seq \l_@@_tmpa_tl } \seq_clear:N \l_@@_tmpb_seq \seq_map_inline:Nn \l_@@_tmpa_seq { \seq_put_right:Nx \l_@@_tmpb_seq {\int_eval:n { \int_mod:nn { ##1 }{ \l_@@_tmpa_int } + 1 } } } \spath_components_to_seq:Nn \l_@@_tmpc_seq {#1} \seq_clear:N \l_@@_tmpd_seq \seq_map_indexed_inline:Nn \l_@@_tmpc_seq { \tl_set:Nn \l_@@_tmpa_tl {##2} \seq_if_in:NnT \l_@@_tmpa_seq {##1} { \spath_shorten_at_end:Nn \l_@@_tmpa_tl {(#2)/2} } \seq_if_in:NnT \l_@@_tmpb_seq {##1} { \spath_shorten_at_start:Nn \l_@@_tmpa_tl {(#2)/2} } \seq_put_right:NV \l_@@_tmpd_seq \l_@@_tmpa_tl } \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpd_seq {} } \group_end: } \cs_new_protected_nopar:Npn \@@_insert_gaps_after_components:Nnnn #1#2#3#4 { \@@_insert_gaps_after_components_aux:nnn {#2}{#3}{#4} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \@@_insert_gaps_after_components:Nnnn {NVnn} \cs_new_protected_nopar:Npn \@@_insert_gaps_after_components:Nnn #1#2#3 { \@@_insert_gaps_after_components:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \@@_insert_gaps_after_components:Nnn {cnn, cVV} \cs_new_protected_nopar:Npn \@@_ginsert_gaps_after_components:Nnnn #1#2#3#4 { \@@_insert_gaps_after_components_aux:nnn {#2}{#3}{#4} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \@@_ginsert_gaps_after_components:Nnnn {NVnn} \cs_new_protected_nopar:Npn \@@_ginsert_gaps_after_components:Nnn #1#2#3 { \@@_ginsert_gaps_after_components:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \@@_ginsert_gaps_after_components:Nnn {cnn, cVV} % \end{macrocode} % \end{macro} % % \begin{macro}{ % \@@_insert_gaps_after_segments:Nn % } % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_insert_gaps_after_segments_aux:nnn #1#2#3 { \group_begin: \spath_reallength:Nn \l_@@_tmpa_int {#1} \@@_seq_from_foreach:NVn \l_@@_tmpa_seq \l_@@_tmpa_int {#3} \tl_if_empty:nT {#3} { \seq_pop_right:NN \l_@@_tmpb_seq \l_@@_tmpa_tl } \seq_clear:N \l_@@_tmpb_seq \seq_map_inline:Nn \l_@@_tmpa_seq { \seq_put_right:Nx \l_@@_tmpb_seq {\int_eval:n { \int_mod:nn { ##1 }{ \l_@@_tmpa_int } + 1 } } } \spath_segments_to_seq:Nn \l_@@_tmpc_seq {#1} \seq_clear:N \l_@@_tmpd_seq \seq_map_indexed_inline:Nn \l_@@_tmpc_seq { \tl_set:Nn \l_@@_tmpa_tl {##2} \seq_if_in:NnT \l_@@_tmpa_seq {##1} { \spath_shorten_at_end:Nn \l_@@_tmpa_tl {(#2)/2} } \seq_if_in:NnT \l_@@_tmpb_seq {##1} { \spath_shorten_at_start:Nn \l_@@_tmpa_tl {(#2)/2} } \seq_put_right:NV \l_@@_tmpd_seq \l_@@_tmpa_tl } \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpd_seq {} } \group_end: } \cs_new_protected_nopar:Npn \@@_insert_gaps_after_segments:Nnnn #1#2#3#4 { \@@_insert_gaps_after_segments_aux:nnn {#2}{#3}{#4} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \@@_insert_gaps_after_segments:Nnnn {NVnn} \cs_new_protected_nopar:Npn \@@_insert_gaps_after_segments:Nnn #1#2#3 { \@@_insert_gaps_after_segments:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \@@_insert_gaps_after_segments:Nnn {cnn, cVV} \cs_new_protected_nopar:Npn \@@_ginsert_gaps_after_segments:Nnnn #1#2#3#4 { \@@_insert_gaps_after_segments_aux:nnn {#2}{#3}{#4} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \@@_ginsert_gaps_after_segments:Nnnn {NVnn} \cs_new_protected_nopar:Npn \@@_ginsert_gaps_after_segments:Nnn #1#2#3 { \@@_ginsert_gaps_after_segments:NVnn #1#1{#2}{#3} } \cs_generate_variant:Nn \@@_ginsert_gaps_after_segments:Nnn {cnn, cVV} % \end{macrocode} % \end{macro} % % \begin{macro}{ % \@@_join_components:Nn % } % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_join_components_aux:nn #1#2 { \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \spath_numberofcomponents:NV \l_@@_tmpa_int \l_@@_tmpa_tl \@@_seq_from_foreach:NVn \l_@@_tmpa_seq \l_@@_tmpa_int {#2} \seq_reverse:N \l_@@_tmpa_seq \seq_map_inline:Nn \l_@@_tmpa_seq { \spath_join_component:Nn \l_@@_tmpa_tl {##1} } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_new_protected_nopar:Npn \@@_join_components:Nnn #1#2#3 { \@@_join_components_aux:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \@@_join_components:Nnn {NVn} \cs_new_protected_nopar:Npn \@@_join_components:Nn #1#2 { \@@_join_components:NVn #1#1{#2} } \cs_generate_variant:Nn \@@_join_components:Nn {cn} \cs_new_protected_nopar:Npn \@@_gjoin_components:Nnn #1#2#3 { \@@_join_components_aux:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \@@_gjoin_components:Nnn {NVn} \cs_new_protected_nopar:Npn \@@_gjoin_components:Nn #1#2 { \@@_gjoin_components:NVn #1#1{#2} } \cs_generate_variant:Nn \@@_gjoin_components:Nn {cn} % \end{macrocode} % \end{macro} % % \begin{macro}{ % \@@_join_components_with_bezier:Nn % } % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_join_components_with_bezier_aux:nn #1#2 { \group_begin: \tl_set:Nn \l_@@_tmpc_tl {#1} \tl_if_empty:nT {#2} { \spath_spot_weld_components:N \l_@@_tmpc_tl } \spath_numberofcomponents:NV \l_@@_tmpa_int \l_@@_tmpc_tl \@@_seq_from_foreach:NVn \l_@@_tmpb_seq \l_@@_tmpa_int {#2} \spath_components_to_seq:NV \l_@@_tmpa_seq \l_@@_tmpc_tl \seq_pop_left:NN \l_@@_tmpa_seq \l_@@_tmpa_tl \seq_pop_left:NN \l_@@_tmpb_seq \l_@@_tmpb_tl \seq_map_indexed_inline:Nn \l_@@_tmpa_seq { \int_compare:nTF { ##1 == \l_@@_tmpb_tl } { \seq_pop_left:NNF \l_@@_tmpb_seq \l_@@_tmpb_tl { \tl_set:Nn \l_@@_tmpb_tl {-1} } \spath_curve_between:Nn \l_@@_tmpa_tl {##2} } { \tl_put_right:Nn \l_@@_tmpa_tl {##2} } } \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpa_tl \group_end: } \cs_new_protected_nopar:Npn \@@_join_components_with_bezier:Nnn #1#2#3 { \@@_join_components_with_bezier_aux:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \@@_join_components_with_bezier:Nnn {NVn} \cs_new_protected_nopar:Npn \@@_join_components_with_bezier:Nn #1#2 { \@@_join_components_with_bezier:NVn #1#1{#2} } \cs_generate_variant:Nn \@@_join_components_with_bezier:Nn {cV} \cs_new_protected_nopar:Npn \@@_gjoin_components_with_bezier:Nnn #1#2#3 { \@@_join_components_with_bezier_aux:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \@@_gjoin_components_with_bezier:Nnn {NVn} \cs_new_protected_nopar:Npn \@@_gjoin_components_with_bezier:Nn #1#2 { \@@_gjoin_components_with_bezier:NVn #1#1{#2} } \cs_generate_variant:Nn \@@_gjoin_components_with_bezier:Nn {cV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{ % \@@_remove_components:nn % } % % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_remove_components_aux:nn #1#2 { \group_begin: \spath_numberofcomponents:Nn \l_@@_tmpa_int {#1} \@@_seq_from_foreach:NVn \l_@@_tmpa_seq \l_@@_tmpa_int {#2} \spath_components_to_seq:Nn \l_@@_tmpb_seq {#1} \seq_pop_left:NNF \l_@@_tmpa_seq \l_@@_tmpa_tl { \tl_clear:N \l_@@_tmpa_tl } \seq_clear:N \l_@@_tmpc_seq \seq_map_indexed_inline:Nn \l_@@_tmpb_seq { \tl_set:Nn \l_@@_tmpb_tl {##1} \tl_if_eq:NNTF \l_@@_tmpb_tl \l_@@_tmpa_tl { \seq_pop_left:NNF \l_@@_tmpa_seq \l_@@_tmpa_tl { \tl_clear:N \l_@@_tmpa_tl } } { \seq_put_right:Nn \l_@@_tmpc_seq {##2} } } \tl_gset:Nx \g_@@_output_tl {\seq_use:Nn \l_@@_tmpc_seq {} } \group_end: } \cs_new_protected_nopar:Npn \@@_remove_components:Nnn #1#2#3 { \@@_remove_components_aux:nn {#2}{#3} \tl_set_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \@@_remove_components:Nnn {NVn} \cs_new_protected_nopar:Npn \@@_remove_components:Nn #1#2 { \@@_remove_components:NVn #1#1{#2} } \cs_generate_variant:Nn \@@_remove_components:Nn {cn} \cs_new_protected_nopar:Npn \@@_gremove_components:Nnn #1#2#3 { \@@_remove_components_aux:nn {#2}{#3} \tl_gset_eq:NN #1 \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \@@_gremove_components:Nnn {NVn} \cs_new_protected_nopar:Npn \@@_gremove_components:Nn #1#2 { \@@_gremove_components:NVn #1#1{#2} } \cs_generate_variant:Nn \@@_gremove_components:Nn {cn} % \end{macrocode} % \end{macro} % % % \begin{macro}[internal]{ % \@@_transform_to:nn, % \@@_transform_upright_to:nn % } % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_transform_to_aux:nn #1#2 { \group_begin: \spath_reallength:Nn \l_@@_tmpa_int {#2} \tl_set:Nx \l_@@_tmpb_tl {\fp_to_decimal:n {(#1) * (\l_@@_tmpa_int)}} \spath_transformation_at:NnV \l_@@_tmpc_tl {#2} \l_@@_tmpb_tl \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpc_tl \group_end: } \cs_new_protected_nopar:Npn \@@_transform_to:nn #1#2 { \@@_transform_to_aux:nn {#1}{#2} \exp_last_unbraced:NV \pgfsettransformentries \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \@@_transform_to:nn {nv} \cs_new_protected_nopar:Npn \@@_transform_upright_to:nn #1#2 { \@@_transform_to_aux:nn {#1}{#2} \fp_compare:nT { \tl_item:Nn \g_@@_output_tl {4} < 0} { \tl_gset:Nx \g_@@_output_tl { { \fp_eval:n { - (\tl_item:Nn \g_@@_output_tl {1})} } { \fp_eval:n { - (\tl_item:Nn \g_@@_output_tl {2})} } { \fp_eval:n { - (\tl_item:Nn \g_@@_output_tl {3})} } { \fp_eval:n { - (\tl_item:Nn \g_@@_output_tl {4})} } { \tl_item:Nn \g_@@_output_tl {5} } { \tl_item:Nn \g_@@_output_tl {6} } } } \exp_last_unbraced:NV \pgfsettransformentries \g_@@_output_tl \tl_gclear:N \g_@@_output_tl } \cs_generate_variant:Nn \@@_transform_upright_to:nn {nv} % \end{macrocode} % \end{macro} % % % % \subsection{Keys} % Now we define all of our keys. % % \begin{macrocode} \tikzset{ % \end{macrocode} % % We're in the \texttt{spath} key family. % % \begin{macrocode} spath/.is~family, spath/.cd, % \end{macrocode} % % We provide for saving soft paths with a specific prefix and suffix in the name. % The default is to make it compatible with the intersections library. % % \begin{macrocode} set~ prefix/.store~ in=\l_@@_prefix_tl, prefix/.is~choice, prefix/default/.style={ /tikz/spath/set~ prefix=tikz@intersect@path@name@ }, set~ suffix/.store~ in=\l_@@_suffix_tl, suffix/.is~choice, suffix/default/.style={ /tikz/spath/set~ suffix={} }, set~ name/.style={ /tikz/spath/prefix=#1, /tikz/spath/suffix=#1 }, % \end{macrocode} % % Hook in to the end of the path construction % % \begin{macrocode} at~ end~ path~ construction/.code={ \tl_put_right:Nn \l_@@_tikzpath_finish_tl {#1} }, % \end{macrocode} % % Keys for saving and cloning a soft path. % % \begin{macrocode} save/.code={ \tikz@addmode{ \spath_get_current_path:N \l_@@_tmpa_tl \spath_bake_round:NV \l_@@_tmpa_tl \l_@@_tmpa_tl \spath_bake_shorten:NV \l_@@_tmpa_tl \l_@@_tmpa_tl \spath_save_path:cV {\@@_path_name:n {#1}} \l_@@_tmpa_tl } }, save~ global/.code={ \tikz@addmode{ \spath_get_current_path:N \l_@@_tmpa_tl \spath_bake_round:NV \l_@@_tmpa_tl \l_@@_tmpa_tl \spath_bake_shorten:NV \l_@@_tmpa_tl \l_@@_tmpa_tl \spath_gsave_path:cV {\@@_path_name:n {#1}} \l_@@_tmpa_tl } }, clone/.code~ 2~ args={ \@@_maybe_current_path:nn { \@@_check_path:nnn { \tl_clear_new:c {\@@_path_name:n {#1}} \tl_set_eq:cc {\@@_path_name:n {#1}} } } {#2}{} }, clone~ global/.code~ 2~ args={ \@@_maybe_current_path:nn { \@@_check_path:nnn { \tl_gclear_new:c {\@@_path_name:n {#1}} \tl_gset_eq:cc {\@@_path_name:n {#1}} } } {#2}{} }, % \end{macrocode} % % Saves a soft path to the aux file. % % \begin{macrocode} save~ to~ aux/.code={ \@@_maybe_current_path:nn { \@@_check_path:nnn { \spath_save_to_aux:c } } {#1} {} }, % \end{macrocode} % % Exports the path as an SVG file. % % \begin{macrocode} export~ to~ svg/.code={ \@@_maybe_current_path:nn { \@@_check_path:nnn { \spath_export_to_svg:nv {#1} } } {#1} {} }, % \end{macrocode} % % Inserts the named path at the current point in the path, with options for how this is accomplished. % The inserted path can be transformed, reversed, moved to the current point, and welded to the current path. % If this is used before the path has been started then it becomes the start of the path (and the ``current point'' is taken as the origin). % % % \begin{macrocode} use/.code={ \bool_set_false:N \l_@@_reverse_bool \bool_set_false:N \l_@@_weld_bool \bool_set_false:N \l_@@_move_bool \bool_set_false:N \l_@@_current_transformation_bool \bool_set_true:N \l_spath_movetorelevant_bool \tl_clear:N \l_@@_joinpath_tl \tl_clear:N \l_@@_transformation_tl \tikzset{ spath/join/.cd, #1 } \@@_check_path:nVn { \@@_use_path:v } \l_@@_joinpath_tl {} }, % \end{macrocode} % % Some aliases for the above. % % \begin{macrocode} restore/.style={/tikz/spath/use={#1}}, restore~ reverse/.style={/tikz/spath/use={reverse, #1}}, append/.style={/tikz/spath/use={move, weld, #1}}, append~ no~ move/.style={/tikz/spath/use={weld, #1}}, append~ reverse/.style={/tikz/spath/use={move, weld, reverse, #1}}, append~ reverse~ no~ move/.style={/tikz/spath/use={weld, reverse, #1}}, insert/.style={/tikz/spath/use={#1}}, insert~ reverse/.style={/tikz/spath/use={reverse, #1}}, % \end{macrocode} % % Diagnostic, show the current path in the terminal and log. % % \begin{macrocode} show~current~path/.code={ \tikz@addmode{ \pgfsyssoftpath@getcurrentpath\l_@@_tmpa_tl \iow_term:n {---~ current~ soft~ path~ ---} \spath_show:V \l_@@_tmpa_tl } }, % \end{macrocode} % % Diagnostic, show the named soft path in the terminal and log. % % \begin{macrocode} show/.code={ \@@_check_path:nnn { \iow_term:n {---~ soft~ path~ #1~ ---} \spath_show:v } {#1} {} }, % \end{macrocode} % % This joins a path on to an existing path, possibly modifying it first. % The possible options are the same as those for \Verb+use+. % It is possible to specify the same path both for the initial and the joining path as a copy is made internally first. % % \begin{macrocode} join~ with/.code~ 2~ args={ \bool_set_false:N \l_@@_reverse_bool \bool_set_false:N \l_@@_weld_bool \bool_set_false:N \l_@@_move_bool \bool_set_false:N \l_@@_global_bool \bool_set_false:N \l_@@_current_transformation_bool \tl_clear:N \l_@@_joinpath_tl \tl_clear:N \l_@@_transformation_tl \tikzset{ spath/join/.cd, #2 } \@@_maybe_current_path_reuse:nnn { \@@_check_two_paths:nnVn { \@@_join_with:cv } } {#1} { \l_@@_joinpath_tl {} } }, % \end{macrocode} % % Does a ``spot weld'' on a soft path, which means that any components that start where the previous component ends are welded together. % % \begin{macrocode} spot~ weld/.code={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_spot_weld_components:c } } {#1} { {} } }, spot~ weld~ globally/.code={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_spot_gweld_components:c } } {#1} { {} } }, % \end{macrocode} % % Reverses the named path. % % \begin{macrocode} reverse/.code={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_reverse:c } } {#1} { {} } }, reverse~ globally/.code={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_greverse:c } } {#1} { {} } }, % \end{macrocode} % % Adjust a path to span between two points. % % \begin{macrocode} span/.code ~n~ args={3}{ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \@@_process_tikz_point:Nn \l_@@_tmpa_tl {#2} \@@_process_tikz_point:Nn \l_@@_tmpb_tl {#3} \spath_span:cVV } } {#1} { {} \l_@@_tmpa_tl \l_@@_tmpb_tl } }, span~ global/.code ~n~ args={3}{ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \@@_process_tikz_point:Nn \l_@@_tmpa_tl {#2} \@@_process_tikz_point:Nn \l_@@_tmpb_tl {#3} \spath_span:cVV } } {#1} { {} \l_@@_tmpa_tl \l_@@_tmpb_tl } }, % \end{macrocode} % % Defines a to path % % \begin{macrocode} to/.style={ to~path={ [ spath/span={#1}{(\tikztostart)}{(\tikztotarget)}, spath/use={#1,weld}, ] \tikztonodes } }, % \end{macrocode} % % % Splice three paths together, transforming the middle one so that it exactly fits between the first and third. % % \begin{macrocode} splice/.code ~n~ args={3}{ \@@_maybe_current_path_reuse:nnn { \@@_check_three_paths:nnnnn { \spath_splice_between:cvv } } {#1} { {#2} {#3} {} } }, splice~ global/.code ~n~ args={3}{ \@@_maybe_current_path_reuse:nnn { \@@_check_three_paths:nnnnn { \spath_gsplice_between:cvv } } {#1} { {#2} {#3} {} } }, % \end{macrocode} % % Join the components of a path by splicing in the second path whenever the components are sufficiently far apart. % The third argument is a list of components to splice after, if it is empty then all components are used and a spot weld is done first so that the splicing only happens if there is an actual gap. % % The \Verb+upright+ versions will join with the reflection of the splice path if it detects that the gap is ``upside-down''. % % % \begin{macrocode} join~ components~ with/.code~2~args={ \tl_if_head_is_group:nTF {#2} { \tl_set:Nx \l_@@_tmpc_tl { \tl_item:nn {#2} {1} } \tl_set:Nx \l_@@_tmpd_tl { \tl_item:nn {#2} {2} } } { \tl_set:Nn \l_@@_tmpc_tl {#2} \tl_clear:N \l_@@_tmpd_tl } \@@_maybe_current_path_reuse:nnn { \@@_check_two_paths:nnVn { \@@_join_components_with:cvV } } {#1} { \l_@@_tmpc_tl {} \l_@@_tmpd_tl } }, join~ components~ globally~ with/.code~2~args={ \tl_if_head_is_group:nTF {#2} { \tl_set:Nx \l_@@_tmpc_tl { \tl_item:nn {#2} {1} } \tl_set:Nx \l_@@_tmpd_tl { \tl_item:nn {#2} {2} } } { \tl_set:Nn \l_@@_tmpc_tl {#2} \tl_clear:N \l_@@_tmpd_tl } \@@_maybe_current_path_reuse:nnn { \@@_check_two_paths:nnVn { \@@_gjoin_components_with:cvV } } {#1} { \l_@@_tmpc_tl {} \l_@@_tmpd_tl } }, join~ components~ upright~ with/.code~2~args={ \tl_if_head_is_group:nTF {#2} { \tl_set:Nx \l_@@_tmpc_tl { \tl_item:nn {#2} {1} } \tl_set:Nx \l_@@_tmpd_tl { \tl_item:nn {#2} {2} } } { \tl_set:Nn \l_@@_tmpc_tl {#2} \tl_clear:N \l_@@_tmpd_tl } \@@_maybe_current_path_reuse:nnn { \@@_check_two_paths:nnVn { \@@_join_components_upright_with:cvV } } {#1} { \l_@@_tmpc_tl {} \l_@@_tmpd_tl } }, join~ components~ globally~ upright~ with/.code~2~args={ \tl_if_head_is_group:nTF {#2} { \tl_set:Nx \l_@@_tmpc_tl { \tl_item:nn {#2} {1} } \tl_set:Nx \l_@@_tmpd_tl { \tl_item:nn {#2} {2} } } { \tl_set:Nn \l_@@_tmpc_tl {#2} \tl_clear:N \l_@@_tmpd_tl } \@@_maybe_current_path_reuse:nnn { \@@_check_two_paths:nnVn { \@@_gjoin_components_upright_with:cvV } } {#1} { \l_@@_tmpc_tl {} \l_@@_tmpd_tl } }, join~ components~ with~ bezier/.code={ \tl_if_head_is_group:nTF {#1} { \tl_set:Nx \l_@@_tmpc_tl { \tl_item:nn {#1} {1} } \tl_set:Nx \l_@@_tmpd_tl { \tl_item:nn {#1} {2} } } { \tl_set:Nn \l_@@_tmpc_tl {#1} \tl_clear:N \l_@@_tmpd_tl } \@@_maybe_current_path_reuse:nVn { \@@_check_path:nnn { \@@_join_components_with_bezier:cV } } \l_@@_tmpc_tl { {} \l_@@_tmpd_tl } }, join~ components~ globally~ with~ bezier/.code~2~args={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \@@_gjoin_components_with_bezier:cn } } {#1} { {} {#2} } }, % \end{macrocode} % % Close a path. % % \begin{macrocode} close/.code={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_close:c } } {#1} { {} } }, close~ globally/.code={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_gclose:c } } {#1} { {} } }, % \end{macrocode} % % Open a path. % % \begin{macrocode} open/.code={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_open:c } } {#1} { {} } }, open~ globally/.code={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_gopen:c } } {#1} { {} } }, % \end{macrocode} % % Close a path, ensuring that the end point is exactly where it will close up to. % % \begin{macrocode} adjust~ and~ close/.code={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_adjust_close:c } } {#1} { {} } }, adjust~ and~ close~ globally/.code={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_adjust_gclose:c } } {#1} { {} } }, % \end{macrocode} % % Close a path with another path. % % \begin{macrocode} close~ with/.code~ 2~ args={ \@@_maybe_current_path_reuse:nnn { \@@_check_two_paths:nnnn { \spath_close_with:cv } } {#1} { {#2} {} } }, close~ globally~ with/.code~ 2~ args={ \@@_maybe_current_path_reuse:nnn { \@@_check_two_paths:nnnn { \spath_gclose_with:cv } } {#1} { {#2} {} } }, % \end{macrocode} % % Close a path with a curve. % % \begin{macrocode} close~ with~ curve/.code={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_close_with_curve:c } } {#1} { {} } }, close~ globally~ with~ curve/.code={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_gclose_with_curve:c } } {#1} { {} } }, % \end{macrocode} % % These keys shorten the path by a dimension. % % \begin{macrocode} shorten~ at~ end/.code~ 2~ args={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_shorten_at_end:cn } } {#1} { {} {#2} } }, shorten~ at~ start/.code~ 2~ args ={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_shorten_at_start:cn } } {#1} { {} {#2} } }, shorten~ at~ both~ ends/.code~ 2~ args={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_shorten_at_both_ends:cn } } {#1} { {} {#2} } }, shorten~ globally~ at~ end/.code~ 2~ args={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_gshorten_at_end:cn } } {#1} { {} {#2} } }, shorten~ globally~ at~ start/.code~ 2~ args ={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_gshorten_at_start:cn } } {#1} { {} {#2} } }, shorten~ globally~ at~ both~ ends/.code~ 2~ args={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_gshorten_at_both_ends:cn } } {#1} { {} {#2} } }, % \end{macrocode} % % These keys split a path at a parameter, the \texttt{keep} versions only keep one part of the resultant path. % % \begin{macrocode} split~ at/.code~ 2~ args={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_split_at_normalised:cn } } {#1} { {} {#2} } }, split~ globally~ at/.code~ 2~ args={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_gsplit_at_normalised:cn } } {#1} { {} {#2} } }, split~ at~ into/.code~ n~ args={4}{ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_split_at_normalised:ccvn {\@@_path_name:n {#1}} {\@@_path_name:n {#2}} } } {#3} { {} {#4} } }, split~ globally~ at~ into/.code~ n~ args={4}{ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_gsplit_at_normalised:ccvn {\@@_path_name:n {#1}} {\@@_path_name:n {#2}} } } {#3} { {} {#4} } }, split~ at~ keep~ start/.code~ 2~ args={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_split_at_normalised_keep_start:cn } } {#1} { {} {#2} } }, split~ globally~ at~ keep~ start/.code~ 2~ args={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_gsplit_at_normalised_keep_start:cn } } {#1} { {} {#2} } }, split~ at~ keep~ end/.code~ 2~ args={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_split_at_normalised_keep_end:cn } } {#1} { {} {#2} } }, split~ globally~ at~ keep~ end/.code~ 2~ args={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_gsplit_at_normalised_keep_end:cn } } {#1} { {} {#2} } }, split~ at~ keep~ middle/.style~ n~ args={3}{ /tikz/spath/split~ at~ keep~ start={#1}{#3}, /tikz/spath/split~ at~ keep~ end={#1}{(#2)/(#3)}, }, split~ globally~ at~ keep~ middle/.style~ n~ args={3}{ /tikz/spath/split~ globally~ at~ keep~ start={#1}{#3}, /tikz/spath/split~ globally~ at~ keep~ end={#1}{(#2)/(#3)}, }, % \end{macrocode} % % This translates the named path. % % \begin{macrocode} translate/.code~ n~ args={3}{ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_translate:cnn } } {#1} { {} {#2}{#3} } }, translate~ globally/.code~ n~ args={3}{ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_gtranslate:cnn } } {#1} { {} {#2}{#3} } }, % \end{macrocode} % % This normalises the named path. % % \begin{macrocode} normalise/.code={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_normalise:c } } {#1} { {} } }, normalise~ globally/.code={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_gnormalise:c } } {#1} { {} } }, % \end{macrocode} % % Transforms the named path using TikZ transformation specifications. % % \begin{macrocode} transform/.code~ 2~ args={ \group_begin: \pgftransformreset \tikzset{#2} \pgfgettransform \l_@@_tmpa_tl \tl_gset_eq:NN \g_@@_smuggle_tl \l_@@_tmpa_tl \group_end: \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_transform:cV } } {#1} { {} \g_@@_smuggle_tl } }, transform~globally/.code~ 2~ args={ \group_begin: \pgftransformreset \tikzset{#2} \pgfgettransform \l_@@_tmpa_tl \tl_gset_eq:NN \g_@@_smuggle_tl \l_@@_tmpa_tl \group_end: \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_gtransform:cV } } {#1} { {} \g_@@_smuggle_tl } }, % \end{macrocode} % % Splits first path where it intersects with the second. % % \begin{macrocode} split~ at~ intersections~ with/.code~ 2~ args={ \tl_if_exist:cTF { tikz@library@intersections@loaded } { \@@_maybe_current_two_paths_reuse_first:nnnn { \@@_check_two_paths:nnnn { \spath_split_path_at_intersections:cv } } {#1} {#2} { {} } } { \msg_warning:nn { spath3 } { load intersections } } }, split~ globally~ at~ intersections~ with/.code~ n~ args={2}{ \tl_if_exist:cTF { tikz@library@intersections@loaded } { \@@_maybe_current_two_paths_reuse_first:nnnn { \@@_check_two_paths:nnnn { \spath_gsplit_path_at_intersections:cv } } {#1} {#2} { {} } } { \msg_warning:nn { spath3 } { load intersections } } }, % \end{macrocode} % % Splits two paths at their mutual intersections. % % \begin{macrocode} split~ at~ intersections/.code~ n~ args={2}{ \tl_if_exist:cTF { tikz@library@intersections@loaded } { \@@_maybe_current_two_paths_reuse_both:nnnn { \@@_check_two_paths:nnnn { \spath_split_at_intersections:cc } } {#1} {#2} { {} } } { \msg_warning:nn { spath3 } { load intersections } } }, split~ globally~ at~ intersections/.code~ n~ args={2}{ \tl_if_exist:cTF { tikz@library@intersections@loaded } { \@@_maybe_current_two_paths_reuse_both:nnnn { \@@_check_two_paths:nnnn { \spath_gsplit_at_intersections:cc } } {#1} {#2} { {} } } { \msg_warning:nn { spath3 } { load intersections } } }, % \end{macrocode} % % Splits a path at its self-intersections. % % \begin{macrocode} split~ at~ self~ intersections/.code={ \tl_if_exist:cTF { tikz@library@intersections@loaded } { \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_split_at_self_intersections:c } } {#1} { {} } } { \msg_warning:nn { spath3 } { load intersections } } }, split~ globally~ at~ self~ intersections/.code={ \tl_if_exist:cTF { tikz@library@intersections@loaded } { \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_gsplit_at_self_intersections:c } } {#1} { {} } } { \msg_warning:nn { spath3 } { load intersections } } }, % \end{macrocode} % % Extract the components of a path into a comma separated list (suitable for using in a \Verb+\foreach+ loop). % % \begin{macrocode} get~ components~ of/.code~ 2~ args={ \@@_maybe_current_path:nn { \@@_check_path:nnn { \@@_get_components:Nv #2 } } {#1} {} }, get~ components~ of~ globally/.code~ 2~ args={ \@@_maybe_current_path:nn { \@@_check_path:nnn { \@@_gget_components:Nv #2 } } {#1} {} }, % \end{macrocode} % % Loop through the components of a soft path and render each as a separate TikZ path so that they can be individually styled. % % \begin{macrocode} render~ components/.code={ \@@_maybe_current_path:nn { \@@_check_path:nnn { \@@_render_components:nv {#1} } } {#1} {} }, % \end{macrocode} % % This puts gaps between components of a soft path. % The list of components is passed through a \Verb+\foreach+ loop so can use the shortcut syntax from those loops. % % \begin{macrocode} insert~ gaps~ after~ components/.code~ 2~ args={ \tl_if_head_is_group:nTF {#2} { \tl_set:Nx \l_@@_tmpc_tl { \tl_item:nn {#2} {1} } \tl_set:Nx \l_@@_tmpd_tl { \tl_item:nn {#2} {2} } } { \tl_set:Nn \l_@@_tmpc_tl {#2} \tl_clear:N \l_@@_tmpd_tl } \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \@@_insert_gaps_after_components:cVV } } {#1} { {} \l_@@_tmpc_tl \l_@@_tmpd_tl } }, insert~ gaps~ globally~ after~ components/.code~ 2~ args={ \tl_if_head_is_group:nTF {#2} { \tl_set:Nx \l_@@_tmpc_tl { \tl_item:nn {#2} {1} } \tl_set:Nx \l_@@_tmpd_tl { \tl_item:nn {#2} {2} } } { \tl_set:Nn \l_@@_tmpc_tl {#2} \tl_clear:N \l_@@_tmpd_tl } \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \@@_ginsert_gaps_after_components:cVV } } {#1} { {} \l_@@_tmpc_tl \l_@@_tmpd_tl } }, % \end{macrocode} % % This puts gaps between segments of a soft path. % The list of segments is passed through a \Verb+\foreach+ loop so can use the shortcut syntax from those loops. % % \begin{macrocode} insert~ gaps~ after~ segments/.code~ 2~ args={ \tl_if_head_is_group:nTF {#2} { \tl_set:Nx \l_@@_tmpc_tl { \tl_item:nn {#2} {1} } \tl_set:Nx \l_@@_tmpd_tl { \tl_item:nn {#2} {2} } } { \tl_set:Nn \l_@@_tmpc_tl {#2} \tl_clear:N \l_@@_tmpd_tl } \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \@@_insert_gaps_after_segments:cVV } } {#1} { {} \l_@@_tmpc_tl \l_@@_tmpd_tl } }, insert~ gaps~ globally~ after~ segments/.code~ 2~ args={ \tl_if_head_is_group:nTF {#2} { \tl_set:Nx \l_@@_tmpc_tl { \tl_item:nn {#2} {1} } \tl_set:Nx \l_@@_tmpd_tl { \tl_item:nn {#2} {2} } } { \tl_set:Nn \l_@@_tmpc_tl {#2} \tl_clear:N \l_@@_tmpd_tl } \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \@@_ginsert_gaps_after_segments:cVV } } {#1} { {} \l_@@_tmpc_tl \l_@@_tmpd_tl } }, % \end{macrocode} % % Join the specified components together, joining each to its previous one. % % \begin{macrocode} join~ components/.code~ 2~ args={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \@@_join_components:cn } } {#1} { {} {#2} } }, join~ components~ globally/.code~ 2~ args={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \@@_gjoin_components:cn } } {#1} { {} {#2} } }, % \end{macrocode} % % Remove all components of the path that don't actually draw anything. % % \begin{macrocode} remove~ empty~ components/.code={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_remove_empty_components:c } } {#1} { {} } }, remove~ empty~ components~ globally/.code={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_gremove_empty_components:c } } {#1} { {} } }, % \end{macrocode} % % Replace all line segments by B\'ezier curves. % % \begin{macrocode} replace~ lines/.code={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_replace_lines:c } } {#1} { {} } }, replace~ lines~ globally/.code={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_greplace_lines:c } } {#1} { {} } }, % \end{macrocode} % % % Remove the specified components. % % \begin{macrocode} remove~ components/.code~ 2~ args={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \@@_remove_components:cn } } {#1} { {} {#2} } }, remove~ components~ globally/.code~ 2~ args={ \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \@@_gremove_components:cn } } {#1} { {} {#2} } }, % \end{macrocode} % % This puts a conditional around the \texttt{spot weld} key because when figuring out a knot drawing then we will initially want to render it without the spot weld to keep the number of components constant. % % \begin{macrocode} draft~ mode/.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 { \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_spot_weld_components:c } } {#1} { {} } } }, maybe~ spot~ weld~ globally/.code={ \bool_if:NF \l_@@_draft_bool { \@@_maybe_current_path_reuse:nnn { \@@_check_path:nnn { \spath_spot_gweld_components:c } } {#1} { {} } } }, % \end{macrocode} % % Set the transformation to lie along a path. % % \begin{macrocode} transform~ to/.code~ 2~ args={ \@@_maybe_current_path:nn { \@@_check_path:nnn { \@@_transform_to:nv {#2} } } {#1} { \pgfsettransformentries {1}{0}{0}{1}{0pt}{0pt} } }, % \end{macrocode} % % As above, but with a possible extra \(180^\circ\) rotation if needed to ensure that the new \(y\)--axis points vaguely upwards. % % \begin{macrocode} upright~ transform~ to/.code~ 2~ args={ \@@_maybe_current_path:nn { \@@_check_path:nnn { \@@_transform_upright_to:nv {#2} } } {#1} { \pgfsettransformentries {1}{0}{0}{1}{0pt}{0pt} } }, % \end{macrocode} % % This is a useful set of styles for drawing a knot diagram. % % \begin{macrocode} knot/.style~ n~ args={3}{ /tikz/spath/split~ at~ self~ intersections=#1, /tikz/spath/remove~ empty~ components=#1, /tikz/spath/insert~ gaps~ after~ components={#1}{#2}{#3}, /tikz/spath/maybe~ spot~ weld=#1, /tikz/spath/render~ components=#1 }, global~ knot/.style~ n~ args={3}{ /tikz/spath/split~ globally~ at~ self~ intersections=#1, /tikz/spath/remove~ empty~ components~ globally=#1, /tikz/spath/insert~ gaps~ globally ~after~ components={#1}{#2}{#3}, /tikz/spath/maybe~ spot~ weld~ globally=#1, /tikz/spath/render~ components=#1 }, % \end{macrocode} % % For single argument commands which take a path as their argument, set the default to be \Verb+current+ so that they use the current path. % % \begin{macrocode} show/.default=current, spot~ weld/.default=current, spot~ weld~ globally/.default=current, reverse/.default=current, reverse~ globally/.default=current, close/.default=current, close~ globally/.default=current, open/.default=current, open~ globally/.default=current, adjust~ and~ close/.default=current, adjust~ and~ close~ globally/.default=current, close~ with~ curve/.default=current, close~ globally~ with~ curve/.default=current, remove~ empty~ components/.default=current, remove~ empty~ components~ globally/.default=current, replace~ lines/.default=current, replace~ lines~ globally/.default=current, maybe~ spot~ weld/.default=current, maybe~ spot~ weld~ globally/.default=current, } % \end{macrocode} % % This defines a coordinate system that finds a position on a soft path. % % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_get_point_at:nn #1#2 { \group_begin: \spath_reallength:Nn \l_@@_tmpa_int {#2} \tl_set:Nx \l_@@_tmpb_tl {\fp_to_decimal:n {(#1) * (\l_@@_tmpa_int)}} \spath_point_at:NnV \l_@@_tmpc_tl {#2} \l_@@_tmpb_tl \tl_clear:N \l_@@_tmpd_tl \tl_put_right:Nn \l_@@_tmpd_tl {\pgf@x=} \tl_put_right:Nx \l_@@_tmpd_tl {\tl_item:Nn \l_@@_tmpc_tl {1}} \tl_put_right:Nn \l_@@_tmpd_tl {\relax} \tl_put_right:Nn \l_@@_tmpd_tl {\pgf@y=} \tl_put_right:Nx \l_@@_tmpd_tl {\tl_item:Nn \l_@@_tmpc_tl {2}} \tl_put_right:Nn \l_@@_tmpd_tl {\relax} \tl_gset_eq:NN \g_@@_output_tl \l_@@_tmpd_tl \group_end: } \cs_generate_variant:Nn \@@_get_point_at:nn {VV, Vn, Vv} \tikzdeclarecoordinatesystem{spath}{% \group_begin: \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_trim_spaces:N \l_@@_tmpa_tl \seq_set_split:NnV \l_@@_tmpa_seq {~} \l_@@_tmpa_tl \seq_pop_right:NN \l_@@_tmpa_seq \l_@@_tmpb_tl \tl_set:Nx \l_@@_tmpa_tl { \seq_use:Nn \l_@@_tmpa_seq {~} } \@@_maybe_current_path:nV { \@@_check_path:nnn { \@@_get_point_at:Vv \l_@@_tmpb_tl } } \l_@@_tmpa_tl { \tl_gset_eq:NN \g_@@_output_tl \pgfpointorigin } \group_end: \use:c {pgf@process}{% \tl_use:N \g_@@_output_tl \pgftransforminvert \use:c {pgf@pos@transform@glob} } \tl_gclear:N \g_@@_output_tl } \ExplSyntaxOff % \end{macrocode} % \iffalse % % \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 \bool_new:N \l_@@_debugging_bool \seq_new:N \g_@@_nodes_seq \bool_set_true:N \l_@@_ignore_ends_bool % \end{macrocode} % % Configuration is via TikZ keys and styles. % \begin{macrocode} \tikzset{ spath/prefix/knot/.style={ spath/set~ prefix=knot strand, }, spath/suffix/knot/.style={ spath/set~ suffix={}, }, knot/.code={ \tl_if_eq:nnTF {#1} {none} { \tikz@addmode{\tikz@mode@doublefalse} } { \tikz@addmode{\tikz@mode@doubletrue} \tl_if_eq:nnTF {\pgfkeysnovalue} {#1} { \tikz@addoption{\pgfsetinnerstrokecolor{.}} } { \pgfsetinnerstrokecolor{#1} } \tikz@addoption{ \pgfsetstrokecolor{knotbg} } \tl_set:Nn \tikz@double@setup{ \pgfsetinnerlinewidth{\pgflinewidth} \pgfsetlinewidth{\dim_eval:n {\tl_use:N \l_@@_gap_tl \pgflinewidth}} } } }, knot~ gap/.store~ in=\l_@@_gap_tl, knot~ gap=3, knot~ diagram/.is~family, knot~ diagram/.unknown/.code={ \tl_set_eq:NN \l_@@_tmpa_tl \pgfkeyscurrentname \pgfkeysalso{ /tikz/\l_@@_tmpa_tl=#1 } }, background~ colour/.code={% \colorlet{knotbg}{#1}% }, background~ color/.code={% \colorlet{knotbg}{#1}% }, background~ colour=white, knot~ diagram, name/.store~ in=\l_@@_name_tl, name={knot}, save~ intersections/.is~ choice, save~ intersections/.default=true, save~ intersections/true/.code={ \bool_set_true:N \l_@@_save_bool }, save~ intersections/false/.code={ \bool_set_false:N \l_@@_save_bool }, every~ strand/.style={draw}, ignore~ endpoint~ intersections/.code={ \tl_if_eq:nnTF {#1} {true} { \bool_set_true:N \l_@@_ignore_ends_bool } { \bool_set_false:N \l_@@_ignore_ends_bool } }, ignore~ endpoint~ intersections/.default=true, consider~ self~ intersections/.is~choice, consider~ self~ intersections/true/.code={ \bool_set_true:N \l_@@_self_intersections_bool \bool_set_true:N \l_@@_splits_bool }, consider~ self~ intersections/false/.code={ \bool_set_false:N \l_@@_self_intersections_bool \bool_set_false:N \l_@@_splits_bool }, consider~ self~ intersections/no~ splits/.code={ \bool_set_true:N \l_@@_self_intersections_bool \bool_set_false:N \l_@@_splits_bool }, consider~ self~ intersections/.default={true}, clip~ radius/.code={ \dim_set:Nn \l_@@_clip_bg_radius_dim {#1} \dim_set:Nn \l_@@_clip_draw_radius_dim {#1+2pt} }, clip~ draw~ radius/.code={ \dim_set:Nn \l_@@_clip_draw_radius_dim {#1} }, clip~ background~ radius/.code={ \dim_set:Nn \l_@@_clip_bg_radius_dim {#1} }, clip~ radius=10pt, end~ tolerance/.code={ \dim_set:Nn \l_@@_tolerance_dim {#1} }, end~ tolerance=14pt, clip/.style={ clip }, background~ clip/.style={ clip }, clip~ width/.code={ \tl_set:Nn \l_@@_clip_width_tl {#1} }, clip~ width=3, flip~ crossing/.code={% \tl_clear_new:c {l_@@_crossing_#1} \tl_set:cn {l_@@_crossing_#1} {x} }, ignore~ crossing/.code={% \tl_clear_new:c {l_@@_ignore_crossing_#1} \tl_set:cn {l_@@_ignore_crossing_#1} {x} }, draft~ mode/.is~ choice, draft~ mode/off/.code={% \bool_set_false:N \l_@@_draft_bool \bool_set_false:N \l_@@_super_draft_bool }, draft~ mode/crossings/.code={% \bool_set_true:N \l_@@_draft_bool \bool_set_false:N \l_@@_super_draft_bool }, draft~ mode/strands/.code={% \bool_set_true:N \l_@@_draft_bool \bool_set_true:N \l_@@_super_draft_bool }, debug/.is~ choice, debug/true/.code={ \bool_set_true:N \l_@@_debugging_bool }, debug/false/.code={ \bool_set_false:N \l_@@_debugging_bool }, debug/.default=true, draft/.is~ family, draft, crossing~ label/.style={ overlay, fill=white, fill~ opacity=.5, text~ opacity=1, text=blue, pin~ edge={blue,<-} }, strand~ label/.style={ overlay, circle, draw=purple, fill=white, fill~ opacity=.5, text~ opacity=1, text=purple, inner~ sep=0pt }, } % \end{macrocode} % % \begin{macro}[internal]{\knot_debug:n} % Debugging % \begin{macrocode} \cs_new_nopar:Npn \knot_debug:n #1 { \bool_if:NT \l_@@_debugging_bool { \iow_term:n {===Knot~ debug: #1===} } } \cs_generate_variant:Nn \knot_debug:n {x} % \end{macrocode} % \end{macro} % % Wrapper around \Verb+\tikzset+ for applying keys from a token list, checking for if the given token list exists. % \begin{macrocode} \cs_new_nopar:Npn \knot_apply_style:N #1 { \knot_debug:n {knot~ apply~ style} \tl_if_exist:NT #1 { \exp_args:NV \tikzset #1 } } \cs_generate_variant:Nn \knot_apply_style:N {c} % \end{macrocode} % %\begin{macro}[internal]{\flipcrossings} % The user can specify a comma separated list of crossings to flip. % \begin{macrocode} \NewDocumentCommand \flipcrossings {m} { \tikzset{knot~ diagram/flip~ crossing/.list={#1}}% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\strand} % This is how the user specifies a strand of the knot. % \begin{macrocode} \NewDocumentCommand \strand { O{} } { \int_incr:N \l_@@_strands_int \tl_clear_new:c {l_@@_options_strand \int_use:N \l_@@_strands_int} \tl_set:cn {l_@@_options_strand \int_use:N \l_@@_strands_int} {#1} \path[#1,spath/set~ name=knot,spath/save=\int_use:N \l_@@_strands_int] } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{knot} % This is the wrapper environment that calls the knot generation code. % \begin{macrocode} \NewDocumentEnvironment{knot} { O{} } { \knot_initialise:n {#1} } { \knot_render: } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_initialise:n} % Set up some stuff before loading in the strands. % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_initialise:n #1 { \knot_debug:n {knot~ initialise} \tikzset{knot~ diagram/.cd,every~ knot~ diagram/.try,#1} \int_zero:N \l_@@_strands_int \tl_clear:N \l_@@_redraws_tl \seq_gclear:N \g_@@_nodes_seq } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_render:} % This is the code that starts the work of rendering the knot. % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_render: { \knot_debug:n {knot~ render} % \end{macrocode} % Start a scope and reset the transformation (since all transformations have already been taken into account when defining the strands). % \begin{macrocode} \pgfscope \pgftransformreset % \end{macrocode} % Set the dimension for deciding when to include neighbouring strands % \begin{macrocode} \dim_set:Nn \l_@@_redraw_tolerance_dim {\fp_to_dim:n { sqrt(2) * max(\l_@@_clip_bg_radius_dim, \l_@@_clip_draw_radius_dim) } } % \end{macrocode} % Loop through the strands drawing each one for the first time. % \begin{macrocode} \int_step_function:nnnN {1} {1} {\l_@@_strands_int} \knot_draw_strand:n % \end{macrocode} % In super draft mode we don't do anything else. % \begin{macrocode} \bool_if:NF \l_@@_super_draft_bool { % \end{macrocode} % In draft mode we draw labels at the ends of the strands; this also handles splitting curves to avoid self-intersections of Bezier curves if that's requested. % \begin{macrocode} \int_step_function:nnnN {1} {1} {\l_@@_strands_int} \knot_draw_labels:n % \end{macrocode} % If we're considering self intersections we need to split the strands into filaments. % \begin{macrocode} \bool_if:NTF \l_@@_self_intersections_bool { \knot_split_strands: \int_set_eq:NN \l_@@_tmpa_int \g_@@_filaments_int \tl_set:Nn \l_@@_prefix_tl {filament} } { \int_set_eq:NN \l_@@_tmpa_int \l_@@_strands_int \tl_set:Nn \l_@@_prefix_tl {strand} } % \end{macrocode} % Initialise the intersection count. % \begin{macrocode} \int_gzero:N \g_@@_intersections_int % \end{macrocode} % If in draft mode we label the intersections, otherwise we just stick a coordinate at each one. % \begin{macrocode} \tl_clear:N \l_@@_node_tl \bool_if:NT \l_@@_draft_bool { \tl_set:Nn \l_@@_node_tl { \exp_not:N \node[coordinate, pin={[ node~ contents={\int_use:N \g_@@_intersections_int}, knot~ diagram/draft/crossing~ label, knot~ diagram/draft/crossing~ \int_use:N \g_@@_intersections_int \c_space_tl label/.try ] }] } } % \end{macrocode} % This double loop steps through the pieces (strands or filaments) and computes the intersections and does stuff with those. % \begin{macrocode} \int_step_variable:nnnNn {1} {1} {\l_@@_tmpa_int - 1} \l_@@_tmpa_tl { \int_step_variable:nnnNn {\tl_use:N \l_@@_tmpa_tl + 1} {1} {\l_@@_tmpa_int} \l_@@_tmpb_tl { \knot_intersections:VV \l_@@_tmpa_tl \l_@@_tmpb_tl } } % \end{macrocode} % If any redraws were requested, do them here. % \begin{macrocode} \tl_use:N \l_@@_redraws_tl % \end{macrocode} % Draw the crossing nodes % \begin{macrocode} \seq_use:Nn \g_@@_nodes_seq {} } % \end{macrocode} % Close the scope % \begin{macrocode} \endpgfscope \knot_debug:x {knot~rendered, ~found~\int_use:N \g_@@_intersections_int \c_space_tl~intersections} } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_draw_strand:n} % This renders a strand using the options originally specified. % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_draw_strand:n #1 { \knot_debug:n {knot~ draw~ strand~ #1} \pgfscope \group_begin: \spath_bake_round:c {knot strand #1} \tl_set:Nn \l_@@_tmpa_tl {knot~ diagram/every~ strand/.try,} \tl_put_right:Nv \l_@@_tmpa_tl {l_@@_options_strand #1} \tl_put_right:Nn \l_@@_tmpa_tl { , knot~ diagram/only~ when~ rendering/.try, only~ when~ rendering/.try, } \spath_tikz_path:Vv \l_@@_tmpa_tl {knot strand #1} \group_end: \endpgfscope } \cs_generate_variant:Nn \tl_put_right:Nn {Nv} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_draw_labels:n} % Draw a label at each end of each strand, if in draft mode. % Also, if requested, split potentially self intersecting Bezier curves. % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_draw_labels:n #1 { \knot_debug:n {knot~ draw~ labels} \bool_if:NT \l_@@_draft_bool { \spath_finalpoint:Nv \l_@@_tmpb_tl {knot strand #1} \dim_set:Nn \l_@@_tmpa_dim {\tl_item:Nn \l_@@_tmpb_tl {1}} \dim_set:Nn \l_@@_tmpb_dim {\tl_item:Nn \l_@@_tmpb_tl {2}} \node[ knot~ diagram/draft/strand~label ] at (\l_@@_tmpa_dim,\l_@@_tmpb_dim) {#1}; \spath_initialpoint:Nv \l_@@_tmpb_tl {knot strand #1} \dim_set:Nn \l_@@_tmpa_dim {\tl_item:Nn \l_@@_tmpb_tl {1}} \dim_set:Nn \l_@@_tmpb_dim {\tl_item:Nn \l_@@_tmpb_tl {2}} \node[ knot~ diagram/draft/strand~label ] at (\l_@@_tmpa_dim,\l_@@_tmpb_dim) {#1}; } \bool_if:nT { \l_@@_self_intersections_bool && \l_@@_splits_bool } { \tl_clear:N \l_@@_tmpa_tl \spath_initialpoint:Nv \l_@@_tmpa_tl {knot strand #1} \tl_put_left:NV \l_@@_tmpa_tl \c_spath_moveto_tl \spath_segments_to_seq:Nv \l_@@_segments_seq {knot strand #1} \seq_map_function:NN \l_@@_segments_seq \knot_split_self_intersects:N \tl_set_eq:cN {knot strand #1} \l_@@_tmpa_tl } } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_split_self_intersects:N} % This is the macro that does the split. % Figuring out whether a Bezier cubic self intersects is apparently a difficult problem so we don't bother. % We compute a point such that if there is an intersection then it lies on either side of the point. % I don't recall where the formula came from! % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_split_self_intersects:N #1 { \knot_debug:n {knot~ split~ self~ intersects} \tl_set:Nx \l_@@_tmpc_tl {\tl_item:nn {#1} {4}} \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} \bool_if:nTF { \fp_compare_p:n {0 < \l_@@_tmpa_fp} && \fp_compare_p:n {\l_@@_tmpa_fp < 1} } { \spath_split_curve:NNnV \l_@@_tmpc_tl \l_@@_tmpd_tl {#1} \l_@@_tmpa_fp \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_set:Nx \l_@@_tmpd_tl {\tl_tail:N \l_@@_tmpd_tl} \tl_set:Nx \l_@@_tmpd_tl {\tl_tail:N \l_@@_tmpd_tl} \tl_set:Nx \l_@@_tmpd_tl {\tl_tail:N \l_@@_tmpd_tl} \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpc_tl \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpd_tl } { \tl_set:Nn \l_@@_tmpc_tl {#1} \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpc_tl } } { \tl_set:Nn \l_@@_tmpc_tl {#1} \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpc_tl } } \c_spath_lineto_tl { \tl_set:Nn \l_@@_tmpc_tl {#1} \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_set:Nx \l_@@_tmpc_tl {\tl_tail:N \l_@@_tmpc_tl} \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpc_tl } } { \tl_put_right:Nn \l_@@_tmpa_tl {#1} } } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_intersections:nn} % This computes the intersections of two pieces and steps through them. % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_intersections:nn #1#2 { \knot_debug:x {knot~ intersections~ between~ \l_@@_prefix_tl \c_space_tl #1~ and~ #2} \group_begin: \tl_set_eq:NN \l_@@_tmpa_tl \l_@@_prefix_tl \tl_put_right:Nn \l_@@_tmpa_tl {#1} \tl_set_eq:NN \l_@@_tmpb_tl \l_@@_prefix_tl \tl_put_right:Nn \l_@@_tmpb_tl {#2} \tl_set_eq:Nc \l_@@_tmpc_tl {knot \tl_use:N \l_@@_tmpa_tl} \tl_set_eq:Nc \l_@@_tmpd_tl {knot \tl_use:N \l_@@_tmpb_tl} \bool_if:nTF { \l_@@_save_bool && \tl_if_exist_p:c { knot~ intersections~ \tl_use:N \l_@@_name_tl - \tl_use:N \l_@@_tmpa_tl - \tl_use:N \l_@@_tmpb_tl } } { \tl_use:c { knot~ intersections~ \tl_use:N \l_@@_name_tl - \tl_use:N \l_@@_tmpa_tl - \tl_use:N \l_@@_tmpb_tl } } { \pgfintersectionofpaths{\pgfsetpath\l_@@_tmpc_tl}{\pgfsetpath\l_@@_tmpd_tl} } \knot_debug:x {found~\pgfintersectionsolutions\c_space_tl~ intersections} \int_compare:nT {\pgfintersectionsolutions > 0} { \int_step_function:nnnN {1} {1} {\pgfintersectionsolutions} \knot_do_intersection:n } \knot_save_intersections:VV \l_@@_tmpa_tl \l_@@_tmpb_tl \group_end: } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_save_intersections:nn} % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_save_intersections:nn #1#2 { \knot_debug:n {knot~ save~ intersections} \bool_if:NT \l_@@_save_bool { \tl_clear:N \l_@@_aux_tl \tl_put_right:Nn \l_@@_aux_tl { \def\pgfintersectionsolutions } \tl_put_right:Nx \l_@@_aux_tl { {\int_eval:n {\pgfintersectionsolutions}} } \int_compare:nT {\pgfintersectionsolutions > 0} { \int_step_inline:nnnn {1} {1} {\pgfintersectionsolutions} { \pgfpointintersectionsolution{##1} \dim_set:Nn \l_@@_tmpa_dim {\pgf@x} \dim_set:Nn \l_@@_tmpb_dim {\pgf@y} \tl_put_right:Nn \l_@@_aux_tl { \expandafter\def\csname pgfpoint@intersect@solution@##1\endcsname } \tl_put_right:Nx \l_@@_aux_tl { { \exp_not:N \pgf@x = \dim_use:N \l_@@_tmpa_dim \exp_not:N \relax \exp_not:N \pgf@y = \dim_use:N \l_@@_tmpb_dim \exp_not:N \relax } } } \tl_set:Nn \l_@@_auxa_tl {\expandafter \gdef \csname knot~ intersections~} \tl_put_right:Nx \l_@@_auxa_tl {\tl_use:N \l_@@_name_tl - #1 - #2} \tl_put_right:Nn \l_@@_auxa_tl {\endcsname} \tl_put_right:Nx \l_@@_auxa_tl {{\tl_to_str:N \l_@@_aux_tl}} \protected@write\@auxout{}{\tl_to_str:N \l_@@_auxa_tl} } } } \cs_generate_variant:Nn \knot_save_intersections:nn {VV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_do_intersection:n} % This handles a specific intersection. % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_do_intersection:n #1 { \knot_debug:n {knot~ do~ intersection~ #1} % \end{macrocode} % Get the intersection coordinates. % \begin{macrocode} \pgfpointintersectionsolution{#1} \dim_set:Nn \l_@@_tmpa_dim {\pgf@x} \dim_set:Nn \l_@@_tmpb_dim {\pgf@y} \knot_debug:x {intersection~at~ (\dim_use:N \l_@@_tmpa_dim,\dim_use:N \l_@@_tmpb_dim)} % \end{macrocode} % If we're dealing with filaments, we can get false positives from the end points. % \begin{macrocode} \bool_set_false:N \l_@@_skip_bool \bool_if:NT \l_@@_self_intersections_bool { % \end{macrocode} % If one filament preceded the other, test for the intersection being at the relevant end point. % \begin{macrocode} \tl_set:Nn \l_@@_tmpc_tl {knot previous} \tl_put_right:NV \l_@@_tmpc_tl \l_@@_tmpa_tl \tl_set:Nv \l_@@_tmpc_tl \l_@@_tmpc_tl \tl_if_eq:NNT \l_@@_tmpc_tl \l_@@_tmpb_tl { \knot_test_endpoint:NVnT \l_@@_tolerance_dim \l_@@_tmpb_tl {final point} { \bool_set_true:N \l_@@_skip_bool } } \tl_set:Nn \l_@@_tmpc_tl {knot previous} \tl_put_right:NV \l_@@_tmpc_tl \l_@@_tmpb_tl \tl_set:Nv \l_@@_tmpc_tl \l_@@_tmpc_tl \tl_if_eq:NNT \l_@@_tmpc_tl \l_@@_tmpa_tl { \knot_test_endpoint:NVnT \l_@@_tolerance_dim \l_@@_tmpa_tl {final point} { \bool_set_true:N \l_@@_skip_bool } } } % \end{macrocode} % The user can also say that end points of filaments (or strands) should simply be ignored anyway. % \begin{macrocode} \bool_if:NT \l_@@_ignore_ends_bool { \knot_test_endpoint:NVnT \l_@@_tolerance_dim \l_@@_tmpa_tl {initial point} { \bool_set_true:N \l_@@_skip_bool } \knot_test_endpoint:NVnT \l_@@_tolerance_dim \l_@@_tmpa_tl {final point} { \bool_set_true:N \l_@@_skip_bool } \knot_test_endpoint:NVnT \l_@@_tolerance_dim \l_@@_tmpb_tl {initial point} { \bool_set_true:N \l_@@_skip_bool } \knot_test_endpoint:NVnT \l_@@_tolerance_dim \l_@@_tmpb_tl {final point} { \bool_set_true:N \l_@@_skip_bool } } % \end{macrocode} % Assuming that we passed all the above tests, we render the crossing. % \begin{macrocode} \bool_if:NF \l_@@_skip_bool { \int_gincr:N \g_@@_intersections_int \knot_debug:x {Processing~intersection~\int_use:N \g_@@_intersections_int} % \end{macrocode} % This is the intersection test. % If the intersection finder finds too many, it might be useful to ignore some. % \begin{macrocode} \bool_if:nF { \tl_if_exist_p:c {l_@@_ignore_crossing_ \int_use:N \g_@@_intersections_int} && ! \tl_if_empty_p:c {l_@@_ignore_crossing_ \int_use:N \g_@@_intersections_int} } { % \end{macrocode} % This is the flip test. % We only render one of the paths. % The ``flip'' swaps which one we render. % \begin{macrocode} \bool_if:nTF { \tl_if_exist_p:c {l_@@_crossing_ \int_use:N \g_@@_intersections_int} && ! \tl_if_empty_p:c {l_@@_crossing_ \int_use:N \g_@@_intersections_int} } { \tl_set_eq:NN \l_@@_tmpg_tl \l_@@_tmpb_tl } { \tl_set_eq:NN \l_@@_tmpg_tl \l_@@_tmpa_tl } % \end{macrocode} % Now we know which one we're rendering, we test to see if we should also render its predecessor or successor to ensure that we render a path through the entire crossing region. % \begin{macrocode} \bool_if:NT \l_@@_self_intersections_bool { \knot_test_endpoint:NVnT \l_@@_redraw_tolerance_dim \l_@@_tmpg_tl {initial point} { \bool_set_true:N \l_@@_prepend_prev_bool } { \bool_set_false:N \l_@@_prepend_prev_bool } \knot_test_endpoint:NVnT \l_@@_redraw_tolerance_dim \l_@@_tmpg_tl {final point} { \bool_set_true:N \l_@@_append_next_bool } { \bool_set_false:N \l_@@_append_next_bool } % \end{macrocode} % If either of those tests succeeded, do the appending or prepending. % \begin{macrocode} \bool_if:nT { \l_@@_prepend_prev_bool || \l_@@_append_next_bool } { \tl_clear_new:c {knot \tl_use:N \l_@@_prefix_tl -1} \tl_set_eq:cc {knot \tl_use:N \l_@@_prefix_tl -1} {knot \tl_use:N \l_@@_tmpg_tl} \tl_clear_new:c {l_@@_options_ \tl_use:N \l_@@_prefix_tl -1} \tl_set_eq:cc {l_@@_options_ \tl_use:N \l_@@_prefix_tl -1} {l_@@_options_ \tl_use:N \l_@@_tmpg_tl} \bool_if:nT { \l_@@_prepend_prev_bool && \tl_if_exist_p:c {knot previous \tl_use:N \l_@@_tmpg_tl} && !\tl_if_empty_p:c {knot previous \tl_use:N \l_@@_tmpg_tl} } { \knot_debug:x {Prepending~ \tl_use:c {knot previous \tl_use:N \l_@@_tmpg_tl}} \spath_prepend_no_move:cv {knot \tl_use:N \l_@@_prefix_tl -1} {knot \tl_use:c {knot previous \tl_use:N \l_@@_tmpg_tl}} % \end{macrocode} % If we split potentially self intersecting curves, we test to see if we should prepend yet another segment. % \begin{macrocode} \bool_if:nT { \l_@@_splits_bool && \tl_if_exist_p:c {knot previous \tl_use:c {knot previous \tl_use:N \l_@@_tmpg_tl} } && !\tl_if_empty_p:c {knot previous \tl_use:c {knot previous \tl_use:N \l_@@_tmpg_tl} } } { \knot_test_endpoint:NvnT \l_@@_redraw_tolerance_dim {knot previous \tl_use:N \l_@@_tmpg_tl} {initial point} { \knot_debug:x {Prepending~ \tl_use:c {knot previous \tl_use:c {knot previous \tl_use:N \l_@@_tmpg_tl} } } \spath_prepend_no_move:cv {knot \tl_use:N \l_@@_prefix_tl -1} {knot \tl_use:c {knot previous \tl_use:c {knot previous \tl_use:N \l_@@_tmpg_tl} } } \tl_set_eq:Nc \l_@@_tmpa_tl {knot \tl_use:N \l_@@_prefix_tl -1} } } } % \end{macrocode} % Now the same for appending. % \begin{macrocode} \bool_if:nT { \l_@@_append_next_bool && \tl_if_exist_p:c {knot next \tl_use:N \l_@@_tmpg_tl} && !\tl_if_empty_p:c {knot next \tl_use:N \l_@@_tmpg_tl} } { \knot_debug:x {Appending~ \tl_use:c {knot next \tl_use:N \l_@@_tmpg_tl}} \spath_append_no_move:cv {knot \tl_use:N \l_@@_prefix_tl -1} {knot \tl_use:c {knot next \tl_use:N \l_@@_tmpg_tl}} \bool_if:nT { \l_@@_splits_bool && \tl_if_exist_p:c {knot next \tl_use:c { knot next \tl_use:N \l_@@_tmpg_tl}} && !\tl_if_empty_p:c {knot next \tl_use:c { knot next \tl_use:N \l_@@_tmpg_tl} } } { \knot_debug:x {Testing~ whether~ to~ append~ {knot next \tl_use:c { knot next \tl_use:N \l_@@_tmpg_tl}} } \knot_test_endpoint:NvnT \l_@@_redraw_tolerance_dim {knot next \tl_use:N \l_@@_tmpg_tl} {final point} { \knot_debug:x {Appending~ {knot next \tl_use:c { knot next \tl_use:N \l_@@_tmpg_tl}} } \spath_append_no_move:cv {knot \tl_use:N \l_@@_prefix_tl -1} {knot \tl_use:c {knot next \tl_use:c {knot next \tl_use:N \l_@@_tmpg_tl} } } } } } \tl_set:Nn \l_@@_tmpg_tl {\tl_use:N \l_@@_prefix_tl -1} } } % \end{macrocode} % Now we render the crossing. % \begin{macrocode} \pgfscope \group_begin: \tikzset{ knot~ diagram/every~ intersection/.try, every~ intersection/.try, knot~ diagram/intersection~ \int_use:N \g_@@_intersections_int/.try } \knot_draw_crossing:VVV \l_@@_tmpg_tl \l_@@_tmpa_dim \l_@@_tmpb_dim \coordinate (\l_@@_name_tl \c_space_tl \int_use:N \g_@@_intersections_int) at (\dim_use:N \l_@@_tmpa_dim, \dim_use:N \l_@@_tmpb_dim); \group_end: \endpgfscope % \end{macrocode} % This ends the boolean as to whether to consider the intersection at all % \begin{macrocode} } % \end{macrocode} % And possibly stick a coordinate with a label at the crossing. % \begin{macrocode} \tl_if_empty:NF \l_@@_node_tl { \seq_gpush:Nx \g_@@_nodes_seq { \l_@@_node_tl at (\dim_use:N \l_@@_tmpa_dim, \dim_use:N \l_@@_tmpb_dim) {}; } } } } \cs_generate_variant:Nn \knot_intersections:nn {VV} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_test_endpoint:N} % Test whether the point is near the intersection point. % \begin{macrocode} \prg_new_conditional:Npnn \knot_test_endpoint:NN #1#2 {p,T,F,TF} { \dim_compare:nTF { \dim_abs:n { \l_@@_tmpa_dim - \tl_item:Nn #2 {1}} + \dim_abs:n { \l_@@_tmpb_dim - \tl_item:Nn #2 {2}} < #1 } { \prg_return_true: } { \prg_return_false: } } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_test_endpoint:nn} % Wrapper around the above. % \begin{macrocode} \prg_new_protected_conditional:Npnn \knot_test_endpoint:Nnn #1#2#3 {T,F,TF} { \use:c {spath_#3:Nv} \l_@@_tmpd_tl {knot #2} \knot_test_endpoint:NNTF #1 \l_@@_tmpd_tl { \prg_return_true: } { \prg_return_false: } } \cs_generate_variant:Nn \knot_test_endpoint:NnnT {NVnT,NvnT} \cs_generate_variant:Nn \knot_test_endpoint:NnnF {NVnF,NvnF} \cs_generate_variant:Nn \knot_test_endpoint:NnnTF {NVnTF,NvnTF} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_draw_crossing:nnn} % This is the code that actually renders a crossing. % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_draw_crossing:nnn #1#2#3 { \knot_debug:n {knot~ draw~ crossing} \group_begin: \pgfscope \path[knot~ diagram/background~ clip] (#2, #3) circle[radius=\l_@@_clip_bg_radius_dim]; \tl_set:Nn \l_@@_tmpa_tl {knot~ diagram/every~ strand/.try,} \tl_if_exist:cT {l_@@_options_ #1} { \tl_put_right:Nv \l_@@_tmpa_tl {l_@@_options_ #1} } \tl_put_right:Nn \l_@@_tmpa_tl { ,knotbg ,line~ width= \tl_use:N \l_@@_clip_width_tl * \pgflinewidth } \spath_tikz_path:Vv \l_@@_tmpa_tl {knot #1} \endpgfscope \pgfscope \path[knot~ diagram/clip] (#2, #3) circle[radius=\l_@@_clip_draw_radius_dim]; \tl_set:Nn \l_@@_tmpa_tl {knot~ diagram/every~ strand/.try,} \tl_if_exist:cT {l_@@_options_ #1} { \tl_put_right:Nv \l_@@_tmpa_tl {l_@@_options_ #1} } \tl_put_right:Nn \l_@@_tmpa_tl { ,knot~ diagram/only~ when~ rendering/.try ,only~ when~ rendering/.try } \spath_tikz_path:Vv \l_@@_tmpa_tl {knot #1} \endpgfscope \group_end: } \cs_generate_variant:Nn \knot_draw_crossing:nnn {nVV, VVV} \cs_new_protected_nopar:Npn \knot_draw_crossing:nn #1#2 { \tikz@scan@one@point\pgfutil@firstofone #2 \relax \knot_draw_crossing:nVV {#1} \pgf@x \pgf@y } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_split_strands:} % This, and the following macros, are for splitting strands into filaments. % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_split_strands: { \knot_debug:n {knot~ split~ strands} \int_gzero:N \g_@@_filaments_int \int_step_function:nnnN {1} {1} {\l_@@_strands_int} \knot_split_strand:n \int_step_function:nnnN {1} {1} {\g_@@_filaments_int} \knot_compute_nexts:n } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_compute_nexts:n} % Each filament needs to know its predecessor and successor. % We work out the predecessors as we go along, this fills in the successors. % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_compute_nexts:n #1 { \knot_debug:n {knot~ compute~ nexts} \tl_clear_new:c {knot next \tl_use:c {knot previous filament #1}} \tl_set:cn {knot next \tl_use:c {knot previous filament #1}} {filament #1} } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_split_strand:n} % Sets up the split for a single strand. % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_split_strand:n #1 { \knot_debug:n {knot~ split~ strand} \int_set_eq:NN \l_@@_component_start_int \g_@@_filaments_int \int_incr:N \l_@@_component_start_int \tl_set_eq:Nc \l_@@_tmpa_tl {l_@@_options_strand #1} \spath_segments_to_seq:Nv \l_@@_segments_seq {knot strand #1} \seq_map_function:NN \l_@@_segments_seq \knot_save_filament:N } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\knot_save_filament:N} % Saves a filament as a new \Verb+spath+ object. % \begin{macrocode} \cs_new_protected_nopar:Npn \knot_save_filament:N #1 { \knot_debug:n {knot~ save~ filament} \tl_set:Nx \l_@@_tmpb_tl {\tl_item:nn {#1} {4}} \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