% \iffalse meta-comment %<*internal> \iffalse % %<*readme> ---------------------------------------------------------------- braids --- a TikZ library for drawing braid diagrams bundle version: v2.3 2024/01/09 E-mail: Andrew Stacey Released under the LaTeX Project Public License v1.3c or later See http://www.latex-project.org/lppl.txt ---------------------------------------------------------------- This library defines some commands for drawing braid diagrams with TikZ/PGF. The initial idea of this package came from a question and answer on the site https://tex.stackexchange.com. The package comes in two versions: a package (braids.sty) and a TikZ library (tikzlibrarybraids.code.tex). The original package (braids.sty) is depreciated and has been frozen at version 1.1. The TikZ library is maintained and should be used in preference, except for legacy code. % %<*internal> \fi \def\nameofplainTeX{plain} \ifx\fmtname\nameofplainTeX\else \expandafter\begingroup \fi % %<*install> \input l3docstrip.tex \keepsilent \askforoverwritefalse \preamble ---------------------------------------------------------------- braids --- a TikZ library for drawing braid diagrams bundle version: v2.3 2024/01/09 E-mail: Andrew Stacey Released under the LaTeX Project Public License v1.3c or later See http://www.latex-project.org/lppl.txt ---------------------------------------------------------------- \endpreamble \postamble Copyright (C) 2011-2024 by Andrew Stacey 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 braids.dtx braids_doc.tex and the derived files README.txt, braids.ins, braids.pdf, braids.sty, tikzlibrarybraids.code.tex, braids_doc.pdf. \endpostamble \usedir{tex/latex/braids} \generate{ \file{braids.sty}{\from{\jobname.dtx}{package}} } \generate{ \file{tikzlibrarybraids.code.tex}{\from{\jobname.dtx}{library}} } % %\endbatchfile %<*internal> \usedir{source/latex/braids} \generate{ \file{\jobname.ins}{\from{\jobname.dtx}{install}} } \nopreamble\nopostamble \usedir{doc/latex/braids} \generate{ \file{README.txt}{\from{\jobname.dtx}{readme}} } \ifx\fmtname\nameofplainTeX \expandafter\endbatchfile \else \expandafter\endgroup \fi % %<*driver> \documentclass{l3doc} \usepackage[T1]{fontenc} \usepackage{lmodern} \usepackage{tikz} \usepackage[nowarning]{braids} \usetikzlibrary{braids} \EnableCrossrefs \CodelineIndex \RecordChanges \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \CheckSum{2401} % % \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}{2011/05/03}{Extended syntax} % \changes{2.0}{2019/03/20}{Converted TikZ library} % \changes{2.1}{2019/08/14}{Extended crossing capability} % \changes{2.2}{2022/10/26}{Advanced positioning} % \changes{2.3}{2024/01/09}{Disambiguated height} % % \DoNotIndex{\newcommand,\newenvironment} % % \GetFileInfo{tikzlibrarybraids.code.tex} % \title{The \textsf{braids} package: codebase} % \author{Andrew Stacey \\ \texttt{loopspace@mathforge.org}} % \date{\fileversion~from \filedate} % % \maketitle % % \section{Introduction} % % This is a package for drawing braid diagrams using PGF/TikZ. % Its inspiration was a question and answer on the website \texttt{http://tex.stackexchange.com}. % % \section{History} % % \begin{itemize} % \item v1.0 First public release. % % \item v1.1 Added ability to configure the gap size, the control points, and the ``nudge''. % Added ability to add labels to strands between crossings. % % \item v2 Reimplemented as TikZ library rather than a standalone package. % \end{itemize} % \StopEventually{} % % \section{Implementation} % % \iffalse %<*package> % \fi % % Issue a notice that this is a depreciated version of the braids package. % \begin{macrocode} \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{braids}[2011/10/18 v1.1 Tikz/PGF commands for drawing braid diagrams (depreciated package)] \newif\if@braids@warning@ \@braids@warning@true \DeclareOption{nowarning}{% \@braids@warning@false } \ProcessOptions \if@braids@warning@ \PackageWarning{braids}{% This package is frozen at v1.1 from 2011/10/18 and has been reimplemented as a TikZ library; if starting with a fresh document, please consider using that instead.% }% \fi % \end{macrocode} % % \begin{macro}[internal]{\ge@addto@macro} % This is an expanded version of \Verb+\g@addto@macro+. % Namely, it adds the \emph{expansion} of the second argument to the first. % \begin{macrocode} \long\def\ge@addto@macro#1#2{% \begingroup \toks@\expandafter\expandafter\expandafter{\expandafter#1#2}% \xdef#1{\the\toks@}% \endgroup} % \end{macrocode} % \end{macro} % % \begin{macro}{\braid} % This is the user command. % We start a group to ensure that all our assignments are local, and then call our initialisation code. % The optional argument is for any keys to be set. % \begin{macrocode} \newcommand{\braid}[1][]{% \begingroup \braid@start{#1}} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@process} % This is the token swallower. % This takes the next token on the braid specification and passes it to the handler command (in the macro \Verb+\braid@token+) which decides what to do next. % (Incidentally, the code here is heavily influenced by TikZ. % That's probably not very surprising.) % \begin{macrocode} \def\braid@process{% \afterassignment\braid@handle\let\braid@token=% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@process@start} % This is a variant of \Verb+\braid@process+ which is used at the start where we might have a few extra bits and pieces before the braid itself starts. % Specifically, we test for the \Verb+at+ and \Verb+(name)+ possibilities. % \begin{macrocode} \def\braid@process@start{% \afterassignment\braid@handle@start\let\braid@token=% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@handle@start} % This is the handler in use at the start. % It looks for the tokens \Verb+a+ or \Verb+(+ which (might) signal the start of an \Verb+at (coordinate)+ or \Verb+(name)+. % If we get anything else (modulo spaces) we decide that we've reached the end of the initialisation stuff and it is time to get started on the braid itself. % \begin{macrocode} \def\braid@handle@start{% \let\braid@next=\braid@handle \ifx\braid@token a % \end{macrocode} % We got an \Verb+a+ so we might have an \Verb+at (coordinate)+ % \begin{macrocode} \let\braid@next=\braid@maybe@locate \else \ifx\braid@token(%) % \end{macrocode} % We got an \Verb+(+ so we have a name % \begin{macrocode} \iffalse)\fi %Indentation hack! \let\braid@next=\braid@assign@name \else \ifx\braid@token\@sptoken % \end{macrocode} % Space; boring, redo from start % \begin{macrocode} \let\braid@next=\braid@process@start \fi \fi \fi \braid@next% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@handle} % This is the main handler for parsing the braid word. % It decides what action to take depending on what the token is. % We have to be a bit careful with catcodes, some packages set % \Verb+;+ and \Verb+|+ to be active. % We should probably also be careful with \Verb+^+ and \Verb+_+. % \begin{macrocode} \let\braid@semicolon=; \let\braid@bar=| \def\braid@handle{% \let\braid@next=\braid@process % \end{macrocode} % Start by checking our catcodes to see what we should check against % \begin{macrocode} \ifnum\the\catcode`\;=\active \expandafter\let\expandafter\braid@semicolon\tikz@activesemicolon \fi \ifnum\the\catcode`\|=\active \expandafter\let\expandafter\braid@bar\tikz@activebar \fi \ifx\braid@token\braid@semicolon % \end{macrocode} % Semicolon, means that we're done reading our braid. % It's time to render it. % \begin{macrocode} \let\braid@next=\braid@render \else \ifx\braid@token^ % \end{macrocode} % Superscript character, the next token tells us whether it's an over-crossing or an under-crossing. % \begin{macrocode} \let\braid@next=\braid@sup \else \ifx\braid@token_ % \end{macrocode} % Subscript character, the next token tells us which strands cross. % \begin{macrocode} \let\braid@next=\braid@sub \else \ifx\braid@token- % \end{macrocode} % Hyphen, this is so that we can have more than one crossing on the same level. % \begin{macrocode} \braid@increase@levelfalse \else \ifx\braid@token1% % \end{macrocode} % 1: this means the ``identity'' crossing, so no crossing here. % Increase the level, unless overriden, and add to the label. % \begin{macrocode} \ifbraid@increase@level \stepcounter{braid@level} \fi \braid@increase@leveltrue \ge@addto@macro\braid@label{\braid@token}% \else \ifx\braid@token[% % \end{macrocode} % Open bracket, this means we have some more options to process. % \begin{macrocode} \let\braid@next=\braid@process@options \else \ifx\braid@token\braid@bar % \end{macrocode} % Bar, this tells us that we want a ``floor'' at this point. % \begin{macrocode} \edef\braid@tmp{,\expandafter\the\value{braid@level}}% \ge@addto@macro\braid@floors\braid@tmp% \else \ifx\braid@token\bgroup % \end{macrocode} % Begin group, which we reinterpret as begining a scope. % \begin{macrocode} \braid@beginscope \else \ifx\braid@token\egroup % \end{macrocode} % End group, which ends the scope % \begin{macrocode} \braid@endscope \else \ifx\braid@token\braid@olabel@strand \let\braid@next=\braid@olabel@strand \else \ifx\braid@token\braid@clabel@strand \let\braid@next=\braid@clabel@strand \else % \end{macrocode} % Otherwise, we add the token to the braid label. % \begin{macrocode} \ge@addto@macro\braid@label{\braid@token}% \fi \fi \fi \fi \fi \fi \fi \fi \fi \fi \fi \braid@next% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@maybe@locate} % If we got an \Verb+a+ token in the \Verb+\braid@handle@start+ then it \emph{might} mean we're looking at \Verb+at (coordinate)+ or it might mean that the user has decided to use \Verb+a+ as the braid parameter. % So we examine the next token for a \Verb+t+. % \begin{macrocode} \def\braid@maybe@locate{% \afterassignment\braid@@maybe@locate\let\braid@token=% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@@maybe@locate} % This is where we test for \Verb+t+ and act appropriately. % \begin{macrocode} \def\braid@@maybe@locate{% \let\braid@next=\braid@handle \ifx\braid@token t \let\braid@next=\braid@find@location \fi \braid@next% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@find@location} % This macro starts us looking for a coordinate. % \begin{macrocode} \def\braid@find@location{% \afterassignment\braid@@find@location\let\braid@token=% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@@find@location} % This is the test for the start of a coordinate. % If we get a \Verb+(+ that means we've reached the coordinate. % A space means ``carry on''. % Anything else is a (non-fatal) error. % \begin{macrocode} \def\braid@@find@location{% \let\braid@next=\braid@location@error \ifx\braid@token(%) \let\braid@next=\braid@locate \else \ifx\braid@token\@sptoken \let\braid@next=\braid@find@location \fi \fi \braid@next% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@location@error} % This is our error message for not getting a location. % \begin{macrocode} \def\braid@location@error{% \PackageWarning{braids}{Could not figure out location for braid}% \braid@process@start% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@locate} % If we reached a \Verb+(+ when looking for a coordinate, everything up to the next \Verb+)+ is that coordinate. % Then we parse the coordinate and call the relocation macro. % \begin{macrocode} \def\braid@locate#1){% \tikz@scan@one@point\braid@relocate(#1)% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@relocate} % This is the macro that actually does the relocation. % \begin{macrocode} \def\braid@relocate#1{% #1\relax \advance\pgf@x by -\braid@width \pgftransformshift{\pgfqpoint{\pgf@x}{\pgf@y}} \braid@process@start% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@assign@name} % This macro saves our name. % \begin{macrocode} \def\braid@assign@name#1){% \def\braid@name{#1}% \braid@process@start% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@process@options} % The intention of this macro is to allow setting of style options mid-braid. % (At present, this wouldn't make a lot of sense.) % \begin{macrocode} \def\braid@process@options#1]{% \tikzset{#1}% \braid@process% } % \end{macrocode} % \end{macro} % % The next macros handle the actual braid elements. % Everything has to have a subscript, but the superscript is optional and can come before or after the subscript. % % \begin{macro}[internal]{\braid@sup} % This handles braid elements of the form \Verb+a^{-1}_2+. % \begin{macrocode} \def\braid@sup#1_#2{% \g@addto@macro\braid@label{_{#2}^{#1}}% \braid@add@crossing{#2}{#1}% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@sub} % \begin{macrocode} % This handles braid elements of the form \Verb+a_1+ or \Verb+a_1^{-1}+. \def\braid@sub#1{% \@ifnextchar^{\braid@@sub{#1}}% {\g@addto@macro\braid@label{_{#1}}\braid@add@crossing{#1}{1}}% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@@sub} % Helper macro for \Verb+\braid@sub+. % \begin{macrocode} \def\braid@@sub#1^#2{% \g@addto@macro\braid@label{_{#1}^{#2}}% \braid@add@crossing{#1}{#2}% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@ne} % Remember what \Verb+1+ looks like for testing against. % \begin{macrocode} \def\braid@ne{1} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@add@crossing} % This is the macro which adds the crossing to the current list of strands. % The strands are stored as \emph{soft paths} (see the TikZ/PGF documentation). % So this selects the right strands and then extends them according to the crossing type. % \begin{macrocode} \def\braid@add@crossing#1#2{% % \end{macrocode} % Our crossing type, which is \Verb+#2+, is one of \Verb+1+ or \Verb+-1+. % Our strands are \Verb+#1+ and \Verb-#1+1-. % \begin{macrocode} \edef\braid@crossing@type{#2}% \edef\braid@this@strand{#1}% \pgfmathtruncatemacro{\braid@next@strand}{#1+1} % \end{macrocode} % Increment the level counter, if requested. % The controls whether the crossing is on the same level as the previous one or is one level further on. % \begin{macrocode} \ifbraid@increase@level \stepcounter{braid@level} \fi % \end{macrocode} % Default is to request increment so we set it for next time. % \begin{macrocode} \braid@increase@leveltrue % \end{macrocode} % Now we figure out the coordinates of the crossing. % \Verb+(\braid@tx,\braid@ty)+ is the top-left corner (assuming the braid flows down the page). % \Verb+(\braid@nx,\braid@ny)+ is the bottom-right corner (assuming the braid flows down the page). % We start by setting \Verb+(\braid@tx,\braid@ty)+ according to the level and strand number, then shift \Verb+\braid@ty+ by \Verb+\braid@eh+ which is the ``edge height'' (the little extra at the start and end of each strand). % Then from these values, we set \Verb+(\braid@nx,\braid@ny)+ by adding on the appropriate amount. % The heights \Verb+\braid@cy+ and \Verb+\braid@dy+ are for the control points for the strands as they cross. % They're actually the same height, but using two gives us the possibility of changing them independently in a later version of this package. % Lastly, we bring \Verb+\braid@ty+ and \Verb+\braid@ny+ towards each other just a little so that there is ``clear water'' between subsequent crossings (makes it look a bit better if the same strand is used in subsequent crossings). % \begin{macrocode} \braid@tx=\braid@this@strand\braid@width \braid@ty=\value{braid@level}\braid@height \advance\braid@ty by \braid@eh \braid@nx=\braid@tx \braid@ny=\braid@ty \advance\braid@nx by \braid@width \advance\braid@ny by \braid@height \advance\braid@ty by \braid@nf\braid@height \advance\braid@ny by -\braid@nf\braid@height \braid@cy=\braid@ty \braid@dy=\braid@ny \advance\braid@cy by \braid@cf\braid@height \advance\braid@dy by -\braid@cf\braid@height % \end{macrocode} % Now we try to find a starting point for the strand ending here. % We might not have used this strand before, so it might not exist. % \begin{macrocode} \expandafter\let\expandafter\braid@this@path@origin% \csname braid@strand@\braid@this@strand @origin\endcsname % \end{macrocode} % If we haven't seen this strand before, that one will be \Verb+\relax+. % \begin{macrocode} \ifx\braid@this@path@origin\relax % \end{macrocode} % Haven't seen this strand before, so initialise it. % Record the initial position of the strand. % \begin{macrocode} \let\braid@this@path@origin\braid@this@strand % \end{macrocode} % Start a new soft path. % \begin{macrocode} \pgfsyssoftpath@setcurrentpath{\@empty} \pgfpathmoveto{\pgfpoint{\braid@tx}{0pt}} % \end{macrocode} % Save the path as \Verb+\braid@this@path+. % \begin{macrocode} \pgfsyssoftpath@getcurrentpath{\braid@this@path} \else % \end{macrocode} % We have seen this before, so we simply copy the associated path in to \Verb+\braid@this@path+. % \begin{macrocode} \expandafter\let\expandafter\braid@this@path% \csname braid@strand@\braid@this@path@origin\endcsname \fi % \end{macrocode} % Now we do the same again with the other strand in the crossing. % \begin{macrocode} \expandafter\let\expandafter\braid@next@path@origin% \csname braid@strand@\braid@next@strand @origin\endcsname \ifx\braid@next@path@origin\relax \let\braid@next@path@origin\braid@next@strand \pgfsyssoftpath@setcurrentpath{\@empty} \pgfpathmoveto{\pgfpoint{\braid@nx}{0pt}} \pgfsyssoftpath@getcurrentpath{\braid@next@path} \else \expandafter\let\expandafter\braid@next@path% \csname braid@strand@\braid@next@path@origin\endcsname \fi % \end{macrocode} % Now that we have the paths for our two strands, we extend them to the next level. % We start by selecting the first path. % \begin{macrocode} \pgfsyssoftpath@setcurrentpath{\braid@this@path} % \end{macrocode} % Draw a line down to the current level, note that this line is always non-trivial since we shifted the corners of the crossing in a little. % \begin{macrocode} \pgfpathlineto{\pgfqpoint{\braid@tx}{\braid@ty}} % \end{macrocode} % Curve across to the next position. % Depending on the crossing type, we either have a single curve or we have to break it in two. % Our gap is to interrupt at times determined by the gap key. % \begin{macrocode} \pgfmathsetmacro{\braid@gst}{0.5 - \pgfkeysvalueof{/pgf/braid/gap}}% \pgfmathsetmacro{\braid@gend}{0.5 + \pgfkeysvalueof{/pgf/braid/gap}}% \ifx\braid@crossing@type\braid@over@cross % \end{macrocode} % We're on the overpass, so just one curve needed. % \begin{macrocode} \pgfpathcurveto{\pgfqpoint{\braid@tx}{\braid@cy}}% {\pgfqpoint{\braid@nx}{\braid@dy}}% {\pgfqpoint{\braid@nx}{\braid@ny}} \else % \end{macrocode} % We're on the underpass, so we need to interrupt our path to allow the other curve to go past. % \begin{macrocode} \pgfpathcurvebetweentimecontinue{0}{\braid@gst}% {\pgfqpoint{\braid@tx}{\braid@ty}}% {\pgfqpoint{\braid@tx}{\braid@cy}}% {\pgfqpoint{\braid@nx}{\braid@dy}}% {\pgfqpoint{\braid@nx}{\braid@ny}}% \pgfpathcurvebetweentime{\braid@gend}{1}% {\pgfqpoint{\braid@tx}{\braid@ty}}% {\pgfqpoint{\braid@tx}{\braid@cy}}% {\pgfqpoint{\braid@nx}{\braid@dy}}% {\pgfqpoint{\braid@nx}{\braid@ny}} \fi % \end{macrocode} % We're done with this path, so now we save it. % \begin{macrocode} \pgfsyssoftpath@getcurrentpath{\braid@this@path} % \end{macrocode} % Now do the same with the second path. % \begin{macrocode} \pgfsyssoftpath@setcurrentpath{\braid@next@path} \pgfpathlineto{\pgfqpoint{\braid@nx}{\braid@ty}} \ifx\braid@crossing@type\braid@over@cross \pgfpathcurvebetweentimecontinue{0}{\braid@gst}% {\pgfqpoint{\braid@nx}{\braid@ty}}% {\pgfqpoint{\braid@nx}{\braid@cy}}% {\pgfqpoint{\braid@tx}{\braid@dy}}% {\pgfqpoint{\braid@tx}{\braid@ny}} \pgfpathcurvebetweentime{\braid@gend}{1}% {\pgfqpoint{\braid@nx}{\braid@ty}}% {\pgfqpoint{\braid@nx}{\braid@cy}}% {\pgfqpoint{\braid@tx}{\braid@dy}}% {\pgfqpoint{\braid@tx}{\braid@ny}} \else \pgfpathcurveto{\pgfqpoint{\braid@nx}{\braid@cy}}% {\pgfqpoint{\braid@tx}{\braid@dy}}% {\pgfqpoint{\braid@tx}{\braid@ny}} \fi \pgfsyssoftpath@getcurrentpath{\braid@next@path} % \end{macrocode} % Now save the paths to their proper macros again. % \begin{macrocode} \expandafter\let% \csname braid@strand@\braid@this@path@origin \endcsname% \braid@this@path \expandafter\let% \csname braid@strand@\braid@next@path@origin \endcsname% \braid@next@path % \end{macrocode} % Now update the origins % \begin{macrocode} \expandafter\let% \csname braid@strand@\braid@this@strand @origin\endcsname% \braid@next@path@origin \expandafter\let% \csname braid@strand@\braid@next@strand @origin\endcsname% \braid@this@path@origin % \end{macrocode} % increment the strand counter, if necessary % \begin{macrocode} \pgfmathparse{\value{braid@strands} < \braid@next@strand ? "\noexpand\setcounter{braid@strands}{\braid@next@strand}" : ""} \pgfmathresult % \end{macrocode} % And merrily go on our way with the next bit of the braid specification. % \begin{macrocode} \braid@process% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@olabel@strand} % This macro allows us to label a strand just before a crossing. % The first argument is the strand number at that particular crossing and the second is the label. % We also save the current height. % This version takes the strand number as meaning the \emph{original} ordering. % \begin{macrocode} \newcommand{\braid@olabel@strand}[3][]{% \edef\braid@tmp{{\the\value{braid@level}}}% \expandafter\ifx\csname braid@strand@#2@origin\endcsname\relax \g@addto@macro\braid@tmp{{#2}}% \else \edef\braid@tmpa{{\csname braid@strand@#2@origin\endcsname}}% \ge@addto@macro\braid@tmp{\braid@tmpa}% \fi \g@addto@macro\braid@tmp{{#3}{#1}}% \ge@addto@macro{\braid@strand@labels}{\braid@tmp}% \braid@process% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@clabel@strand} % This macro allows us to label a strand just before a crossing. % The first argument is the strand number at that particular crossing and the second is the label. % We also save the current height. % This version takes the strand number as meaning the \emph{current} ordering. % \begin{macrocode} \newcommand{\braid@clabel@strand}[3][]{% \edef\braid@tmp{{\the\value{braid@level}}}% \g@addto@macro\braid@tmp{{#2}{#3}{#1}}% \ge@addto@macro{\braid@strand@labels}{\braid@tmp}% \braid@process% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@floors@trim} % The list of floors, if given, will start with a superfluous comma. % This removes it. % \begin{macrocode} \def\braid@floors@trim,{} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@render@floor} % This is the default rendering for floors: it draws a rectangle. % \begin{macrocode} \def\braid@render@floor{% \draw (\floorsx,\floorsy) rectangle (\floorex,\floorey); } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@render@strand@labels} % This starts rendering the labels on the strands at the crossings. % \begin{macrocode} \def\braid@render@strand@labels#1{% \def\braid@tmp{#1}% \ifx\braid@tmp\pgfutil@empty \let\braid@next=\pgfutil@gobble \else \let\braid@next=\braid@@render@strand@labels \fi \braid@next{#1}% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@@render@strand@labels} % This is the actual renderer. % \begin{macrocode} \def\braid@@render@strand@labels#1#2#3#4{% \begingroup \pgfscope \let\tikz@options=\pgfutil@empty \let\tikz@mode=\pgfutil@empty \let\tik@transform=\pgfutil@empty \let\tikz@fig@name=\pgfutil@empty \tikzset{/pgf/braid/strand label,#4}% \braid@nx=#2\braid@width \braid@ny=#1\braid@height \advance\braid@ny by \braid@eh \advance\braid@ny by \braid@height \pgftransformshift{\pgfqpoint{\braid@nx}{\braid@ny}}% \tikz@options \setbox\pgfnodeparttextbox=\hbox% \bgroup% \tikzset{every text node part/.try}% \ifx\tikz@textopacity\pgfutil@empty% \else% \pgfsetfillopacity{\tikz@textopacity}% \pgfsetstrokeopacity{\tikz@textopacity}% \fi% \pgfinterruptpicture% \tikz@textfont% \ifx\tikz@text@width\pgfutil@empty% \else% \begingroup% \pgfmathsetlength{\pgf@x}{\tikz@text@width}% \pgfutil@minipage[t]{\pgf@x}\leavevmode\hbox{}% \tikz@text@action% \fi% \tikz@atbegin@node% \bgroup% \aftergroup\unskip% \ifx\tikz@textcolor\pgfutil@empty% \else% \pgfutil@colorlet{.}{\tikz@textcolor}% \fi% \pgfsetcolor{.}% \setbox\tikz@figbox=\box\pgfutil@voidb@x% \tikz@uninstallcommands% \tikz@halign@check% \ignorespaces% #3 \egroup \tikz@atend@node% \ifx\tikz@text@width\pgfutil@empty% \else% \pgfutil@endminipage% \endgroup% \fi% \endpgfinterruptpicture% \egroup% \ifx\tikz@text@width\pgfutil@empty% \else% \pgfmathsetlength{\pgf@x}{\tikz@text@width}% \wd\pgfnodeparttextbox=\pgf@x% \fi% \ifx\tikz@text@height\pgfutil@empty% \else% \pgfmathsetlength{\pgf@x}{\tikz@text@height}% \ht\pgfnodeparttextbox=\pgf@x% \fi% \ifx\tikz@text@depth\pgfutil@empty% \else% \pgfmathsetlength{\pgf@x}{\tikz@text@depth}% \dp\pgfnodeparttextbox=\pgf@x% \fi% \pgfmultipartnode{\tikz@shape}{\tikz@anchor}{\tikz@fig@name}{% {\begingroup\tikz@finish}% }% \endpgfscope \endgroup \braid@render@strand@labels% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@render} % This is called at the end of the braid and it renders the braids and floors according to whatever has been built up up to now. % \begin{macrocode} \def\braid@render{ % \end{macrocode} % Check for floors since we do them first. % \begin{macrocode} \ifx\braid@floors\@empty \else % \end{macrocode} % Have some floors, start a scope and prepare to render them. % \begin{macrocode} \pgfsys@beginscope % \end{macrocode} % Clear the path (just to be sure). % \begin{macrocode} \pgfsyssoftpath@setcurrentpath{\empty} % \end{macrocode} % Trim the initial comma off the list of floors. % \begin{macrocode} \edef\braid@floors{\expandafter\braid@floors@trim\braid@floors} % \end{macrocode} % Initialise our horizontal coordinates. % \begin{macrocode} \braid@tx=\braid@width \advance\braid@tx by \braid@eh \braid@nx=\value{braid@strands}\braid@width \advance\braid@nx by -\braid@eh % \end{macrocode} % Loop over the list of floors. % \begin{macrocode} \foreach \braid@f in \braid@floors { \pgfsys@beginscope % \end{macrocode} % Figure out the vertical coordinates for the current floor. % \begin{macrocode} \braid@ty=\braid@f\braid@height \advance\braid@ty by \braid@eh \advance\braid@ty by \braid@height \braid@ny=\braid@ty \advance\braid@ny by \braid@height % \end{macrocode} % Save the coordinates for use in the floor rendering macro. % \begin{macrocode} \edef\floorsx{\the\braid@tx} \edef\floorsy{\the\braid@ty} \edef\floorex{\the\braid@nx} \edef\floorey{\the\braid@ny} \let\tikz@options=\pgfutil@empty % \end{macrocode} % Load general floor style options. % \begin{macrocode} \expandafter\tikzset\expandafter{\braid@floors@style} % \end{macrocode} % Load any style options specific to this floor. % We're actually offset by 2 from what the user thinks the floor level is. % \begin{macrocode} \pgfmathtruncatemacro{\braid@ff}{\braid@f+2} % \end{macrocode} % Load the relevant floor style, if it exists. % \begin{macrocode} \expandafter\let\expandafter\braid@floor@style% \csname braid@options@floor@\braid@ff\endcsname \ifx\braid@floor@style\relax \else % \end{macrocode} % There is a floor style for this level, so process it. % \begin{macrocode} \expandafter\tikzset\expandafter{\braid@floor@style}% \fi % \end{macrocode} % The \Verb+\tikzset+ just parses the options, we need to call \Verb+\tikz@options+ to actually set them. % \begin{macrocode} \tikz@options % \end{macrocode} % Now we call the rendering code. % \begin{macrocode} \braid@render@floor % \end{macrocode} % Done! % End the scope for \emph{this} floor and go again. % \begin{macrocode} \pgfsys@endscope } % \end{macrocode} % Done rendering floors, end the scope. % \begin{macrocode} \pgfsys@endscope \fi % \end{macrocode} % Finished with floors (if we had them), now get on with the strands. % \begin{macrocode} \stepcounter{braid@level} \foreach \braid@k in {1,...,\value{braid@strands}} { % \end{macrocode} % Start a local scope to ensure we don't mess with other braids % \begin{macrocode} \pgfsys@beginscope % \end{macrocode} % Default is to draw each braid % \begin{macrocode} \tikz@mode@drawtrue% \let\tikz@mode=\pgfutil@empty \let\tikz@options=\pgfutil@empty % \end{macrocode} % (x,y) coordinates of bottom of strand % \begin{macrocode} \braid@tx=\braid@k\braid@width \braid@ty=\value{braid@level}\braid@height \advance\braid@ty by 2\braid@eh % \end{macrocode} % Try to find the starting point of this strand % \begin{macrocode} \expandafter\let\expandafter\braid@path@origin% \csname braid@strand@\braid@k @origin\endcsname \ifx\braid@path@origin\relax % \end{macrocode} % If that doesn't exist, we'll just draw a straight line % so we move to the top of the current position % \begin{macrocode} \pgfsyssoftpath@setcurrentpath{\@empty} \pgfpathmoveto{\pgfqpoint{\braid@tx}{0pt}} \let\braid@path@origin\braid@k \else % \end{macrocode} % If the path does exist, we load it % \begin{macrocode} \expandafter\let\expandafter\braid@path% \csname braid@strand@\braid@path@origin\endcsname \pgfsyssoftpath@setcurrentpath{\braid@path} \fi % \end{macrocode} % Extend the path to the bottom % \begin{macrocode} \pgflineto{\pgfqpoint{\braid@tx}{\braid@ty}} % \end{macrocode} % Load common style options % \begin{macrocode} \expandafter\tikzset\expandafter{\braid@style} % \end{macrocode} % Load any style options specific to this strand % \begin{macrocode} \expandafter\let\expandafter\braid@style% \csname braid@options@strand@\braid@path@origin\endcsname \ifx\braid@style\relax \else \expandafter\tikzset\expandafter{\braid@style} \fi \braid@options \tikz@mode \tikz@options % \end{macrocode} % This is the command that actually draws the strand. % \begin{macrocode} \edef\tikz@temp{\noexpand\pgfusepath{% \iftikz@mode@draw draw\fi% }}% \tikz@temp % \end{macrocode} % If our braid has a name, we label the ends of the strand. % \begin{macrocode} \ifx\braid@name\pgfutil@empty \else % \end{macrocode} % Label the ends of the strand. % \begin{macrocode} \coordinate (\braid@name-\braid@path@origin-e) at (\braid@tx,\braid@ty); \coordinate (\braid@name-rev-\braid@k-e) at (\braid@tx,\braid@ty); \braid@nx=\braid@path@origin\braid@width \coordinate (\braid@name-\braid@path@origin-s) at (\braid@nx,0pt); \coordinate (\braid@name-rev-\braid@k-s) at (\braid@nx,0pt); \fi % \end{macrocode} % Done with this strand, close the scope and do the next one. % \begin{macrocode} \pgfsys@endscope } % \end{macrocode} % If our braid has a name, we also want to label the centre. % \begin{macrocode} \ifx\braid@name\pgfutil@empty \else \braid@tx=\value{braid@strands}\braid@width \braid@ty=\value{braid@level}\braid@height \advance\braid@ty by 2\braid@eh \advance\braid@tx by \braid@width \braid@tx=.5\braid@tx \braid@ty=.5\braid@ty \coordinate (\braid@name) at (\braid@tx,\braid@ty); \fi % \end{macrocode} % Now we label the strands if needed. % \begin{macrocode} \ifx\braid@strand@labels\pgfutil@empty \else \expandafter\braid@render@strand@labels\braid@strand@labels{}% \fi % \end{macrocode} % All done now, close the scope and end the group (which was opened right at the start). % \begin{macrocode} \pgfsys@endscope \endgroup} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\braid@start} % This starts off the braid, initialising a load of stuff. % We start a PGF scope, set the level to \(-1\), the label, floors, and name to empty, process any options we're given, and save certain lengths for later use.. % \begin{macrocode} \def\braid@start#1{% \pgfsys@beginscope \setcounter{braid@level}{-1}% \let\braid@label\@empty \let\braid@strand@labels\@empty \let\braid@floors\@empty \let\braid@name\empty \let\clabel=\braid@clabel@strand \let\olabel=\braid@olabel@strand \pgfkeys{/pgf/braid/.cd,#1}% \ifbraid@strand@labels@origin \let\label=\braid@olabel@strand \else \let\label=\braid@clabel@strand \fi \let\braid@options\tikz@options \tikz@transform \setcounter{braid@strands}{% \pgfkeysvalueof{/pgf/braid/number of strands}}% \braid@width=\pgfkeysvalueof{/pgf/braid/width}% \braid@height=\pgfkeysvalueof{/pgf/braid/height}% \braid@eh=\pgfkeysvalueof{/pgf/braid/border height}% \pgfkeysgetvalue{/pgf/braid/control factor}{\braid@cf}% \pgfkeysgetvalue{/pgf/braid/nudge factor}{\braid@nf}% \braid@height=-\braid@height \braid@eh=-\braid@eh \braid@increase@leveltrue \braid@process@start } % \end{macrocode} % \end{macro} % % These are the lengths we'll use as we construct the braid % \begin{macrocode} \newdimen\braid@width \newdimen\braid@height \newdimen\braid@tx \newdimen\braid@ty \newdimen\braid@nx \newdimen\braid@ny \newdimen\braid@cy \newdimen\braid@dy \newdimen\braid@eh % \end{macrocode} % % An if to decide whether or not to step to the next level or not % \begin{macrocode} \newif\ifbraid@increase@level % \end{macrocode} % An if to decide whether label indices should be absolute or not % \begin{macrocode} \newif\ifbraid@strand@labels@origin % \end{macrocode} % % % Some initial values % \begin{macrocode} \let\braid@style\pgfutil@empty \let\braid@floors@style\pgfutil@empty \def\braid@over@cross{1} % \end{macrocode} % % Counters to track the strands and the levels. % \begin{macrocode} \newcounter{braid@level} \newcounter{braid@strands} % \end{macrocode} % % All the keys we'll use. % \begin{macrocode} \pgfkeys{ % \end{macrocode} % Handle unknown keys by passing them to \Verb+pgf+ and \Verb+tikz+. % \begin{macrocode} /tikz/braid/.search also={/pgf}, /pgf/braid/.search also={/pgf,/tikz}, % \end{macrocode} % Our ``namespace'' is \Verb+/pgf/braid+. % \begin{macrocode} /pgf/braid/.cd, number of strands/.initial=0, height/.initial=1cm, width/.initial=1cm, gap/.initial=.1, border height/.initial=.25cm, control factor/.initial=.5, nudge factor/.initial=.05, name/.code={% \def\braid@name{#1}% }, at/.code={% \braid@relocate{#1}% }, floor command/.code={% \def\braid@render@floor{#1}% }, style strands/.code 2 args={% \def\braid@temp{#2}% \braidset{style each strand/.list={#1}}% }, style each strand/.code={% \expandafter\edef% \csname braid@options@strand@#1\endcsname{\braid@temp}% }, style floors/.code 2 args={% \def\braid@temp{#2}% \braidset{style each floor/.list={#1}}% }, style each floor/.code={% \expandafter\edef% \csname braid@options@floor@#1\endcsname{\braid@temp}% }, style all floors/.code={% \def\braid@floors@style{#1} }, strand label/.style={}, strand label by origin/.is if=braid@strand@labels@origin, } % \end{macrocode} % \begin{macro}{\braidset} % Shorthand for setting braid-specific keys. % \begin{macrocode} \def\braidset#1{% \pgfkeys{/pgf/braid/.cd,#1}} % \end{macrocode} % \end{macro} % % \iffalse % % \fi % \begin{macrocode} %<*library> %<@@=braid> % \end{macrocode} % % \section{Reimplementation as a TikZ Library} % % Life is so much easier with \LaTeX3. % \begin{macrocode} \ProvidesFile{tikzlibrarybraids.code.tex}[% 2024/01/09 v2.3 Tikz/PGF library for drawing braid diagrams% ] \RequirePackage{expl3} \ExplSyntaxOn % \end{macrocode} % Define all the variables we'll be using. % \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_@@_anchor_strand_tl \tl_new:N \l_@@_anchor_level_tl \fp_new:N \l_@@_height_fp \fp_new:N \l_@@_width_fp \fp_new:N \l_@@_nudge_fp \fp_new:N \l_@@_control_fp \fp_new:N \l_@@_ctrlax_fp \fp_new:N \l_@@_ctrlay_fp \fp_new:N \l_@@_ctrlbx_fp \fp_new:N \l_@@_ctrlby_fp \fp_new:N \l_@@_endx_fp \fp_new:N \l_@@_endy_fp \fp_new:N \l_@@_anchor_x_fp \fp_new:N \l_@@_anchor_y_fp \int_new:N \l_@@_tmpa_int \int_new:N \l_@@_tmpb_int \int_new:N \l_@@_length_int \int_new:N \l_@@_strands_int \int_new:N \l_@@_crossing_int \int_new:N \l_@@_crossing_start_int \int_new:N \l_@@_crossing_end_int \int_new:N \l_@@_crossing_width_int \int_new:N \l_@@_crossing_long_int \int_new:N \l_@@_crossing_start_factor_int \int_new:N \l_@@_crossing_end_factor_int \int_new:N \l_@@_anchor_level_int \int_new:N \l_@@_floor_int \seq_new:N \l_@@_tmpa_seq \seq_new:N \l_@@_word_seq \seq_new:N \l_@@_crossing_seq \seq_new:N \l_@@_anchor_seq \seq_new:N \l_@@_floors_seq \str_new:N \l_@@_tmpa_str \bool_new:N \l_@@_step_level_bool \bool_new:N \l_@@_swap_crossing_bool \bool_new:N \l_@@_default_crossing_bool \bool_new:N \l_@@_default_symbol_bool \bool_set_true:N \l_@@_default_crossing_bool \bool_set_true:N \l_@@_default_symbol_bool \bool_new:N \l_@@_floor_bool \bool_new:N \l_@@_height_bool \bool_new:N \l_@@_crossing_height_bool \prop_new:N \l_@@_strands_prop \prop_new:N \l_@@_permutation_prop \prop_new:N \l_@@_crossing_permutation_prop \prop_new:N \l_@@_inverse_prop \prop_new:N \l_@@_anchor_prop \msg_new:nnn {braids} {height} {The~ keys~ "height"~ and~ "crossing~ height"~ shouldn't~ be~ used~ together~ (the~ setting~ for~ "crossing~ height"~ will~ be~ used)} \cs_generate_variant:Nn \seq_set_split:Nnn {NVn} \cs_generate_variant:Nn \str_compare:nNnTF {VNnTF} % \end{macrocode} % Our interface is through a TikZ pic. % \begin{macrocode} \tikzset{ braid/.pic={ \begin{scope}[every~ braid/.try] \@@_parse_word:n {#1} \@@_count: \@@_render: \end{scope} }, floor/.pic={ \path[pic~ actions, draw=none] (0,0) rectangle (1,1); \path[pic~ actions, fill=none] (0,0) -- (1,0) (0,1) -- (1,1); }, /tikz/braid/.search~ also={/tikz}, braid/.cd, % \end{macrocode} % The various TikZ parameters for the braid. % % The anchor determines which part of the braid is located at the position specified by the pic. % It can be of the form \Verb+n-m+ where \Verb+n+ is a strand number and +m+ is a crossing level. % The strand number can be either a number or \Verb+rev-n+ to use the ending numbering of the strands. % The crossing level can also be \Verb+s+ or \Verb+e+ which means the actual start or end of the strand (including the border). % % \begin{macrocode} anchor/.initial=1-s, % \end{macrocode} % \Verb+number of strands+ sets a minimum for the number of strands in the braid (otherwise, it is set by the strands used in the specified crossings). % \begin{macrocode} number~ of~ strands/.initial=0, % \end{macrocode} % % These keys determine whether crossings are over or under by default. % \begin{macrocode} crossing~ convention/.is~choice, crossing~ convention/over/.code={ \bool_set_true:N \l_@@_default_crossing_bool }, crossing~ convention/down/.code={ \bool_set_true:N \l_@@_default_crossing_bool }, crossing~ convention/under/.code={ \bool_set_false:N \l_@@_default_crossing_bool }, crossing~ convention/up/.code={ \bool_set_false:N \l_@@_default_crossing_bool }, crossing~ convention/.default=over, % \end{macrocode} % % \begin{macrocode} flip~ crossing~ convention/.code={ \bool_set_inverse:N \l_@@_default_crossing_bool }, % \end{macrocode} % % These keys determine whether elements should be inverted by default. % \begin{macrocode} set~ symbols/.is~choice, set~ symbols/over/.code={ \bool_set_true:N \l_@@_default_symbol_bool }, set~ symbols/down/.code={ \bool_set_true:N \l_@@_default_symbol_bool }, set~ symbols/under/.code={ \bool_set_false:N \l_@@_default_symbol_bool }, set~ symbols/up/.code={ \bool_set_false:N \l_@@_default_symbol_bool }, set~ symbols/.default=over, % \end{macrocode} % % \begin{macrocode} flip~ symbols/.code={ \bool_set_inverse:N \l_@@_default_symbol_bool }, % \end{macrocode} % % The next two keys are used to control the separation between the crossings. % The original braid package used the \Verb+height+ as part of how it determined the direction of the braid on the page. % In particular, the \Verb+height+ could be negative, and indeed the default is for it to be so since a vertical braid usually flows from top to bottom. % Now that the drawing is reimplemented as a \Verb+pic+ then the direction is better controlled using transformations. % So then \Verb+height+ should be simply to set the gap between crossings in whatever orientation. % In particular, \Verb+height+ should be a positive length. % % To avoid backwards incompatibility, the original (counter-intuitive) version of \Verb+height+ is retained but a new key, \Verb+crossing height+, is introduced which can only be positive (rather, the code will take its absolute value so you can \emph{declare} it to be negative but the code will laugh at you and ignore the sign). % % To ensure that \Verb+crossing height+ wins, we use a boolean to see if it has been invoked by the user. % \begin{macrocode} height/.initial=-1cm, height/.code={ \bool_if:NTF \l_@@_crossing_height_bool { \msg_term:nn {braids} {height} } { \pgfkeyssetvalue{/tikz/braid/height}{#1} \bool_set_true:N \l_@@_height_bool } }, crossing~ height/.code={ \bool_if:NT \l_@@_height_bool { \msg_term:nn {braids} {height} } \exp_args:Nnx \pgfkeyssetvalue {/tikz/braid/height} {\dim_eval:n {-\dim_abs:n{#1}}} \bool_set_true:N \l_@@_crossing_height_bool }, % \end{macrocode} % \Verb+width+ is the distance between strands (can be negative). % \begin{macrocode} width/.initial=1cm, % \end{macrocode} % \Verb+gap+ is for determining the gap in the under-strand of a crossing. % \begin{macrocode} gap/.initial=.05, % \end{macrocode} % \Verb+border height+ is a length added at the start and end of each strand. % \begin{macrocode} border~ height/.initial=.25cm, % \end{macrocode} % \Verb+floor border+ is added to the width of any floors % \begin{macrocode} floor~ border/.initial=.25cm, % \end{macrocode} % \Verb+floors+ is a list of floors to draw, specified as a cslist of coordinates as (x,y,w,h,a) in which the units are numbers of strands and crossing levels. % The parameters are: coordinates of lower left corner, width, height, (optional) name for styling. % \begin{macrocode} add~ floor/.code={ \seq_push:Nn \l_@@_floors_seq {#1} }, % \end{macrocode} % \Verb+control factor+ determines the proportion of the \Verb+height+ used for the control points. % \begin{macrocode} control~ factor/.initial=.5, % \end{macrocode} % \Verb+nudge factor+ is used to compress each crossing slightly within its rectangle. % \begin{macrocode} nudge~ factor/.initial=.05 } % \end{macrocode} % % \begin{macro}[internal]{\@@_parse_word:Nn} % Parse the braid word as a token list and convert it into a sequence. % \begin{macrocode} \cs_new_nopar:Npn \@@_parse_word:n #1 { \seq_clear:N \l_@@_word_seq \tl_clear:N \l_@@_tmpa_tl \tl_set:Nn \l_@@_tmpb_tl {#1} \bool_until_do:nn { \tl_if_empty_p:N \l_@@_tmpb_tl } { % \end{macrocode} % We step through the braid specification, looking for special characters. % To avoid catcode issues, the comparison is as strings. % Some actions may involve consuming more tokens from the list so we can't do a simple \Verb+map_inline+ but have to keep stripping off the head token. % % The idea is to store information about the current crossing in a token list (noting that it may be specified in a variety of orders) and then when we're sure we have all the information we add it to our sequence of crossings. % \begin{macrocode} \str_set:Nx \l_@@_tmpa_str {\tl_head:N \l_@@_tmpb_tl} \tl_set:Nx \l_@@_tmpb_tl {\tl_tail:N \l_@@_tmpb_tl} % \end{macrocode} % % \begin{macrocode} \str_case_e:nnTF {\l_@@_tmpa_str} { % \end{macrocode} % Underscore introduces the crossing numbers % \begin{macrocode} {_} { \tl_put_right:Nx \l_@@_tmpa_tl { \exp_not:N \@@_parse_index:n {\tl_head:N \l_@@_tmpb_tl} } \tl_set:Nx \l_@@_tmpb_tl {\tl_tail:N \l_@@_tmpb_tl} } % \end{macrocode} % Power is used to indicate inverse. % \begin{macrocode} {^} { \tl_put_left:Nx \l_@@_tmpa_tl { \exp_not:N \@@_parse_exponent:n {\tl_head:N \l_@@_tmpb_tl} } \tl_set:Nx \l_@@_tmpb_tl {\tl_tail:N \l_@@_tmpb_tl} } % \end{macrocode} % Bar is for floors. % \begin{macrocode} {|} { \tl_if_empty:NF \l_@@_tmpa_tl { \seq_put_right:NV \l_@@_word_seq \l_@@_tmpa_tl \tl_clear:N \l_@@_tmpa_tl } \tl_set:Nn \l_@@_tmpa_tl { \bool_set_false:N \l_@@_step_level_bool \bool_set_true:N \l_@@_floor_bool } \seq_put_right:NV \l_@@_word_seq \l_@@_tmpa_tl \tl_clear:N \l_@@_tmpa_tl } % \end{macrocode} % Hyphen says the next crossing is on the same level as the current one. % \begin{macrocode} {-} { \tl_put_right:Nn \l_@@_tmpa_tl { \bool_set_false:N \l_@@_step_level_bool } } % \end{macrocode} % \(1\) is for the identity (i.e., no crossing but still have a level). % We put a nop token on the list so that it is no longer empty. % \begin{macrocode} {1} { \tl_if_empty:NF \l_@@_tmpa_tl { \seq_put_right:NV \l_@@_word_seq \l_@@_tmpa_tl \tl_clear:N \l_@@_tmpa_tl } \tl_put_right:Nn \l_@@_tmpa_tl {\@@_do_identity:} } % \end{macrocode} % Ignore spaces. % \begin{macrocode} {~} { } } { } { % \end{macrocode} % If we get an unrecognised token, it's our trigger to start accumulating information for the next crossing. % \begin{macrocode} \tl_if_empty:NF \l_@@_tmpa_tl { \seq_put_right:NV \l_@@_word_seq \l_@@_tmpa_tl \tl_clear:N \l_@@_tmpa_tl } } } % \end{macrocode} % At the end, we also put our current token list on the word sequence. % \begin{macrocode} \tl_if_empty:NF \l_@@_tmpa_tl { \seq_put_right:NV \l_@@_word_seq \l_@@_tmpa_tl \tl_clear:N \l_@@_tmpa_tl } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_parse_index:n} % Parse an index, saving it in a sequence with the two indices such that the first goes over the second. % \begin{macrocode} \cs_new_nopar:Npn \@@_parse_index:n #1 { \seq_clear:N \l_@@_crossing_seq \clist_map_inline:nn {#1} { \tl_if_in:nnTF {##1} {-} { \seq_set_split:Nnn \l_@@_tmpa_seq {-} {##1} \int_compare:nTF {\seq_item:Nn \l_@@_tmpa_seq {1} < \seq_item:Nn \l_@@_tmpa_seq {2} } { \int_set:Nn \l_@@_tmpa_int {1} } { \int_set:Nn \l_@@_tmpa_int {-1} } \int_step_inline:nnnn {\seq_item:Nn \l_@@_tmpa_seq {1}} {\l_@@_tmpa_int} {\seq_item:Nn \l_@@_tmpa_seq {2}} { \seq_put_right:Nn \l_@@_crossing_seq {####1} } } { \seq_put_right:Nn \l_@@_crossing_seq {##1} } } \int_compare:nT {\seq_count:N \l_@@_crossing_seq == 1} { \seq_put_right:Nx \l_@@_crossing_seq {\int_eval:n {#1 + 1} } } \bool_xor:nnT {\l_@@_swap_crossing_bool} {\l_@@_default_symbol_bool} { \seq_reverse:N \l_@@_crossing_seq } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_parse_exponent:n} % Parse an exponent, basically testing to see if it is \(-1\) in which case our crossing numbers should be reversed. % \begin{macrocode} \cs_new_nopar:Npn \@@_parse_exponent:n #1 { \int_compare:nTF {#1 == -1} { \bool_set_true:N \l_@@_swap_crossing_bool } { \bool_set_false:N \l_@@_swap_crossing_bool } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_do_identity:} % \begin{macrocode} \cs_new_nopar:Npn \@@_do_identity: { } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_count:NNN} % Work out how big the braid is by counting strands and levels. % We also figure out the permutation from the start to end of the strands. % This is useful for labelling various parts of the braid. % \begin{macrocode} \cs_new_nopar:Npn \@@_count: { \int_zero:N \l_@@_length_int \int_set:Nn \l_@@_strands_int {\@@_value:n {number~of~strands}} \prop_clear:N \l_@@_permutation_prop \prop_clear:N \l_@@_crossing_permutation_prop \prop_clear:N \l_@@_anchor_prop \prop_clear:N \l_@@_inverse_prop \seq_map_inline:Nn \l_@@_word_seq { % \end{macrocode} % Clear the crossing sequence and assume we're going to step the level. % \begin{macrocode} \seq_clear:N \l_@@_crossing_seq \bool_set_true:N \l_@@_step_level_bool \bool_set_false:N \l_@@_swap_crossing_bool % \end{macrocode} % Run the details of this crossing. % \begin{macrocode} ##1 % \end{macrocode} % If we're increasing the level (no hyphen), do so. % \begin{macrocode} \bool_if:NT \l_@@_step_level_bool { \int_incr:N \l_@@_length_int } % \end{macrocode} % If we have a crossing, check we have enough strands to cover it. % \begin{macrocode} \seq_if_empty:NF \l_@@_crossing_seq { \seq_map_inline:Nn \l_@@_crossing_seq { \int_set:Nn \l_@@_strands_int { \int_max:nn {\l_@@_strands_int} {####1} } } } } % \end{macrocode} % Now that we know how many strands we have, we can initialise our permutation props. % One will hold the overall permutation, the other will keep track of our current permutation. % \begin{macrocode} \int_step_inline:nnnn {1} {1} {\l_@@_strands_int} { \prop_put:Nnn \l_@@_permutation_prop {##1} {##1} \prop_put:Nnn \l_@@_anchor_prop {##1} {##1} \prop_put:Nnn \l_@@_crossing_permutation_prop {##1} {##1} } % \end{macrocode} % Now we step through the braid word again and record the permutations so that we can calculate the overall permutation defined by the braid. % % We will also figure out our shift from the anchor, so first we need to get some information about the anchor. % % If the anchor specification has a hyphen then it is of the form strand-level, otherwise it is an anchor as if the whole braid were contained in a rectangular node. % \begin{macrocode} \tl_set:Nx \l_@@_tmpa_tl {\@@_value:n {anchor}} \tl_if_in:NnTF \l_@@_tmpa_tl {-} { \seq_set_split:NnV \l_@@_anchor_seq {-} \l_@@_tmpa_tl \tl_set:Nx \l_@@_tmpa_tl {\seq_item:Nn \l_@@_anchor_seq {1}} \tl_if_eq:VnTF \l_@@_tmpa_tl {rev} { \tl_set:Nx \l_@@_anchor_strand_tl {\seq_item:Nn \l_@@_anchor_seq {2}} \tl_set:Nx \l_@@_anchor_level_tl {\seq_item:Nn \l_@@_anchor_seq {3}} } { \tl_set:Nx \l_@@_anchor_strand_tl {\seq_item:Nn \l_@@_anchor_seq {1}} \tl_set:Nx \l_@@_anchor_level_tl {\seq_item:Nn \l_@@_anchor_seq {2}} } % \end{macrocode} % The important information is as to the level at which the requested anchor resides. % If it is at the end or start of a strand, we set the level to \(-1\) so that it never matches a level number. % \begin{macrocode} \tl_if_eq:VnTF \l_@@_anchor_level_tl {s} { \int_set:Nn \l_@@_anchor_level_int {-1} } { \tl_if_eq:VnTF \l_@@_anchor_level_tl {e} { \int_set:Nn \l_@@_anchor_level_int {-1} } { \int_set:Nn \l_@@_anchor_level_int {\tl_use:N \l_@@_anchor_level_tl} } } } { % \end{macrocode} % There wasn't a hyphen in the anchor specification, so assume it's an anchor on a node surrounding the entire braid. % For now, set the anchor strand and level to \(-1\). % \begin{macrocode} \int_set:Nn \l_@@_anchor_level_int {-1} \tl_set:Nn \l_@@_anchor_strand_tl {-1} } \int_zero:N \l_@@_crossing_int \int_incr:N \l_@@_crossing_int % \end{macrocode} % % \begin{macrocode} \seq_map_inline:Nn \l_@@_word_seq { \bool_set_true:N \l_@@_step_level_bool \seq_clear:N \l_@@_crossing_seq \bool_set_false:N \l_@@_swap_crossing_bool ##1 \seq_if_empty:NF \l_@@_crossing_seq { \int_step_inline:nnn {2} {\seq_count:N \l_@@_crossing_seq} { \int_set:Nn \l_@@_tmpa_int {####1} \int_set:Nn \l_@@_tmpb_int {####1 - 1} \prop_get:NxN \l_@@_permutation_prop { \seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpa_int} } \l_@@_tmpa_tl \prop_get:NxN \l_@@_permutation_prop { \seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpb_int} } \l_@@_tmpb_tl \prop_put:NxV \l_@@_permutation_prop { \seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpb_int} } \l_@@_tmpa_tl \prop_put:NxV \l_@@_permutation_prop { \seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpa_int} } \l_@@_tmpb_tl } } % \end{macrocode} % See if the current level is what was requested by the anchor. % \begin{macrocode} \int_compare:nT {\l_@@_crossing_int = \l_@@_anchor_level_int} { \prop_set_eq:NN \l_@@_anchor_prop \l_@@_permutation_prop } \bool_if:NT \l_@@_step_level_bool { \int_incr:N \l_@@_crossing_int } } % \end{macrocode} % This inverts the anchor permutation. % \begin{macrocode} \int_step_inline:nnnn {1} {1} {\l_@@_strands_int} { \prop_get:NnN \l_@@_anchor_prop {##1} \l_@@_tmpa_tl \prop_put:NVn \l_@@_inverse_prop \l_@@_tmpa_tl {##1} } \prop_set_eq:NN \l_@@_anchor_prop \l_@@_inverse_prop % \end{macrocode} % This inverts the full permutation. % \begin{macrocode} \int_step_inline:nnnn {1} {1} {\l_@@_strands_int} { \prop_get:NnN \l_@@_permutation_prop {##1} \l_@@_tmpa_tl \prop_put:NVn \l_@@_inverse_prop \l_@@_tmpa_tl {##1} } % \end{macrocode} % Now that we have the inverse, we can figure out our anchor. % If the strand was recorded as \(-1\), then we want to figure out the position from the braid as a whole so we don't bother with processing. % \begin{macrocode} \tl_if_eq:VnF \l_@@_anchor_strand_tl {-1} { % \end{macrocode} % Now, see if we requested a strand by its position at the end of the braid. % \begin{macrocode} \tl_set:Nx \l_@@_tmpa_tl {\seq_item:Nn \l_@@_anchor_seq {1}} \tl_if_eq:VnT \l_@@_tmpa_tl {rev} { \prop_get:NVN \l_@@_permutation_prop \l_@@_anchor_strand_tl \l_@@_anchor_strand_tl } \tl_if_eq:VnF \l_@@_anchor_level_tl {s} { \tl_if_eq:VnTF \l_@@_anchor_level_tl {e} { \prop_get:NVN \l_@@_inverse_prop \l_@@_anchor_strand_tl \l_@@_anchor_strand_tl } { \prop_get:NVN \l_@@_anchor_prop \l_@@_anchor_strand_tl \l_@@_anchor_strand_tl } } } % \end{macrocode} % \begin{macrocode} } % \end{macrocode} % % % \end{macro} % % % \begin{macro}{\@@_dim_value:n, \@@_value:n} % Extract a length or a value from a PGF key. % \begin{macrocode} \cs_new_nopar:Npn \@@_dim_value:n #1 { \dim_to_fp:n {\pgfkeysvalueof{/tikz/braid/#1}} } \cs_new_nopar:Npn \@@_value:n #1 { \pgfkeysvalueof{/tikz/braid/#1} } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_render:} % This is the macro that converts the braid word into TikZ paths. % \begin{macrocode} \cs_generate_variant:Nn \prop_get:NnN {NxN} \cs_generate_variant:Nn \prop_put:Nnn {NxV} \cs_generate_variant:Nn \tl_if_eq:nnTF {VnTF} \cs_generate_variant:Nn \tl_if_eq:nnF {VnF} \cs_generate_variant:Nn \tl_if_eq:nnT {VnT} \cs_new_nopar:Npn \@@_render: { % \end{macrocode} % Start by figuring out our anchor. % \begin{macrocode} \tl_if_eq:VnTF \l_@@_anchor_strand_tl {-1} { % \end{macrocode} % The strand is \(-1\) then we're working with the braid as if a node. % We'll redefine this node later anyway. % \begin{macrocode} \tl_set:cn {pgf@sh@ns@temporary braid node}{rectangle} \tl_set:cx {pgf@sh@np@temporary braid node}{% \exp_not:N\def \exp_not:N\southwest { \exp_not:N\pgfqpoint {0pt} {0pt} } \exp_not:N\def \exp_not:N\northeast { \exp_not:N\pgfqpoint { \fp_to_dim:n { (\l_@@_strands_int - 1) * abs(\@@_dim_value:n {width}) } } { \fp_to_dim:n { \l_@@_length_int * abs(\@@_dim_value:n {height}) + 2 * \@@_dim_value:n {border~ height} } } } }% \pgfgettransform\l_@@_tmpa_tl \tl_set:cV {pgf@sh@nt@temporary braid node} \l_@@_tmpa_tl \tl_set:cV {pgf@sh@pi@temporary braid node} \pgfpictureid \pgfpointanchor{temporary braid node} {\@@_value:n {anchor}} % \end{macrocode} % Adjustments due to the possibility of negative widths/heights % \begin{macrocode} \fp_set:Nn \l_@@_anchor_x_fp { - \dim_use:c {pgf@x} - (1 - sign(\@@_dim_value:n {width})) / 2 * (\l_@@_strands_int - 1) * \@@_dim_value:n {width} } \fp_set:Nn \l_@@_anchor_y_fp { - \dim_use:c {pgf@y} - (1 - sign(\@@_dim_value:n {height})) / 2 * ( \l_@@_length_int * abs(\@@_dim_value:n {height}) + 2 * \@@_dim_value:n {border~ height} ) * sign(\@@_dim_value:n {height}) } } { % \end{macrocode} % The strand is not \(-1\) so we're setting the anchor via strand and level numbers. % \begin{macrocode} \fp_set:Nn \l_@@_anchor_x_fp { - 1 * (\tl_use:N \l_@@_anchor_strand_tl - 1) * \@@_dim_value:n {width} } \tl_if_eq:VnTF \l_@@_anchor_level_tl {s} { \fp_set:Nn \l_@@_anchor_y_fp {0} } { \tl_if_eq:VnTF \l_@@_anchor_level_tl {e} { \fp_set:Nn \l_@@_anchor_y_fp { -1 * \l_@@_length_int * \@@_dim_value:n {height} - sign(\@@_dim_value:n {height}) * 2 * \@@_dim_value:n {border~ height} } } { \fp_set:Nn \l_@@_anchor_y_fp { -1 * \l_@@_anchor_level_tl * \@@_dim_value:n {height} - sign(\@@_dim_value:n {height}) * \@@_dim_value:n {border~ height} } } } } \begin{scope}[ shift={ (\fp_to_decimal:N \l_@@_anchor_x_fp pt, \fp_to_decimal:N \l_@@_anchor_y_fp pt ) } ] % \end{macrocode} % Initialise a prop for the individual strands. % \begin{macrocode} \prop_clear:N \l_@@_strands_prop % \end{macrocode} % Initialise some lengths. % \begin{macrocode} \fp_zero:N \l_@@_height_fp \fp_zero:N \l_@@_nudge_fp \fp_zero:N \l_@@_control_fp % \end{macrocode} % This holds our current \Verb+height+ of our strands. % \begin{macrocode} \fp_set:Nn \l_@@_height_fp { sign(\@@_dim_value:n {height}) * \@@_dim_value:n {border~ height} } % \end{macrocode} % This holds the total \Verb+width+ of our strands. % \begin{macrocode} \fp_set:Nn \l_@@_width_fp { (\l_@@_strands_int - 1) * \@@_dim_value:n {width} + 2 * sign(\@@_dim_value:n{width}) * \@@_dim_value:n {floor~ border} } % \end{macrocode} % Each crossing actually starts a little bit into the crossing space, as defined by the \Verb+nudge factor+. % \begin{macrocode} \fp_set:Nn \l_@@_nudge_fp { \@@_value:n {nudge~ factor} * \@@_dim_value:n {height} } % \end{macrocode} % This sets where the control points for the crossing curves will be. % \begin{macrocode} \fp_set:Nn \l_@@_control_fp { \@@_value:n {control~ factor} * \@@_dim_value:n {height} } \fp_sub:Nn \l_@@_control_fp {\l_@@_nudge_fp} % \end{macrocode} % Initialise our strand paths with a \Verb+\draw+. % \begin{macrocode} \int_step_inline:nnnn {1} {1} {\l_@@_strands_int} { \prop_get:NnN \l_@@_inverse_prop {##1} \l_@@_tmpa_tl \prop_put:Nnx \l_@@_strands_prop {##1} { \exp_not:N \draw[ braid/every~ strand/.try, braid/strand~ ##1/.try ] \exp_not:N \@@_moveto:nn { \fp_eval:n {(##1 - 1) * \@@_dim_value:n {width} } } {0} \exp_not:N \@@_lineto:nn { \fp_eval:n {(##1 - 1) * \@@_dim_value:n {width} } } { \fp_to_decimal:N \l_@@_height_fp} } % \end{macrocode} % Add a load of coordinates at the start of each strand, indexed by both forward and backward strand numbers. % \begin{macrocode} \@@_coordinate:xxxx {-##1-s} {-rev-\l_@@_tmpa_tl-s} {\fp_eval:n {(##1 - 1) * \@@_dim_value:n {width} }} {0} \@@_coordinate:xxxx {-##1-0} {-rev-\l_@@_tmpa_tl-0} {\fp_eval:n {(##1 - 1) * \@@_dim_value:n {width} }} { \fp_to_decimal:N \l_@@_height_fp} } % \end{macrocode} % % Run through any extra floors requested. % \begin{macrocode} \seq_map_inline:Nn \l_@@_floors_seq { \tl_set:Nx \l_@@_tmpa_tl {\clist_item:nn {##1} {5}} \@@_do_floor:Vxxxx \l_@@_tmpa_tl {\fp_eval:n { -1*sign(\@@_dim_value:n{width}) * \@@_dim_value:n {floor~ border} + (\@@_dim_value:n {width}) * (\clist_item:nn {##1} {1} - 1) } pt } {\fp_eval:n { \l_@@_height_fp + ( \@@_dim_value:n {height} ) * (\clist_item:nn {##1} {2}) } pt } {\fp_eval:n { ( (\clist_item:nn {##1} {3}) * \@@_dim_value:n {width} + 2 * sign(\@@_dim_value:n{width}) * \@@_dim_value:n {floor~ border} ) / \dim_to_fp:n {1cm} } } {\fp_eval:n { (\clist_item:nn {##1} {4}) * ( \@@_dim_value:n {height} ) / \dim_to_fp:n {1cm} } } } % \end{macrocode} % % Keep track of the crossing level for the floor. % \begin{macrocode} \int_zero:N \l_@@_crossing_int \int_incr:N \l_@@_crossing_int \seq_map_inline:Nn \l_@@_word_seq { % \end{macrocode} % Clear the flags for this segment of the braid word % \begin{macrocode} \seq_clear:N \l_@@_crossing_seq \bool_set_true:N \l_@@_step_level_bool \bool_set_false:N \l_@@_floor_bool \bool_set_false:N \l_@@_swap_crossing_bool ##1 % \end{macrocode} % If we're drawing a floor, do so straightaway. % \begin{macrocode} \bool_if:NT \l_@@_floor_bool { \@@_do_floor:Vxxxx \l_@@_crossing_int {\fp_eval:n { -1*sign(\@@_dim_value:n{width}) * \@@_dim_value:n {floor~ border} } pt } {\fp_to_decimal:N \l_@@_height_fp pt} {\fp_eval:n { \l_@@_width_fp / \dim_to_fp:n {1cm} }} {\fp_eval:n { ( \@@_dim_value:n {height} ) / \dim_to_fp:n {1cm}}} } % \end{macrocode} % If we have a crossing, process it. % \begin{macrocode} \seq_if_empty:NF \l_@@_crossing_seq { \int_set:Nn \l_@@_crossing_long_int { % \seq_item:Nn \l_@@_crossing_seq {\seq_count:N \l_@@_crossing_seq} \seq_item:Nn \l_@@_crossing_seq {1} } \int_set:Nn \l_@@_crossing_start_int { \int_min:nn { \seq_item:Nn \l_@@_crossing_seq {1} } { \seq_item:Nn \l_@@_crossing_seq {\seq_count:N \l_@@_crossing_seq} } } \int_set:Nn \l_@@_crossing_end_int { \int_max:nn { \seq_item:Nn \l_@@_crossing_seq {1} } { \seq_item:Nn \l_@@_crossing_seq {\seq_count:N \l_@@_crossing_seq} } } \int_set:Nn \l_@@_crossing_width_int { \l_@@_crossing_end_int - \l_@@_crossing_start_int } % \end{macrocode} % Step through the crossing % \begin{macrocode} \int_step_inline:nnn {2} {\seq_count:N \l_@@_crossing_seq} { \bool_if:NTF \l_@@_default_crossing_bool { \int_set:Nn \l_@@_tmpa_int {####1} \int_set:Nn \l_@@_tmpb_int {####1 - 1} } { \int_set:Nn \l_@@_tmpa_int {####1 - 1} \int_set:Nn \l_@@_tmpb_int {####1} } % \end{macrocode} % Keep track of the current permutation. % \begin{macrocode} \prop_get:NxN \l_@@_crossing_permutation_prop {\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpa_int}} \l_@@_tmpa_tl \prop_get:NxN \l_@@_crossing_permutation_prop {\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpb_int}} \l_@@_tmpb_tl \prop_put:NxV \l_@@_crossing_permutation_prop {\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpb_int}} \l_@@_tmpa_tl \prop_put:NxV \l_@@_crossing_permutation_prop {\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpa_int}} \l_@@_tmpb_tl % \end{macrocode} % Now get the strands corresponding to the ones involved in the crossing. % \begin{macrocode} \prop_get:NxN \l_@@_strands_prop {\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpa_int}} \l_@@_tmpa_tl \prop_get:NxN \l_@@_strands_prop {\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpb_int}} \l_@@_tmpb_tl % \end{macrocode} % The over-strand is easy as that's a single curve. % \begin{macrocode} % \int_set:Nn \l_@@_crossing_start_factor_int {1} % \int_set:Nn \l_@@_crossing_end_factor_int {1} % \int_compare:nT { % \seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpa_int} % = % \l_@@_crossing_long_int % } % { \int_set:Nn \l_@@_crossing_start_factor_int {0} \int_set:Nn \l_@@_crossing_end_factor_int {0} \int_compare:nT { ####1 = \seq_count:N \l_@@_crossing_seq } { \int_set:Nn \l_@@_crossing_end_factor_int {1} } \int_compare:nT { ####1 = 2 } { \int_set:Nn \l_@@_crossing_start_factor_int {1} } % } \tl_put_right:Nx \l_@@_tmpa_tl { \exp_not:N \@@_lineto:nn {\fp_eval:n { (\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpa_int} - 1) * \@@_dim_value:n {width} } } {\fp_eval:n { \l_@@_height_fp + \l_@@_nudge_fp * \l_@@_crossing_start_factor_int + \@@_dim_value:n {height} * (####1 - 2)/(\seq_count:N \l_@@_crossing_seq - 1) } } \exp_not:N \@@_curveto:nnnnnn {0} {\fp_eval:n { \l_@@_control_fp % * \l_@@_crossing_start_factor_int * 1/(\seq_count:N \l_@@_crossing_seq - 1)}} {0} {\fp_eval:n {- \l_@@_control_fp % * \l_@@_crossing_end_factor_int * 1/(\seq_count:N \l_@@_crossing_seq - 1)}} {\fp_eval:n { (\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpb_int} - 1) * \@@_dim_value:n {width} } } {\fp_eval:n { \l_@@_height_fp + \@@_dim_value:n {height} * (####1 - 1)/(\seq_count:N \l_@@_crossing_seq - 1) - \l_@@_nudge_fp * \l_@@_crossing_end_factor_int } } } % \end{macrocode} % The under-strand is a bit more complicated as we need to break it in the middle. % \begin{macrocode} % \int_set:Nn \l_@@_crossing_start_factor_int {1} % \int_set:Nn \l_@@_crossing_end_factor_int {1} % \int_compare:nT { % \seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpb_int} % = % \l_@@_crossing_long_int % } % { \int_set:Nn \l_@@_crossing_start_factor_int {0} \int_set:Nn \l_@@_crossing_end_factor_int {0} \int_compare:nT { ####1 = \seq_count:N \l_@@_crossing_seq } { \int_set:Nn \l_@@_crossing_end_factor_int {1} } \int_compare:nT { ####1 = 2 } { \int_set:Nn \l_@@_crossing_start_factor_int {1} } % } \tl_put_right:Nx \l_@@_tmpb_tl { \exp_not:N \@@_lineto:nn {\fp_eval:n { (\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpb_int} - 1) * \@@_dim_value:n {width} } } {\fp_eval:n { \l_@@_height_fp + \l_@@_nudge_fp * \l_@@_crossing_start_factor_int + \@@_dim_value:n {height} * (####1 - 2)/(\seq_count:N \l_@@_crossing_seq - 1) } } \exp_not:N \@@_curveto:nnnnnn {0} { \fp_eval:n { \l_@@_control_fp * (.5 - \@@_value:n {gap} * (\seq_count:N \l_@@_crossing_seq - 1) ) * 1/(\seq_count:N \l_@@_crossing_seq - 1) % * \l_@@_crossing_start_factor_int } } { \fp_eval:n { - (.5 - \@@_value:n {gap} * (\seq_count:N \l_@@_crossing_seq - 1) ) / 3 * \@@_bezier_tangent:nnnnn {.5 - \@@_value:n {gap} * (\seq_count:N \l_@@_crossing_seq - 1) } {0} {0} { (\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpa_int} - \seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpb_int}) * \@@_dim_value:n {width} } { (\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpa_int} - \seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpb_int}) * \@@_dim_value:n {width} } } } { \fp_eval:n { -(.5 - \@@_value:n {gap} * (\seq_count:N \l_@@_crossing_seq - 1) ) / 3 * \@@_bezier_tangent:nnnnn {.5 - \@@_value:n {gap} * (\seq_count:N \l_@@_crossing_seq - 1) } {0} { \l_@@_control_fp * 1/(\seq_count:N \l_@@_crossing_seq - 1) % * \l_@@_crossing_start_factor_int } { \@@_dim_value:n {height} * 1/(\seq_count:N \l_@@_crossing_seq - 1) - \l_@@_nudge_fp * \l_@@_crossing_start_factor_int - \l_@@_nudge_fp * \l_@@_crossing_end_factor_int - \l_@@_control_fp * 1/(\seq_count:N \l_@@_crossing_seq - 1) % * \l_@@_crossing_end_factor_int } { \@@_dim_value:n {height} * 1/(\seq_count:N \l_@@_crossing_seq - 1) - \l_@@_nudge_fp * \l_@@_crossing_start_factor_int - \l_@@_nudge_fp * \l_@@_crossing_end_factor_int } } } { \fp_eval:n { (\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpb_int} - 1) * \@@_dim_value:n {width} + \@@_bezier_point:nnnnn {.5 - \@@_value:n {gap} * (\seq_count:N \l_@@_crossing_seq - 1) } {0} {0} { (\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpa_int} - \seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpb_int}) * \@@_dim_value:n {width} } { (\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpa_int} - \seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpb_int}) * \@@_dim_value:n {width} } } } { \fp_eval:n { \l_@@_height_fp + \l_@@_nudge_fp * \l_@@_crossing_start_factor_int + \@@_dim_value:n {height} * (####1 - 2)/(\seq_count:N \l_@@_crossing_seq - 1) + \@@_bezier_point:nnnnn {.5 - \@@_value:n {gap} * (\seq_count:N \l_@@_crossing_seq - 1) } {0} { \l_@@_control_fp * 1/(\seq_count:N \l_@@_crossing_seq - 1) % * \l_@@_crossing_start_factor_int } { \@@_dim_value:n {height} * 1/(\seq_count:N \l_@@_crossing_seq - 1) - \l_@@_nudge_fp * \l_@@_crossing_start_factor_int - \l_@@_nudge_fp * \l_@@_crossing_end_factor_int - \l_@@_control_fp * 1/(\seq_count:N \l_@@_crossing_seq - 1) % * \l_@@_crossing_end_factor_int } {\@@_dim_value:n {height} * 1/(\seq_count:N \l_@@_crossing_seq - 1) - \l_@@_nudge_fp * \l_@@_crossing_start_factor_int - \l_@@_nudge_fp * \l_@@_crossing_end_factor_int } } } \exp_not:N \@@_moveto:nn { \fp_eval:n { (\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpb_int} - 1) * \@@_dim_value:n {width} + \@@_bezier_point:nnnnn {.5 + \@@_value:n {gap} * (\seq_count:N \l_@@_crossing_seq - 1) } {0} {0} { (\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpa_int} - \seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpb_int}) * \@@_dim_value:n {width} } { (\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpa_int} - \seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpb_int}) * \@@_dim_value:n {width} } } } { \fp_eval:n { \l_@@_height_fp + \l_@@_nudge_fp * \l_@@_crossing_start_factor_int + \@@_dim_value:n {height} * (####1 - 2)/(\seq_count:N \l_@@_crossing_seq - 1) + \@@_bezier_point:nnnnn {.5 + \@@_value:n {gap} * (\seq_count:N \l_@@_crossing_seq - 1) } {0} { \l_@@_control_fp * 1/(\seq_count:N \l_@@_crossing_seq - 1) % * \l_@@_crossing_start_factor_int } { \@@_dim_value:n {height} * 1/(\seq_count:N \l_@@_crossing_seq - 1) - \l_@@_nudge_fp * \l_@@_crossing_start_factor_int - \l_@@_nudge_fp * \l_@@_crossing_end_factor_int - \l_@@_control_fp * 1/(\seq_count:N \l_@@_crossing_seq - 1) % * \l_@@_crossing_end_factor_int } {\@@_dim_value:n {height} * 1/(\seq_count:N \l_@@_crossing_seq - 1) - \l_@@_nudge_fp * \l_@@_crossing_start_factor_int - \l_@@_nudge_fp * \l_@@_crossing_end_factor_int } } } \exp_not:N \@@_curveto:nnnnnn { \fp_eval:n { (.5 - \@@_value:n {gap} * (\seq_count:N \l_@@_crossing_seq - 1) ) / 3 * \@@_bezier_tangent:nnnnn {.5 + \@@_value:n {gap} * (\seq_count:N \l_@@_crossing_seq - 1) } {0} {0} { (\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpa_int} - \seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpb_int}) * \@@_dim_value:n {width} } { (\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpa_int} - \seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpb_int}) * \@@_dim_value:n {width} } } } { \fp_eval:n { (.5 - \@@_value:n {gap} * (\seq_count:N \l_@@_crossing_seq - 1) ) / 3 * \@@_bezier_tangent:nnnnn {.5 + \@@_value:n {gap} * (\seq_count:N \l_@@_crossing_seq - 1) } {0} { \l_@@_control_fp * 1/(\seq_count:N \l_@@_crossing_seq - 1) % * \l_@@_crossing_start_factor_int } { \@@_dim_value:n {height} * 1/(\seq_count:N \l_@@_crossing_seq - 1) - \l_@@_nudge_fp * \l_@@_crossing_start_factor_int - \l_@@_nudge_fp * \l_@@_crossing_end_factor_int - \l_@@_control_fp * 1/(\seq_count:N \l_@@_crossing_seq - 1) % * \l_@@_crossing_end_factor_int } {\@@_dim_value:n {height} * 1/(\seq_count:N \l_@@_crossing_seq - 1) - \l_@@_nudge_fp * \l_@@_crossing_start_factor_int - \l_@@_nudge_fp * \l_@@_crossing_end_factor_int } } } {0} {\fp_eval:n { - \l_@@_control_fp * (.5 - \@@_value:n {gap} * (\seq_count:N \l_@@_crossing_seq - 1) ) % * \l_@@_crossing_end_factor_int * 1/(\seq_count:N \l_@@_crossing_seq - 1)} } {\fp_eval:n { (\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpa_int} - 1) * \@@_dim_value:n {width} } } {\fp_eval:n { \l_@@_height_fp + \@@_dim_value:n {height} * (####1 - 1)/(\seq_count:N \l_@@_crossing_seq - 1) - \l_@@_nudge_fp * \l_@@_crossing_end_factor_int } } } % \end{macrocode} % Now put those new strands back in the prop. % \begin{macrocode} \prop_put:NxV \l_@@_strands_prop {\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpb_int}} \l_@@_tmpa_tl \prop_put:NxV \l_@@_strands_prop {\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpa_int}} \l_@@_tmpb_tl % \end{macrocode} % If the strands are more than one apart, the intermediate strands need to be broken as well. % \begin{macrocode} \int_compare:nT { \int_max:nn { \seq_item:Nn \l_@@_crossing_seq {####1 - 1} } { \seq_item:Nn \l_@@_crossing_seq {####1} } - \int_min:nn { \seq_item:Nn \l_@@_crossing_seq {####1 - 1} } { \seq_item:Nn \l_@@_crossing_seq {####1} } > 1 } { \int_step_inline:nnnn { \int_min:nn { \seq_item:Nn \l_@@_crossing_seq {####1 - 1} } { \seq_item:Nn \l_@@_crossing_seq {####1} } + 1} {1} { \int_max:nn { \seq_item:Nn \l_@@_crossing_seq {####1 - 1} } { \seq_item:Nn \l_@@_crossing_seq {####1} } - 1 } { \prop_get:NnN \l_@@_strands_prop {########1} \l_@@_tmpa_tl \tl_put_right:Nx \l_@@_tmpa_tl { \exp_not:N \@@_lineto:nn {\fp_eval:n {(########1 - 1) * \@@_dim_value:n {width} }} {\fp_eval:n { \l_@@_height_fp + \l_@@_nudge_fp + .5 * \l_@@_control_fp / (\seq_count:N \l_@@_crossing_seq - 1) + \@@_dim_value:n {height} * (####1 - 2)/(\seq_count:N \l_@@_crossing_seq - 1) } } \exp_not:N \@@_moveto:nn {\fp_eval:n {(########1 - 1) * \@@_dim_value:n {width} }} {\fp_eval:n { \l_@@_height_fp - \l_@@_nudge_fp - .5 * \l_@@_control_fp / (\seq_count:N \l_@@_crossing_seq - 1) + \@@_dim_value:n {height} * (####1 - 1)/(\seq_count:N \l_@@_crossing_seq - 1) } } } \prop_put:NnV \l_@@_strands_prop {########1} \l_@@_tmpa_tl } } % \end{macrocode} % Reset the current long % \begin{macrocode} \int_compare:nTF { \seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpa_int} = \l_@@_crossing_long_int } { \int_set:Nn \l_@@_crossing_long_int {\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpb_int}} } { \int_compare:nT { \seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpb_int} = \l_@@_crossing_long_int } { \int_set:Nn \l_@@_crossing_long_int {\seq_item:Nn \l_@@_crossing_seq {\l_@@_tmpa_int}} } } % \end{macrocode} % \begin{macrocode} } } % \end{macrocode} % If we're to step the level, increase the height and add a load of coordinates. % \begin{macrocode} \bool_if:NT \l_@@_step_level_bool { \fp_add:Nn \l_@@_height_fp { \@@_dim_value:n {height} } \int_step_inline:nnnn {1} {1} {\l_@@_strands_int} { \prop_get:NnN \l_@@_crossing_permutation_prop {####1} \l_@@_tmpb_tl \prop_get:NVN \l_@@_inverse_prop \l_@@_tmpb_tl \l_@@_tmpa_tl \@@_coordinate:xxxx {-\l_@@_tmpb_tl-\int_use:N \l_@@_crossing_int} {-rev-\l_@@_tmpa_tl-\int_use:N \l_@@_crossing_int } {\fp_eval:n { (####1 - 1) * \@@_dim_value:n {width} }} {\fp_to_decimal:N \l_@@_height_fp} } \int_incr:N \l_@@_crossing_int } } \fp_add:Nn \l_@@_height_fp { sign(\@@_dim_value:n {height}) * \@@_dim_value:n {border~ height} } % \end{macrocode} % Add a little bit to the end of each strand, together with some coordinates. % \begin{macrocode} \int_step_inline:nnnn {1} {1} {\l_@@_strands_int} { \prop_get:NxN \l_@@_strands_prop {##1} \l_@@_tmpa_tl \prop_get:NxN \l_@@_permutation_prop {##1} \l_@@_tmpb_tl \tl_put_right:Nx \l_@@_tmpa_tl { \exp_not:N \@@_lineto:nn {\fp_eval:n { (##1 - 1) * \@@_dim_value:n {width} }} {\fp_to_decimal:N \l_@@_height_fp} coordinate (-rev-##1-e) coordinate (-\l_@@_tmpb_tl-e) ; } \prop_put:NnV \l_@@_strands_prop {##1} \l_@@_tmpa_tl } % \end{macrocode} % This is where we actually carry out the drawing commands. % \begin{macrocode} \int_step_inline:nnnn {1} {1} {\l_@@_strands_int} { \prop_get:NnN \l_@@_strands_prop {##1} \l_@@_tmpa_tl \tl_use:N \l_@@_tmpa_tl } % \end{macrocode} % Finally, put a node around the whole braid if it's been named % \begin{macrocode} \tl_if_empty:cF {tikz@fig@name} { \tl_gset:cn {pgf@sh@ns@ \tl_use:c{tikz@fig@name} }{rectangle} \tl_gset:cx {pgf@sh@np@ \tl_use:c{tikz@fig@name} }{% \exp_not:N\def \exp_not:N\southwest { \exp_not:N\pgfqpoint { \fp_to_dim:n { min(0, (\l_@@_strands_int - 1) * (\@@_dim_value:n {width}) ) } } { \fp_to_dim:n { min(0, \l_@@_length_int * (\@@_dim_value:n {height}) + 2 * sign(\@@_dim_value:n {height}) * \@@_dim_value:n {border~ height} ) } } } \exp_not:N\def \exp_not:N\northeast { \exp_not:N\pgfqpoint { \fp_to_dim:n { max(0, (\l_@@_strands_int - 1) * (\@@_dim_value:n {width}) ) } } { \fp_to_dim:n { max(0, \l_@@_length_int * (\@@_dim_value:n {height}) + 2 * sign(\@@_dim_value:n {height}) * \@@_dim_value:n {border~ height} ) } } } }% \pgfgettransform\l_@@_tmpa_tl \tl_gset:cV {pgf@sh@nt@ \tl_use:c{tikz@fig@name} } \l_@@_tmpa_tl \tl_gset:cV {pgf@sh@pi@ \tl_use:c{tikz@fig@name} } \pgfpictureid } \end{scope} } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_moveto:nn, \@@_lineto:nn, \@@_curveto:nnnnnn, \@@_coordinate:nnnn} % These are our interfaces to the TikZ code. % \begin{macrocode} \cs_new_nopar:Npn \@@_moveto:nn #1#2 { (#1 pt, #2 pt) } \cs_new_nopar:Npn \@@_lineto:nn #1#2 { -- (#1 pt, #2 pt) } \cs_new_nopar:Npn \@@_curveto:nnnnnn #1#2#3#4#5#6 { % -- +(5 pt, 0) -- +(0 pt, 0pt) % -- +(#1 pt, #2 pt) -- (#5 pt + #3 pt, #6 pt + #4 pt) -- (#5 pt, #6 pt) .. controls +(#1 pt, #2 pt) and +(#3 pt, #4 pt) .. (#5 pt, #6 pt) } \cs_new_nopar:Npn \@@_coordinate:nnnn #1#2#3#4 { \coordinate[alias=#2] (#1) at (#3 pt,#4 pt); } \cs_generate_variant:Nn \@@_coordinate:nnnn {xxxx} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\@@_bezier_point:nnnnn, \@@_bezier_tangent:nnnnn} % Used to calculate intermediate points and tangents on a bezier curve. % \begin{macrocode} \cs_new_nopar:Npn \@@_bezier_point:nnnnn #1#2#3#4#5 { \fp_eval:n { (1 - (#1)) * (1 - (#1)) * (1 - (#1)) * (#2) + 3 * (1 - (#1)) * (1 - (#1)) * (#1) * (#3) + 3 * (1 - (#1)) * (#1) * (#1) * (#4) + (#1) * (#1) * (#1) * (#5) } } \cs_new_nopar:Npn \@@_bezier_tangent:nnnnn #1#2#3#4#5 { \fp_eval:n { 3 * (1 - (#1)) * (1 - (#1)) * (#3 - (#2)) + 6 * (1 - (#1)) * (#1) * (#4 - (#3)) + 3 * (#1) * (#1) * (#5 - (#4)) } } \cs_new_nopar:Npn \@@_do_floor:nnnnn #1#2#3#4#5 { \pic[pic~ type=floor, xscale=#4, yscale=#5, at={(#2,#3)}, braid/every~ floor/.try, braid/floor~#1/.try, ]; } \cs_generate_variant:Nn \@@_do_floor:nnnnn {Vxxxx} % \end{macrocode} % \end{macro} % \begin{macrocode} \ExplSyntaxOff % \end{macrocode} % % \iffalse % % \fi % % \Finale \endinput