From a0ec6c4d3c2dc5ee28fce9fff1c114a2cccfe2dd Mon Sep 17 00:00:00 2001
From: Karl Berry <karl@freefriends.org>
Date: Sun, 20 Jul 2014 22:17:56 +0000
Subject: celtic (20jul14)

git-svn-id: svn://tug.org/texlive/trunk@34676 c570f23f-e606-0410-a88d-b1316a301751
---
 Master/texmf-dist/source/latex/celtic/celtic.dtx | 1059 ++++++++++++++++++++++
 1 file changed, 1059 insertions(+)
 create mode 100644 Master/texmf-dist/source/latex/celtic/celtic.dtx

(limited to 'Master/texmf-dist/source/latex/celtic/celtic.dtx')

diff --git a/Master/texmf-dist/source/latex/celtic/celtic.dtx b/Master/texmf-dist/source/latex/celtic/celtic.dtx
new file mode 100644
index 00000000000..b5e6a92232b
--- /dev/null
+++ b/Master/texmf-dist/source/latex/celtic/celtic.dtx
@@ -0,0 +1,1059 @@
+% \iffalse meta-comment
+%<*internal>
+\iffalse
+%</internal>
+%<*readme>
+----------------------------------------------------------------
+celtic --- TikZ library for drawing Celtic knots
+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 is for the generation of Celtic knots starting from
+a grid of walls.
+%</readme>
+%<*internal>
+\fi
+\def\nameofplainTeX{plain}
+\ifx\fmtname\nameofplainTeX\else
+  \expandafter\begingroup
+\fi
+%</internal>
+%<*install>
+\input docstrip.tex
+\keepsilent
+\askforoverwritefalse
+\preamble
+----------------------------------------------------------------
+celtic --- TikZ library for producing Celtic knots.
+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) 2014 by Andrew Stacey <loopspace@mathforge.org>
+
+This work may be distributed and/or modified under the
+conditions of the LaTeX Project Public License (LPPL), either
+version 1.3c of this license or (at your option) any later
+version.  The latest version of this license is in the file:
+
+http://www.latex-project.org/lppl.txt
+
+This work is "maintained" (as per LPPL maintenance status) by
+Andrew Stacey.
+
+This work consists of the files  celtic.dtx
+                                 celtic_doc.tex
+and the derived files            celtic.ins
+                                 celtic_code.pdf
+                                 tikzlibraryceltic.code.tex
+                                 celtic.pdf
+                                 README
+
+\endpostamble
+\usedir{tex/latex/celtic}
+\generate{
+  \file{tikzlibraryceltic.code.tex}{\from{\jobname.dtx}{library}}
+}
+%</install>
+%<install>\endbatchfile
+%<*internal>
+\usedir{source/latex/celtic}
+\generate{
+  \file{\jobname.ins}{\from{\jobname.dtx}{install}}
+}
+\nopreamble\nopostamble
+\usedir{doc/latex/celtic}
+\generate{
+  \file{README.txt}{\from{\jobname.dtx}{readme}}
+}
+\ifx\fmtname\nameofplainTeX
+  \expandafter\endbatchfile
+\else
+  \expandafter\endgroup
+\fi
+%</internal>
+%<*driver>
+\documentclass[full]{l3doc}
+\usepackage[T1]{fontenc}
+\usepackage{lmodern}
+\usepackage{tikz}
+\usepackage{trace}
+\usetikzlibrary{celtic}
+%\traceoff
+%\usepackage[numbered]{hypdoc}
+\definecolor{lstbgcolor}{rgb}{0.9,0.9,0.9} 
+ 
+\usepackage{listings}
+\lstloadlanguages{[LaTeX]TeX}
+\lstset{breakatwhitespace=true,breaklines=true,language=TeX}
+ 
+\usepackage{fancyvrb}
+
+\newenvironment{example}
+  {\VerbatimEnvironment
+   \begin{VerbatimOut}[gobble=2]{example.out}}
+  {\end{VerbatimOut}
+   \begin{center}
+%   \setlength{\parindent}{0pt}
+   \fbox{\begin{minipage}{.9\linewidth}
+     \lstset{breakatwhitespace=true,breaklines=true,language=TeX,basicstyle=\small}
+     \lstinputlisting[]{example.out}
+   \end{minipage}}
+
+   \fbox{\begin{minipage}{.9\linewidth}
+     \input{example.out}
+   \end{minipage}}
+\end{center}
+}
+\EnableCrossrefs
+\CodelineIndex
+\RecordChanges
+\begin{document}
+  \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \CheckSum{716}
+%
+% \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}{2014/05/23}{Converted to DTX file}
+%
+% \DoNotIndex{\newcommand,\newenvironment}
+%
+% \providecommand*{\url}{\texttt}
+% \title{The \textsf{celtic} package}
+% \author{Andrew Stacey \\ \url{loopspace@mathforge.org}}
+% \date{1.0 from 2014/05/23}
+%
+%
+% \maketitle
+%
+% 
+% \section{Introduction}
+%
+% This is a TikZ library for drawing Celtic knot diagrams.
+% For user documentation, see the \Verb+celtic.pdf+ file.
+%
+% \StopEventually{}
+%
+% \section{Implementation}
+%
+% \iffalse
+%<*library>
+% \fi
+% \subsection{Initialisation}
+%
+% Load the \LaTeX3 basics ...
+%    \begin{macrocode}
+\usepackage{expl3}
+\usepackage{xparse}
+%    \end{macrocode}
+% ... and enter the Realm of the 3rd \LaTeX.
+%    \begin{macrocode}
+\ExplSyntaxOn
+%    \end{macrocode}
+% Wrapper around \Verb+\tikz@scan@one@point+ for the \Verb+add=<coord>+ key.
+%    \begin{macrocode}
+\cs_new_nopar:Npn \celtic_shift:n #1
+{
+  \use:c{tikz@scan@one@point}\pgftransformshift #1\relax
+}
+%    \end{macrocode}
+%
+% We need one or two variables ...
+%    \begin{macrocode}
+\int_new:N \l__celtic_max_steps_int
+\int_new:N \l__celtic_int
+\int_new:N \l__celtic_flip_int
+\int_new:N \l__celtic_width_int
+\int_new:N \l__celtic_height_int
+\int_new:N \l__celtic_x
+\int_new:N \l__celtic_y
+\int_new:N \l__celtic_dx
+\int_new:N \l__celtic_dy
+\int_new:N \l__celtic_ox
+\int_new:N \l__celtic_oy
+\int_new:N \l__celtic_lout
+\int_new:N \l__celtic_cross_int
+\int_new:N \l__celtic_component_int
+\fp_new:N \l__celtic_clip_fp
+\fp_new:N \l__celtic_inner_clip_fp
+\fp_new:N \l__celtic_inner_fp
+\fp_new:N \l__celtic_outer_fp
+\seq_new:N \l__celtic_path_seq
+\seq_new:N \l__celtic_component_seq
+\seq_new:N \l__celtic_crossing_seq
+\seq_new:N \l__celtic_tmpa_seq
+\clist_new:N \l__celtic_tmpa_clist
+\tl_new:N \l__celtic_tmpa_tl
+\tl_new:N \l__celtic_path_tl
+\tl_new:N \g__celtic_colon_tl
+\tl_new:N \l__celtic_bar_tl
+\tl_new:N \l__celtic_active_bar_tl
+\bool_new:N \l__celtic_bounce_bool
+\bool_new:N \l__celtic_pbounce_bool
+%    \end{macrocode}
+% Define our warning message.
+%    \begin{macrocode}
+\msg_new:nnnn { celtic } { max~ steps } { Limit~ of~ number~ of~ steps~ exceeded~ \msg_line_context:.}
+{ Paths~ may~ not~ be~ correctly~ constructed.~
+Consider~ raising~ the~ limit~ from  \int_use:N \l__celtic_max_steps_int.}
+%    \end{macrocode}
+% Using a colon for a range separator was possibly not the best idea I ever had, seeing as \LaTeX3 alters its catcode.
+% So we need to get creative.
+%    \begin{macrocode}
+\group_begin:
+\char_set_lccode:nn {`;}{`:}
+\tl_to_lowercase:n {
+\group_end:
+  \tl_set:Nn \g__celtic_colon_tl {;}
+}
+%    \end{macrocode}
+% Some packages mess with the catcode of \Verb+|+.
+%    \begin{macrocode}
+\tl_set:Nn \l__celtic_bar_tl {|}
+\group_begin:
+\char_set_catcode_active:N \|
+\tl_gset:Nn \l__celtic_active_bar_tl {|}
+\group_end:
+%    \end{macrocode}
+% We need a few variants of standard \LaTeX3 functions.
+%    \begin{macrocode}
+\cs_generate_variant:Nn \tl_if_single_p:N {c}
+\cs_generate_variant:Nn \tl_if_single:NTF {cTF}
+\cs_generate_variant:Nn \tl_if_eq:nnTF {xnTF}
+\cs_generate_variant:Nn \tl_head:N {c}
+\cs_generate_variant:Nn \tl_tail:N {c}
+\cs_generate_variant:Nn \tl_if_eq:nnTF {vnTF}
+\cs_generate_variant:Nn \tl_if_in:nnTF {nVTF}
+%    \end{macrocode}
+% Initialise a few variables.
+%    \begin{macrocode}
+\int_set:Nn \l__celtic_max_steps_int {20}
+\fp_set:Nn \l__celtic_inner_fp {1}
+\fp_set:Nn \l__celtic_outer_fp {2}
+%    \end{macrocode}
+%
+% The following functions are for parsing and setting the crossing information.
+% \begin{macro}{\celtic_do_crossing:nnn}
+% This function sets the information for a particular crossing.
+% The first argument can be empty, meaning ``ignore this crossing as a starting point'', or it should be one of \Verb+|+ or \Verb+-+ to denote the wall type that is placed at this crossing.
+%    \begin{macrocode}
+\cs_new_nopar:Npn \celtic_do_crossing:nnn #1#2#3
+{
+  \tl_if_empty:nTF {#1}
+  {
+    \tl_clear:c {crossing used \int_eval:n {#2} - \int_eval:n {#3}}
+  }
+  {
+    \tl_set:cn {crossing \int_eval:n {#2} - \int_eval:n{#3}}{#1}
+  }
+}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\celtic_maybe_symmetric:nnnn}
+% If a crossing is designated as symmetric, we repeat the action four times.
+% This macro tests to see if it is symmetric or not and acts accordingly.
+%    \begin{macrocode}
+\cs_new_nopar:Npn \celtic_maybe_symmetric:nnnn #1#2#3#4
+{
+  \tl_if_empty:nTF {#1}
+  {
+    \celtic_do_crossing:nnn {#2}{#3}{#4}
+  }
+  {
+    \celtic_do_crossing:nnn {#2}{#3}{#4}
+    \celtic_do_crossing:nnn {#2}{\l__celtic_width_int - #3}{#4}
+    \celtic_do_crossing:nnn {#2}{#3}{\l__celtic_height_int - #4}
+    \celtic_do_crossing:nnn {#2}{\l__celtic_width_int - #3}{\l__celtic_height_int - #4}
+  }
+}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\celtic_maybe_xrange:nnnn}
+% The \Verb+x+-coordinate might be a range.
+% If it is, it contains a colon (with the normal catcode).
+% So we test for a colon and act accordingly.
+%    \begin{macrocode}
+\cs_new_nopar:Npn \celtic_maybe_xrange:nnnn #1#2#3#4
+{
+  \tl_if_in:nVTF {#3} \g__celtic_colon_tl
+  {
+    \celtic_do_xrange:w {#1}{#2}#3\q_stop{#4}
+  }
+  {
+    \celtic_maybe_yrange:nnnn {#1}{#2}{#3}{#4}
+  }
+}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\celtic_maybe_yrange:nnnn}
+% Same with the \Verb+y+-coordinate.
+%    \begin{macrocode}
+\cs_new_nopar:Npn \celtic_maybe_yrange:nnnn #1#2#3#4
+{
+  \tl_if_in:nVTF {#4} \g__celtic_colon_tl
+  {
+    \celtic_do_yrange:w {#1}{#2}{#3}#4\q_stop
+  }
+  {
+    \celtic_maybe_symmetric:nnnn {#1}{#2}{#3}{#4}
+  }
+}
+%    \end{macrocode}
+% \end{macro}
+%
+% When processing ranges, we need to use colons with the original catcode.
+% We've stored one in \Verb+\g__celtic_colon_tl+ but we need to use it in actuality.
+% So we make a token list containing the definitions we want to make, expanding \Verb+\g__celtic_colon_tl+ to its colon, but not expanding anything else.
+%    \begin{macrocode}
+\tl_set:Nx \l_tmpa_tl
+{
+%    \end{macrocode}
+%
+% \begin{macro}{\celtic_do_xrange:w}
+% This splits the \Verb+x+-coordinate into a range and repeats the function for each intermediate value.
+%    \begin{macrocode}
+  \exp_not:N \cs_new_nopar:Npn \exp_not:N \celtic_do_xrange:w ##1##2##3\tl_use:N \g__celtic_colon_tl ##4\exp_not:N \q_stop##5
+  {
+    \exp_not:N \int_step_inline:nnnn {##3} {2} {##4}
+    {
+      \exp_not:N \celtic_maybe_yrange:nnnn {##1}{##2} {####1}{##5}
+    }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\celtic_do_yrange:w}
+% Same, for the \Verb+y+-coordinate.
+%    \begin{macrocode}
+  \exp_not:N \cs_new_nopar:Npn \exp_not:N \celtic_do_yrange:w ##1##2##3##4\tl_use:N \g__celtic_colon_tl ##5\exp_not:N \q_stop
+  {
+    \exp_not:N \int_step_inline:nnnn {##4} {2} {##5}
+    {
+      \exp_not:N \celtic_maybe_symmetric:nnnn {##1}{##2}{##3}{####1}
+    }
+  }
+}
+%    \end{macrocode}
+% \end{macro}
+% 
+% Now we use the above token list to make our definitions with the right colon in them.
+%    \begin{macrocode}
+\tl_use:N \l_tmpa_tl
+%    \end{macrocode}
+%
+% The next functions are those that take the individual crossing specifications from the key/value list and begin the process of converting the data to an action to be taken for a specific crossing.
+% \begin{macro}{\celtic_ignore_crossings:w}
+%    \begin{macrocode}
+\cs_new_nopar:Npn \celtic_ignore_crossings:w #1,#2\q_stop
+{
+  \celtic_maybe_xrange:nnnn {}{}{#1}{#2}
+}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\celtic_ignore_symmetric_crossings:w}
+%    \begin{macrocode}
+\cs_new_nopar:Npn \celtic_ignore_symmetric_crossings:w #1,#2\q_stop
+{
+  \celtic_maybe_xrange:nnnn {s}{}{#1}{#2}
+}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\celtic_set_crossings:w}
+%    \begin{macrocode}
+\cs_new_nopar:Npn \celtic_set_crossings:w #1,#2,#3\q_stop
+{
+  \celtic_maybe_xrange:nnnn {}{#3}{#1}{#2}
+}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\celtic_set_symmetric_crossings:w}
+%    \begin{macrocode}
+\cs_new_nopar:Npn \celtic_set_symmetric_crossings:w #1,#2,#3\q_stop
+{
+  \celtic_maybe_xrange:nnnn {s}{#3}{#1}{#2}
+}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\celtic_next_crossing:}
+% This is the function that does all the work.
+% Starting from an undercrossing, it computes the segment leading to the next undercrossing working out all of the ``bounces'' on the way.
+%    \begin{macrocode}
+\cs_new_nopar:Npn \celtic_next_crossing:
+{
+%    \end{macrocode}
+% Clear our starting conditions.
+%    \begin{macrocode}
+  \int_zero:N \l__celtic_cross_int
+  \tl_clear:N \l__celtic_crossing_tl
+  \tl_clear:N \l__celtic_path_tl
+  \bool_set_false:N \l__celtic_bounce_tl
+%    \end{macrocode}
+% Start our path with a move to the initial point and record our current direction.
+%    \begin{macrocode}
+  \tl_put_right:Nx \l__celtic_path_tl {(\int_use:N \l__celtic_x, \int_use:N \l__celtic_y)}
+  \int_set:Nn \l__celtic_lout {(90 - \l__celtic_dx * 45) * \l__celtic_dy}
+%    \end{macrocode}
+% We loop until we get to the second crossing on the path (the first will be the overpass).
+%    \begin{macrocode}
+  \bool_do_until:nn {\int_compare_p:n {\l__celtic_cross_int > 1}}
+  {
+%    \end{macrocode}
+% We keep a record of whether the last bit contained a bounce.
+%    \begin{macrocode}
+    \bool_set_eq:NN \l__celtic_pbounce_bool \l__celtic_bounce_bool
+    \bool_set_false:N \l__celtic_bounce_bool
+%    \end{macrocode}
+% Move to the next point in our current direction.
+%    \begin{macrocode}
+    \int_add:Nn \l__celtic_x {\l__celtic_dx}
+    \int_add:Nn \l__celtic_y {\l__celtic_dy}
+%    \end{macrocode}
+% Now we look to see if we should bounce.
+% Is the crossing defined?
+%    \begin{macrocode}
+    \tl_if_exist:cT {crossing \int_use:N \l__celtic_x - \int_use:N       \l__celtic_y}
+    {
+%    \end{macrocode}
+% Yes, so we bounce.
+% But which way?
+%    \begin{macrocode}
+      \tl_if_eq:cNTF {crossing \int_use:N \l__celtic_x - \int_use:N \l__celtic_y} \l__celtic_bar_tl
+      {
+%    \end{macrocode}
+% Vertical wall.
+% Have we just bounced?
+%    \begin{macrocode}
+        \bool_if:NTF \l__celtic_pbounce_bool
+        {
+%    \end{macrocode}
+% Yes, so the next part of the path is a right angle.
+%    \begin{macrocode}
+          \tl_put_right:Nn \l__celtic_path_tl { -| }
+        }
+        {
+%    \end{macrocode}
+% No, so the next part of the path is a curve.
+% (This is where we use the direction that we recorded earlier.)
+%    \begin{macrocode}
+          \tl_put_right:Nx \l__celtic_path_tl { to[out=\int_eval:n
+{(90 - 45*\l__celtic_dx)*\l__celtic_dy}, in=\int_eval:n
+{-90*\l__celtic_dy}] }
+        }
+%    \end{macrocode}
+% We record the new direction and ``bounce'' our direction vector.
+% Then we add our new point to the path (which, due to the bounce, is offset).
+%    \begin{macrocode}
+        \int_set:Nn \l__celtic_lout {90*\l__celtic_dy}
+        \int_set:Nn \l__celtic_dx {-\l__celtic_dx}
+        \tl_put_right:Nx  \l__celtic_path_tl {(\fp_eval:n {\int_use:N \l__celtic_x + .5 * \int_use:N \l__celtic_dx}, \int_use:N \l__celtic_y)}
+%    \end{macrocode}
+% We bounced, so record that too.
+%    \begin{macrocode}
+        \bool_set_true:N \l__celtic_bounce_bool
+      }
+      {
+%    \end{macrocode}
+% At this point, we've bounced but our bounce was horizontal so we do the same as for the vertical but all turned round.
+%    \begin{macrocode}
+        \bool_if:NTF \l__celtic_pbounce_bool
+        {
+%    \end{macrocode}
+% We're out from a bounce, so turn at right angles.
+%    \begin{macrocode}
+          \tl_put_right:Nn \l__celtic_path_tl { |- }
+        }
+        {
+%    \end{macrocode}
+% We're not out from a bounce, so we curve ...
+%    \begin{macrocode}
+          \tl_put_right:Nx \l__celtic_path_tl { to[out=\int_eval:n {(90 - 45*\l__celtic_dx)*\l__celtic_dy}, in=\int_eval:n {90 + 90*\l__celtic_dx}] }
+        }
+%    \end{macrocode}
+% ... and record our new direction and out angle.
+%    \begin{macrocode}
+        \int_set:Nn \l__celtic_lout {90-90*\l__celtic_dx}
+        \int_set:Nn \l__celtic_dy {-\l__celtic_dy}
+%    \end{macrocode}
+% Now we add our new position (adjusted from the bounce) to the path.
+%    \begin{macrocode}
+        \tl_put_right:Nx \l__celtic_path_tl {(\int_use:N \l__celtic_x, \fp_eval:n {\int_use:N \l__celtic_y + .5 * \int_use:N \l__celtic_dy})}
+%    \end{macrocode}
+% And record the fact that we've bounced.
+%    \begin{macrocode}
+        \bool_set_true:N \l__celtic_bounce_bool
+      }
+    }
+%    \end{macrocode}
+% Now we check to see if we're at the edge of the rectangle, starting with the left.
+%    \begin{macrocode}
+    \int_compare:nT {\l__celtic_x == 0}
+    {
+%    \end{macrocode}
+% Yes, so treat this as a vertical bounce.
+%    \begin{macrocode}
+      \bool_if:NTF \l__celtic_pbounce_bool
+      {
+%    \end{macrocode}
+% Previous bounce, so right angle.
+%    \begin{macrocode}
+        \tl_put_right:Nn \l__celtic_path_tl { -| }
+      }
+      {
+%    \end{macrocode}
+% No previous bounce, so curve.
+%    \begin{macrocode}
+        \tl_put_right:Nx \l__celtic_path_tl { to[out=\int_eval:n {(90 - 45*\l__celtic_dx)*\l__celtic_dy}, in=\int_eval:n {-90*\l__celtic_dy}] }
+      }
+%    \end{macrocode}
+% Record our out angle and change our direction.
+%    \begin{macrocode}
+      \int_set:Nn \l__celtic_lout {90*\l__celtic_dy}
+      \int_set:Nn \l__celtic_dx {-\l__celtic_dx}
+%    \end{macrocode}
+% Add the correct position to the path.
+%    \begin{macrocode}
+      \tl_put_right:Nx  \l__celtic_path_tl {(\fp_eval:n {\int_use:N \l__celtic_x + .5 * \int_use:N \l__celtic_dx}, \int_use:N \l__celtic_y)}
+%    \end{macrocode}
+% We've bounced.
+%    \begin{macrocode}
+      \bool_set_true:N \l__celtic_bounce_bool
+    }
+%    \end{macrocode}
+% Same for the right-hand edge.
+%    \begin{macrocode}
+    \int_compare:nT {\l__celtic_x == \l__celtic_width_int}
+    {
+      \bool_if:NTF \l__celtic_pbounce_bool
+      {
+        \tl_put_right:Nn \l__celtic_path_tl { -| }
+      }
+      {
+        \tl_put_right:Nx \l__celtic_path_tl { to[out=\int_eval:n {(90 - 45*\l__celtic_dx)*\l__celtic_dy}, in=\int_eval:n {-90*\l__celtic_dy}] }
+      }
+      \int_set:Nn \l__celtic_lout {90*\l__celtic_dy}
+      \int_set:Nn \l__celtic_dx {-\l__celtic_dx}
+      \tl_put_right:Nx \l__celtic_path_tl {(\fp_eval:n {\int_use:N \l__celtic_x + .5 * \int_use:N \l__celtic_dx}, \int_use:N \l__celtic_y)}
+      \bool_set_true:N \l__celtic_bounce_bool
+    }
+%    \end{macrocode}
+% Now the lower edge.
+%    \begin{macrocode}
+    \int_compare:nT {\l__celtic_y == 0}
+    {
+      \bool_if:NTF \l__celtic_pbounce_bool
+      {
+        \tl_put_right:Nn \l__celtic_path_tl { |- }
+      }
+      {
+        \tl_put_right:Nx \l__celtic_path_tl { to[out=\int_eval:n {(90 - 45*\l__celtic_dx)*\l__celtic_dy}, in=\int_eval:n {90 + 90*\l__celtic_dx}] }
+      }
+      \int_set:Nn \l__celtic_lout {90-90*\l__celtic_dx}
+      \int_set:Nn \l__celtic_dy {-\l__celtic_dy}
+      \tl_put_right:Nx \l__celtic_path_tl {(\int_use:N \l__celtic_x, \fp_eval:n {\int_use:N \l__celtic_y + .5 * \int_use:N \l__celtic_dy})}
+      \bool_set_true:N \l__celtic_bounce_bool
+    }
+%    \end{macrocode}
+% And the upper edge.
+%    \begin{macrocode}
+    \int_compare:nT {\l__celtic_y == \l__celtic_height_int}
+    {
+      \bool_if:NTF \l__celtic_pbounce_bool
+      {
+        \tl_put_right:Nn \l__celtic_path_tl { |- }
+      }
+      {
+        \tl_put_right:Nx \l__celtic_path_tl { to[out=\int_eval:n {(90 - 45*\l__celtic_dx)*\l__celtic_dy}, in=\int_eval:n {90 + 90*\l__celtic_dx}] }
+      }
+      \int_set:Nn \l__celtic_lout {-90+90*\l__celtic_dx}
+      \int_set:Nn \l__celtic_dy {-\l__celtic_dy}
+      \tl_put_right:Nx \l__celtic_path_tl {(\int_use:N \l__celtic_x, \fp_eval:n {\int_use:N \l__celtic_y + .5 * \int_use:N \l__celtic_dy})}
+      \bool_set_true:N \l__celtic_bounce_bool
+    }
+%    \end{macrocode}
+% Did we bounce this time?
+%    \begin{macrocode}
+    \bool_if:NF \l__celtic_bounce_bool
+    {
+%    \end{macrocode}
+% Did we bounce last time?
+%    \begin{macrocode}
+      \bool_if:NTF \l__celtic_pbounce_bool
+      {
+%    \end{macrocode}
+% Yes, so the second half is a curve.
+%    \begin{macrocode}
+        \tl_put_right:Nx \l__celtic_path_tl { to[out=\int_use:N \l__celtic_lout,in=\int_eval:n {(-90 - 45 * \l__celtic_dx)*\l__celtic_dy}] }
+      }
+      {
+%    \end{macrocode}
+% No, so the second half is a straight line.
+%    \begin{macrocode}
+        \tl_put_right:Nn \l__celtic_path_tl { -- }
+      }
+%    \end{macrocode}
+% The next crossing.
+%    \begin{macrocode}
+      \tl_put_right:Nx  \l__celtic_path_tl { (\int_use:N         \l__celtic_x, \int_use:N \l__celtic_y)}
+%    \end{macrocode}
+% If we haven't already gone over a crossing, this is our overcrossing.
+%    \begin{macrocode}
+      \tl_if_empty:NTF \l__celtic_crossing_tl
+      {
+%    \end{macrocode}
+% So we record this as our overcrossing.
+%    \begin{macrocode}
+        \tl_set:Nx \l__celtic_crossing_tl {(\int_use:N         \l__celtic_x, \int_use:N \l__celtic_y)}
+      }
+      {
+%    \end{macrocode}
+% Otherwise, it's the undercrossing so we note that we've visited this one. 
+%    \begin{macrocode}
+        \tl_clear:c {crossing used \int_use:N \l__celtic_x - \int_use:N \l__celtic_y}
+      }
+%    \end{macrocode}
+% Increment the crossing count.
+%    \begin{macrocode}
+      \int_incr:N \l__celtic_cross_int
+%    \end{macrocode}
+% Record our outward angle.
+%    \begin{macrocode}
+      \int_set:Nn \l__celtic_lout {(90 - \l__celtic_dx * 45) * \l__celtic_dy}
+    }
+  }
+}
+%    \end{macrocode}
+% \end{macro}
+%
+% Now we set up the keys we'll use.
+%    \begin{macrocode}
+\keys_define:nn { celtic }
+{
+%    \end{macrocode}
+% This sets the maximum number of steps in a path.
+%    \begin{macrocode}
+  max~ steps .int_set:N = \l__celtic_max_steps_int,
+%    \end{macrocode}
+% This flips the over/under crossings.
+%    \begin{macrocode}
+  flip .code:n = {
+    \int_set:Nn \l__celtic_flip_int {-1}
+  },
+%    \end{macrocode}
+% These set the size of the knot.
+%    \begin{macrocode}
+  width .int_set:N = \l__celtic_width_int,
+  height .int_set:N = \l__celtic_height_int,
+  size .code:n = {
+%    \end{macrocode}
+% The size is a CSV so we use a \Verb+clist+ to separate it.
+%    \begin{macrocode}
+    \clist_set:Nn \l__celtic_tmpa_clist {#1}
+    \clist_pop:NN \l__celtic_tmpa_clist \l__celtic_tmpa_tl
+    \int_set:Nn \l__celtic_width_int {\l__celtic_tmpa_tl}
+    \clist_pop:NN \l__celtic_tmpa_clist \l__celtic_tmpa_tl
+    \int_set:Nn \l__celtic_height_int {\l__celtic_tmpa_tl}
+  },
+%    \end{macrocode}
+% The size keys are placed in a separate group to make it possible to process them before all other keys.
+%    \begin{macrocode}
+  width  .groups:n = { size },
+  height .groups:n = { size },
+  size   .groups:n = { size },
+%    \end{macrocode}
+% The next keys set the various crossing behaviours.
+%    \begin{macrocode}
+  crossings .code:n = {
+    \seq_set_split:Nnn \l__celtic_tmpa_seq {;} {#1}
+    \seq_map_inline:Nn \l__celtic_tmpa_seq {
+      \tl_if_empty:nF {##1}
+      {
+        \celtic_set_crossings:w ##1 \q_stop
+      }
+    }
+  },
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+  symmetric~ crossings .code:n = {
+    \seq_set_split:Nnn \l__celtic_tmpa_seq {;} {#1}
+    \seq_map_inline:Nn \l__celtic_tmpa_seq {
+      \tl_if_empty:nF {##1}
+      {
+        \celtic_set_symmetric_crossings:w ##1 \q_stop
+      }
+    }
+  },
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+  ignore~ crossings .code:n ={
+    \seq_set_split:Nnn \l__celtic_tmpa_seq {;} {#1}
+    \seq_map_inline:Nn \l__celtic_tmpa_seq {
+      \tl_if_empty:nF {##1}
+      {
+        \celtic_ignore_crossings:w ##1 \q_stop
+      }
+    }
+  },
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+  ignore~ symmetric~ crossings .code:n ={
+    \seq_set_split:Nnn \l__celtic_tmpa_seq {;} {#1}
+    \seq_map_inline:Nn \l__celtic_tmpa_seq {
+      \tl_if_empty:nF {##1}
+      {
+        \celtic_ignore_symmetric_crossings:w ##1 \q_stop
+      }
+    }
+  },
+%    \end{macrocode}
+% The \Verb+style+ key is passed on to \Verb+\tikzset+.
+%    \begin{macrocode}
+  style .code:n = {
+    \tikzset {#1}
+  },
+%    \end{macrocode}
+% This relocates the diagram.
+%    \begin{macrocode}
+  at .code:n = {
+    \celtic_shift:n {#1}
+  },
+%    \end{macrocode}
+% These set the margin for the clip regions.
+%    \begin{macrocode}
+  inner~ clip .fp_set:N = \l__celtic_inner_fp,
+  outer~ clip .fp_set:N = \l__celtic_outer_fp,
+}
+%    \end{macrocode}
+%
+% \begin{macro}{\CelticDrawPath}
+% This is the user macro.
+% Its mandatory argument is a list of key/value pairs.
+%    \begin{macrocode}
+\DeclareDocumentCommand \CelticDrawPath { m }
+{
+%    \end{macrocode}
+% Get a nice clean initial state.
+%    \begin{macrocode}
+  \group_begin:
+  \pgfscope
+  \seq_clear:N \l__celtic_path_seq
+  \seq_clear:N \l__celtic_component_seq
+  \seq_clear:N \l__celtic_crossing_seq
+  \int_set:Nn \l__celtic_flip_int {1}
+%    \end{macrocode}
+% Figure out if \Verb+|+ is active or not (\Verb+fancyvrb+ sets it active).
+%    \begin{macrocode}
+\int_compare:nT {\char_value_catcode:n {`\|} = 13}
+{
+  \tl_set_eq:NN \l__celtic_bar_tl \l__celtic_active_bar_tl
+}
+%    \end{macrocode}
+% Clear all the crossing data.
+%    \begin{macrocode}
+  \int_step_inline:nnnn {1} {1} {\l__celtic_height_int-1}
+  {
+    \int_step_inline:nnnn {1 + \int_mod:nn {##1}{2}} {2} {\l__celtic_width_int-1}
+{
+  \tl_clear_new:c {crossing used ####1 - ##1}
+  \tl_set:cn {crossing used ####1 - ##1} {X}
+}
+  }
+%    \end{macrocode}
+% Process the keys relating to the size of the knot.
+%    \begin{macrocode}
+  \keys_set_groups:nnn { celtic } { size } {#1}
+%    \end{macrocode}
+% Process all other keys.
+%    \begin{macrocode}
+  \keys_set_filter:nnn { celtic } { size } {#1}
+%    \end{macrocode}
+% Draw (maybe) the outer boundary.
+%    \begin{macrocode}
+  \path[celtic~ bar/.try, celtic~ surround/.try] (0,0) rectangle (\int_use:N \l__celtic_width_int, \int_use:N \l__celtic_height_int);
+%    \end{macrocode}
+% Draw (maybe) the crossings.
+%    \begin{macrocode}
+  \int_step_inline:nnnn {1} {1} {\l__celtic_height_int-1}
+  {
+    \int_step_inline:nnnn {1 + \int_mod:nn {##1}{2}} {2} {\l__celtic_width_int-1}
+{
+  \tl_if_exist:cT {crossing ####1 - ##1}
+  {
+    \tl_if_eq:cNTF {crossing ####1 - ##1} \l__celtic_bar_tl
+    {
+%    \end{macrocode}
+% Vertical crossing.
+%    \begin{macrocode}
+      \path[celtic~ bar/.try] (####1,##1-1) -- (####1,##1+1);
+    }
+    {
+%    \end{macrocode}
+% Horizontal crossing.
+%    \begin{macrocode}
+      \path[celtic~ bar/.try] (####1-1,##1) -- (####1+1,##1);
+    }
+  }
+}
+  }
+%    \end{macrocode}
+% Now we work through the crossings, trying to generate a path starting at each one.
+% The crossings are at points \((x,y)\) with \(x + y\) odd.
+%    \begin{macrocode}
+  \int_step_inline:nnnn {1} {1} {\l__celtic_height_int-1}
+  {
+    \int_step_inline:nnnn {1 + \int_mod:nn {##1}{2}} {2} {\l__celtic_width_int-1}
+{
+%    \end{macrocode}
+% Attempt to generate a path starting from that crossing.
+% The third argument is to indicate which way the under-path goes from that crossing.
+%    \begin{macrocode}
+    \celtic_generate_path:nnx {####1}{##1}{\int_eval:n {\l__celtic_flip_int*(2*\int_mod:nn{####1}{2} - 1)}}
+    }
+  }
+%    \end{macrocode}
+% Once we have generated our paths, we render them and close our scope and group.
+%    \begin{macrocode}
+  \celtic_render_path:
+  \endpgfscope
+  \group_end:
+}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\celtic_generate_path:nnn}
+% This macro generates a sequence of path segments.
+%    \begin{macrocode}
+\cs_new_nopar:Npn \celtic_generate_path:nnn #1#2#3
+{
+%    \end{macrocode}
+% First off, we test to see if the given coordinates are allowed as a starting point.
+% If the crossing has a wall or it is already marked as ``used'' then it isn't.
+%    \begin{macrocode}
+  \bool_if:nF {
+    \tl_if_exist_p:c {crossing #1 - #2}
+    ||
+    \tl_if_empty_p:c {crossing used #1 - #2}
+  }
+  {
+%    \end{macrocode}
+% Those tests failed, so we procede.
+% First, we mark the crossing as used and set our initial data.
+% Position, original position, and direction.
+%    \begin{macrocode}
+    \tl_clear:c {crossing used #1 - #2}
+    \int_incr:N \l__celtic_component_int
+    \int_set:Nn \l__celtic_x {#1}
+    \int_set:Nn \l__celtic_y {#2}
+    \int_set_eq:NN \l__celtic_ox \l__celtic_x
+    \int_set_eq:NN \l__celtic_oy \l__celtic_y
+    \int_set:Nn \l__celtic_dx {#3}
+    \int_set:Nn \l__celtic_dy {1}
+%    \end{macrocode}
+% This holds our recursion index so that we can bail out if we look like we're entering a loop (which we shouldn't).
+%    \begin{macrocode}
+    \int_zero:N \l__celtic_int
+%    \end{macrocode}
+% We stop the loop if we get back where we started or we hit the maximum recursion limit.
+%    \begin{macrocode}
+    \bool_do_until:nn
+    {
+      (\int_compare_p:n {\l__celtic_x == \l__celtic_ox}
+      &&
+      \int_compare_p:n {\l__celtic_y == \l__celtic_oy}
+      )
+      || \int_compare_p:n {\l__celtic_int > \l__celtic_max_steps_int}
+    }
+    {
+%    \end{macrocode}
+% Increment our counter.
+%    \begin{macrocode}
+      \int_incr:N \l__celtic_int
+%    \end{macrocode}
+% Create the segment between this crossing and the next one.
+%    \begin{macrocode}
+      \celtic_next_crossing:
+%    \end{macrocode}
+% Store the segment, its over-crossing, and its component number.
+% Then return to the start of the loop.
+%    \begin{macrocode}
+      \seq_put_left:NV \l__celtic_path_seq \l__celtic_path_tl
+      \seq_put_left:NV \l__celtic_crossing_seq \l__celtic_crossing_tl
+      \seq_put_left:NV \l__celtic_component_seq \l__celtic_component_int
+    }
+%    \end{macrocode}
+% If we hit the maximum number of steps, issue a warning.
+%    \begin{macrocode}
+    \int_compare:nT {\l__celtic_int > \l__celtic_max_steps_int}
+    {
+      \msg_warning:nn {celtic} { max~ steps }
+    }
+  }
+}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\celtic_generate_path:nnx}
+% Useful variant.
+%    \begin{macrocode}
+\cs_generate_variant:Nn \celtic_generate_path:nnn {nnx}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\celtic_render_path:}
+% This takes a generated list of path segments and renders them.
+%    \begin{macrocode}
+\cs_new_nopar:Npn \celtic_render_path:
+{
+%    \end{macrocode}
+% First pass through the sequence of segments.
+%    \begin{macrocode}
+  \seq_map_inline:Nn \l__celtic_path_seq
+  {
+%    \end{macrocode}
+% We need to get the component number, but \Verb+pop+ removes it from the sequence so we put it back at the other end again.
+%    \begin{macrocode}
+    \seq_pop:NN \l__celtic_component_seq \l__celtic_tmpa_tl
+    \seq_put_right:NV \l__celtic_component_seq \l__celtic_tmpa_tl
+%    \end{macrocode}
+% Draw the path segment, styling by the component number.
+%    \begin{macrocode}
+    \path[celtic~ path/.try, celtic~ path~ \tl_use:N \l__celtic_tmpa_tl/.try] ##1;
+  }
+%    \end{macrocode}
+% This next bit of code attempts to work out the true thickness of the presumably doubled path.
+% We do it in a group and scope to limit its effect.
+%    \begin{macrocode}
+  \group_begin:
+  \pgfscope
+  \tikzset{celtic~ path/.try}
+  \tl_use:c {tikz@double@setup}
+%    \end{macrocode}
+% This gets the resulting line width outside the group and scope.
+%    \begin{macrocode}
+  \tl_set:Nn \l__celtic_tmpa_tl
+  {
+    \endpgfscope
+    \group_end:
+    \fp_set:Nn \l__celtic_clip_fp
+  }
+  \tl_put_right:Nx \l__celtic_tmpa_tl {{\dim_use:N \pgflinewidth}}
+  \tl_use:N \l__celtic_tmpa_tl
+%    \end{macrocode}
+% Now we set the inner and outer clip sizes based on that line width.
+%    \begin{macrocode}
+  \fp_set:Nn \l__celtic_inner_clip_fp {sqrt(2) * (\l__celtic_clip_fp + \l__celtic_inner_fp)}
+  \fp_set:Nn \l__celtic_clip_fp {sqrt(2) * (\l__celtic_clip_fp + \l__celtic_outer_fp)}
+%    \end{macrocode}
+%
+% This second pass through the segments redraws each one clipped to a diamond neighbourhood of its over-crossing.
+%    \begin{macrocode}
+  \seq_map_inline:Nn \l__celtic_path_seq
+  {
+%    \end{macrocode}
+% We get the crossing coordinate.
+%    \begin{macrocode}
+    \seq_pop:NN \l__celtic_crossing_seq \l__celtic_crossing_tl
+%    \end{macrocode}
+% Again, we need the component number.
+%    \begin{macrocode}
+    \seq_pop:NN \l__celtic_component_seq \l__celtic_tmpa_tl
+    \seq_put_right:NV \l__celtic_component_seq \l__celtic_tmpa_tl
+    \pgfscope
+%    \end{macrocode}
+% This is the smaller of the clip regions.
+%    \begin{macrocode}
+    \clip \l__celtic_crossing_tl +(-\fp_to_dim:N \l__celtic_inner_clip_fp,0) -- +(0,\fp_to_dim:N \l__celtic_inner_clip_fp) -- +(\fp_to_dim:N \l__celtic_inner_clip_fp,0) -- +(0,-\fp_to_dim:N \l__celtic_inner_clip_fp) --  +(-\fp_to_dim:N \l__celtic_inner_clip_fp,0);
+%    \end{macrocode}
+% We draw just the background part of the (presumably doubled) path.
+%    \begin{macrocode}
+    \path[celtic~ path/.try, celtic~ path~ \tl_use:N \l__celtic_tmpa_tl/.try, double~ background] ##1;
+    \endpgfscope
+    \pgfscope
+%    \end{macrocode}
+% Noew we apply the larger clip region.
+%    \begin{macrocode}
+    \clip \l__celtic_crossing_tl +(-\fp_to_dim:N \l__celtic_clip_fp,0) -- +(0,\fp_to_dim:N \l__celtic_clip_fp) -- +(\fp_to_dim:N \l__celtic_clip_fp,0) -- +(0,-\fp_to_dim:N \l__celtic_clip_fp) --  +(-\fp_to_dim:N \l__celtic_clip_fp,0);
+%    \end{macrocode}
+% And draw the foreground part.
+%    \begin{macrocode}
+    \path[celtic~ path/.try, celtic~ path~ \tl_use:N \l__celtic_tmpa_tl/.try,double~ foreground] ##1;
+    \endpgfscope
+  }
+}
+%    \end{macrocode}
+% \end{macro}
+% We are now leaving \LaTeX3 world.
+%    \begin{macrocode}
+\ExplSyntaxOff
+%    \end{macrocode}
+%
+% Clipping with doubled paths isn't perfect when anti-aliasing is used as it produces artefacts where the lower path shows through.
+% To get round that, we need to draw the two parts of the doubled path separately.
+% The following two keys extract the line widths and colours of the two parts of a doubled path and apply it.
+%    \begin{macrocode}
+\tikzset{
+%    \end{macrocode}
+% This sets the stye to that of the under path.
+%    \begin{macrocode}
+  double background/.code={%
+    \begingroup
+    \tikz@double@setup
+    \global\pgf@xa=\pgflinewidth
+    \endgroup
+    \expandafter\tikz@semiaddlinewidth\expandafter{\the\pgf@xa}%
+    \tikz@addmode{\tikz@mode@doublefalse}%
+  },
+%    \end{macrocode}
+% This to the over path.
+%    \begin{macrocode}
+  double foreground/.code={%
+    \begingroup
+    \tikz@double@setup
+    \global\pgf@xa=\pgfinnerlinewidth
+    \endgroup
+    \expandafter\tikz@semiaddlinewidth\expandafter{\the\pgf@xa}%
+    \tikz@addmode{\tikz@mode@doublefalse}%
+    \tikzset{color=\pgfinnerstrokecolor}%
+  },
+}
+%    \end{macrocode}
+%
+% \iffalse
+%</library>
+% \fi
+%\Finale
+\endinput
-- 
cgit v1.2.3

