diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /graphics/pgf/contrib/celtic |
Initial commit
Diffstat (limited to 'graphics/pgf/contrib/celtic')
-rw-r--r-- | graphics/pgf/contrib/celtic/README | 9 | ||||
-rw-r--r-- | graphics/pgf/contrib/celtic/celtic.dtx | 1092 | ||||
-rw-r--r-- | graphics/pgf/contrib/celtic/celtic.pdf | bin | 0 -> 357298 bytes | |||
-rw-r--r-- | graphics/pgf/contrib/celtic/celtic_code.pdf | bin | 0 -> 330231 bytes | |||
-rw-r--r-- | graphics/pgf/contrib/celtic/celtic_doc.tex | 229 | ||||
-rw-r--r-- | graphics/pgf/contrib/celtic/tikzlibraryceltic.code.tex | 536 |
6 files changed, 1866 insertions, 0 deletions
diff --git a/graphics/pgf/contrib/celtic/README b/graphics/pgf/contrib/celtic/README new file mode 100644 index 0000000000..7fd9769531 --- /dev/null +++ b/graphics/pgf/contrib/celtic/README @@ -0,0 +1,9 @@ +---------------------------------------------------------------- +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. diff --git a/graphics/pgf/contrib/celtic/celtic.dtx b/graphics/pgf/contrib/celtic/celtic.dtx new file mode 100644 index 0000000000..4cc3caa118 --- /dev/null +++ b/graphics/pgf/contrib/celtic/celtic.dtx @@ -0,0 +1,1092 @@ +% \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{783} +% +% \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.1 from 2016/02/19} +% +% +% \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_overpath_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 \c__celtic_colon_tl +\tl_new:N \l__celtic_bar_tl +\tl_new:N \l__celtic_active_bar_tl +\tl_new:N \l__celtic_start_tl +\bool_new:N \l__celtic_bounce_bool +\bool_new:N \l__celtic_pbounce_bool + +\cs_new_nopar:Npn \tl_split_after:Nnn #1#2#3 +{ + \cs_set:Npn \tl_split_aux:nnn ##1#3##2 \q_stop: {#3##2} + \tl_set:Nx #1 {\tl_split_aux:nnn #2 \q_stop:} +} +\cs_generate_variant:Nn \tl_split_after:Nnn {NVn} +\cs_new_nopar:Npn \tl_split_before:Nnn #1#2#3 +{ + \cs_set:Npn \tl_split_aux:nnn ##1#3##2 \q_stop: {##1#3} + \tl_set:Nx #1 {\tl_split_aux:nnn #2 \q_stop:} +} +\cs_generate_variant:Nn \tl_split_before:Nnn {NVn} +% \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} +\tl_const:Nx \c__celtic_colon_tl { \token_to_str:N : } +% \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} \c__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} \c__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+\c__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+\c__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 \c__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 \c__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 + \tl_clear:N \l__celtic_overpath_tl + \bool_set_false:N \l__celtic_bounce_tl + \tl_set:Nx \l__celtic_start_tl {(\int_use:N \l__celtic_x, \int_use:N \l__celtic_y)} +% \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} +% Is our overcrossing one of the undercrossings? +% If so, remove the initial or final segment as appropriate. +% \begin{macrocode} + \tl_set_eq:NN \l__celtic_overpath_tl \l__celtic_path_tl + \tl_set:Nx \l__celtic_tmpa_tl {(\int_use:N \l__celtic_x, \int_use:N +\l__celtic_y)} + \tl_if_eq:NNT \l__celtic_crossing_tl \l__celtic_tmpa_tl + { + \tl_reverse:N \l__celtic_overpath_tl + \tl_set:Nx \l__celtic_overpath_tl {\tl_tail:N \l__celtic_overpath_tl} + \tl_split_after:NVn \l__celtic_overpath_tl \l__celtic_overpath_tl {)} + \tl_reverse:N \l__celtic_overpath_tl + } + \tl_if_eq:NNT \l__celtic_crossing_tl \l__celtic_start_tl + { + \tl_set:Nx \l__celtic_overpath_tl {\tl_tail:N \l__celtic_overpath_tl} + \tl_split_after:NVn \l__celtic_overpath_tl \l__celtic_overpath_tl {(} + } +} +% \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_overpath_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 proceed. +% 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_overpath_seq \l__celtic_overpath_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_overpath_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 diff --git a/graphics/pgf/contrib/celtic/celtic.pdf b/graphics/pgf/contrib/celtic/celtic.pdf Binary files differnew file mode 100644 index 0000000000..74e79bdab9 --- /dev/null +++ b/graphics/pgf/contrib/celtic/celtic.pdf diff --git a/graphics/pgf/contrib/celtic/celtic_code.pdf b/graphics/pgf/contrib/celtic/celtic_code.pdf Binary files differnew file mode 100644 index 0000000000..36757a9306 --- /dev/null +++ b/graphics/pgf/contrib/celtic/celtic_code.pdf diff --git a/graphics/pgf/contrib/celtic/celtic_doc.tex b/graphics/pgf/contrib/celtic/celtic_doc.tex new file mode 100644 index 0000000000..a8cd3abaa1 --- /dev/null +++ b/graphics/pgf/contrib/celtic/celtic_doc.tex @@ -0,0 +1,229 @@ +\immediate\write18{tex celtic.dtx} +\documentclass{ltxdoc} +\usepackage[T1]{fontenc} +\usepackage{trace} +\usepackage{lmodern} +\usepackage{morefloats} +\usepackage{tikz} +\usetikzlibrary{celtic} +\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}{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} + \centering + \input{example.out} + \end{minipage}} +\end{center} +} + +\providecommand*{\url}{\texttt} + +\title{The \textsf{celtic} TikZ Library: Documentation} +\author{Andrew Stacey \\ \url{stacey@math.ntnu.no}} +\date{19th February 2016} + +\begin{document} +\VerbatimFootnotes +\maketitle + +\begin{center} +\begin{tikzpicture}[ + scale=.5, + celtic path/.style={ + draw, + double=gray!40, + red, + double distance=1mm, + line width=4pt + }, + celtic path 1/.style={ + green!50!black, + }, +] +\CelticDrawPath{ + symmetric crossings={ + 1,2,-; + 2,1,|; + 4,3,-; + 3,4,|; + }, + size={8,8}, +} +\end{tikzpicture} +\end{center} + +\section{Introduction} + +This is a little TikZ library for drawing Celtic style knots. +The particular type of Celtic knot (technically, \emph{link}) is very simple and can be specified by listing the ``walls'' within the region of the knot. +From this information, it is possible to build the entire link and thus to tell TikZ how render it. +That is what this library does. + +\section{Usage} + +The routine is implemented as a TikZ library. +Thus to use it, add \Verb+celtic+ to the list of TikZ libraries that you load. + +\begin{verbatim} +\usetikzlibrary{celtic} +\end{verbatim} + +The library defines one command which renders a Celtic knot. +The knot is specified by passing various \emph{key-value} pairs to this command. +The library also defines styles which can be used to modify the rendering. + +\DescribeMacro{\CelticDrawPath}\Verb+\CelticDrawPath{<opt>}+ is the command to render a knot. +It takes one option which is a list of key-value pairs which specify the knot. +The allowed key-value pairs are as follows. + +\begin{itemize} +\item \Verb+max steps=N+ The process of finding the paths through the knot (needed to ensure that they are coloured correctly) is iterative. +Although every care has been taken to ensure that the iteration is confined (and therefore finite), the iteration has been devised with a built-in limit. +This limit can be adjusted using this key. +The default is \(20\). +If the limit is reached, a warning will be issued (and the knot will probably look wrong). +In that case, use this key to raise the limit. + +\item \Verb+flip+ The specification of a Celtic knot in terms of walls does not completely determine it. +There is an ambiguity as to which crossings are over and which under (once one crossing is determined, all the others follow). +This key flips all of the crossings and so can be used to switch between the two variants. + +\item \Verb+width=W+, \Verb+height=H+, \Verb+size={W,H}+ These set the dimensions on the knot in terms of the number of crossings. +The numbers must be even. + +\item \Verb+crossings+, \Verb+symmetric crossings+ These set the crossings. +The general format of a crossing is \Verb+x,y,type+ where \Verb+x+ and \Verb+y+ can be either numbers or ranges, using the format \Verb+n:m+\footnote{This package uses \LaTeX3 internally; using a colon as the range separator was a headache to implement.}. +The type of the crossing is either \Verb+|+ or \Verb+-+ for (respectively) vertical or horizontal walls\footnote{The package attempts to be smart with regard to allowing \Verb+|+ to be active.}. +Multiple crossing specifications can be given as a semi-colon-delimited list (a final semi-colon is acceptable, making it easy to comment out items in the list). +The \Verb+symmetric+ variant places walls at four points obtained by applying reflections to the specified crossing. + +\item \Verb+ignore crossings+, \Verb+ignore symmetric crossings+ The code works out the paths involved by picking a starting point and direction and then following it, bouncing off walls as appropriate, until it comes back to the beginning. +It then picks a new starting point and continues until all crossings are used up. +These keys designate certain points as \emph{disallowed} as starting points. +This can be used to remove certain regions from the knot, for example to create a border around a rectangle. +The \Verb+symmetric+ version works \dots\ symmetrically. + +\item \Verb+style={<style>}+ The contents of this are passed on to \Verb+\tikzset+. +(The key-value pairs for the \Verb+\CelticDrawPath+ are actually \LaTeX3 keys, not TikZ keys, so this is the simplest way to pass them through.) + +\item \Verb+at=<coordinate>+ This shifts the knot so that the lower left corner is at the coordinate. +(The default location is \Verb+(0,0)+.) +The coordinate is passed through TikZ's processing so can be any legal TikZ coordinate. + +\item \Verb+inner clip=N+, \Verb+outer clip=N+. The crossings are rendered by redrawing the over paths with a clipping region. +The size of the clipping region is determined by the line width. +These keys add a little to that clipping region. +This can be useful if the overdraws are not large enough, but the added amount should not be so much that the overdraws interfere with each other. +Some experimentation is needed. +The \Verb+inner clip+ applies to the background part of the path and the \Verb+outer clip+ to the foreground. + +\end{itemize} + +As the pieces are rendered, various TikZ styles are invoked. + +\begin{itemize} +\item \Verb+celtic surround+ This style is used for the outer border. + +\item \Verb+celtic bar+ This is used for the internal walls. +It is also used on the outer border (prior to the \Verb+celtic surround+ so it can be overridden). + +\item \Verb+celtic path+ This is used for the components of the celtic knot (link). + +\item \Verb+celtic path N+ The individual components can be styled using their number (there is a logic to the numbering, but experimentation is the best way to work out which is which). + + +\end{itemize} + +\section{Example} + +\begin{example} +\begin{tikzpicture}[ + scale=.5, + celtic path/.style={ + draw, + double=gray!40, + red, + double distance=1mm, + line width=4pt + }, + celtic path 1/.style={ + green!50!black, + }, + celtic path 2/.style={ + blue, + }, + celtic path 3/.style={ + red!50!black, + }, + celtic surround/.style={ + ultra thick, + black, + fill=black + }, +] +\CelticDrawPath{ + symmetric crossings={ + 4,1:3,|; + 10,1,|; + 5,4,-; + 8,3,-; + }, + size={20,10}, + max steps=50 +} +\end{tikzpicture} +\end{example} + +\begin{example} +\begin{tikzpicture}[ + scale=.5, + celtic path/.style={ + draw, + double=white, + red, + double distance=5pt, + line width=1pt + }, + celtic bar/.style={ + ultra thick, + black, + draw + }, +] +\CelticDrawPath{ + size={20,10}, + symmetric crossings={ + 3,4:5,|; + 4:16,3,- + }, + ignore symmetric crossings={ + 4:10,5; + 5:10,4 + }, + max steps=50 +} +\end{tikzpicture} +\end{example} + +\end{document} + +% Local Variables: +% tex-output-type: "pdf18" +% End: diff --git a/graphics/pgf/contrib/celtic/tikzlibraryceltic.code.tex b/graphics/pgf/contrib/celtic/tikzlibraryceltic.code.tex new file mode 100644 index 0000000000..f70dc9e1f8 --- /dev/null +++ b/graphics/pgf/contrib/celtic/tikzlibraryceltic.code.tex @@ -0,0 +1,536 @@ +%% +%% This is file `tikzlibraryceltic.code.tex', +%% generated with the docstrip utility. +%% +%% The original source files were: +%% +%% celtic.dtx (with options: `library') +%% ---------------------------------------------------------------- +%% 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 +%% ---------------------------------------------------------------- +%% +\usepackage{expl3} +\usepackage{xparse} +\ExplSyntaxOn +\cs_new_nopar:Npn \celtic_shift:n #1 +{ + \use:c{tikz@scan@one@point}\pgftransformshift #1\relax +} +\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_overpath_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 \l__celtic_bar_tl +\tl_new:N \l__celtic_active_bar_tl +\tl_new:N \l__celtic_start_tl +\bool_new:N \l__celtic_bounce_bool +\bool_new:N \l__celtic_pbounce_bool + +\cs_new_nopar:Npn \tl_split_after:Nnn #1#2#3 +{ + \cs_set:Npn \tl_split_aux:nnn ##1#3##2 \q_stop: {#3##2} + \tl_set:Nx #1 {\tl_split_aux:nnn #2 \q_stop:} +} +\cs_generate_variant:Nn \tl_split_after:Nnn {NVn} +\cs_new_nopar:Npn \tl_split_before:Nnn #1#2#3 +{ + \cs_set:Npn \tl_split_aux:nnn ##1#3##2 \q_stop: {##1#3} + \tl_set:Nx #1 {\tl_split_aux:nnn #2 \q_stop:} +} +\cs_generate_variant:Nn \tl_split_before:Nnn {NVn} +\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.} +\tl_const:Nx \c__celtic_colon_tl { \token_to_str:N : } +\tl_set:Nn \l__celtic_bar_tl {|} +\group_begin: +\char_set_catcode_active:N \| +\tl_gset:Nn \l__celtic_active_bar_tl {|} +\group_end: +\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} +\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} +\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} + } +} +\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} + } +} +\cs_new_nopar:Npn \celtic_maybe_xrange:nnnn #1#2#3#4 +{ + \tl_if_in:nVTF {#3} \c__celtic_colon_tl + { + \celtic_do_xrange:w {#1}{#2}#3\q_stop{#4} + } + { + \celtic_maybe_yrange:nnnn {#1}{#2}{#3}{#4} + } +} +\cs_new_nopar:Npn \celtic_maybe_yrange:nnnn #1#2#3#4 +{ + \tl_if_in:nVTF {#4} \c__celtic_colon_tl + { + \celtic_do_yrange:w {#1}{#2}{#3}#4\q_stop + } + { + \celtic_maybe_symmetric:nnnn {#1}{#2}{#3}{#4} + } +} +\tl_set:Nx \l_tmpa_tl +{ + \exp_not:N \cs_new_nopar:Npn \exp_not:N \celtic_do_xrange:w ##1##2##3\tl_use:N \c__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} + } + } + \exp_not:N \cs_new_nopar:Npn \exp_not:N \celtic_do_yrange:w ##1##2##3##4\tl_use:N \c__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} + } + } +} +\tl_use:N \l_tmpa_tl +\cs_new_nopar:Npn \celtic_ignore_crossings:w #1,#2\q_stop +{ + \celtic_maybe_xrange:nnnn {}{}{#1}{#2} +} +\cs_new_nopar:Npn \celtic_ignore_symmetric_crossings:w #1,#2\q_stop +{ + \celtic_maybe_xrange:nnnn {s}{}{#1}{#2} +} +\cs_new_nopar:Npn \celtic_set_crossings:w #1,#2,#3\q_stop +{ + \celtic_maybe_xrange:nnnn {}{#3}{#1}{#2} +} +\cs_new_nopar:Npn \celtic_set_symmetric_crossings:w #1,#2,#3\q_stop +{ + \celtic_maybe_xrange:nnnn {s}{#3}{#1}{#2} +} +\cs_new_nopar:Npn \celtic_next_crossing: +{ + \int_zero:N \l__celtic_cross_int + \tl_clear:N \l__celtic_crossing_tl + \tl_clear:N \l__celtic_path_tl + \tl_clear:N \l__celtic_overpath_tl + \bool_set_false:N \l__celtic_bounce_tl + \tl_set:Nx \l__celtic_start_tl {(\int_use:N \l__celtic_x, \int_use:N \l__celtic_y)} + \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} + \bool_do_until:nn {\int_compare_p:n {\l__celtic_cross_int > 1}} + { + \bool_set_eq:NN \l__celtic_pbounce_bool \l__celtic_bounce_bool + \bool_set_false:N \l__celtic_bounce_bool + \int_add:Nn \l__celtic_x {\l__celtic_dx} + \int_add:Nn \l__celtic_y {\l__celtic_dy} + \tl_if_exist:cT {crossing \int_use:N \l__celtic_x - \int_use:N \l__celtic_y} + { + \tl_if_eq:cNTF {crossing \int_use:N \l__celtic_x - \int_use:N \l__celtic_y} \l__celtic_bar_tl + { + \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 + } + { + \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 + } + } + \int_compare:nT {\l__celtic_x == 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*\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 + } + \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 + } + \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 + } + \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 + } + \bool_if:NF \l__celtic_bounce_bool + { + \bool_if:NTF \l__celtic_pbounce_bool + { + \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}] } + } + { + \tl_put_right:Nn \l__celtic_path_tl { -- } + } + \tl_put_right:Nx \l__celtic_path_tl { (\int_use:N \l__celtic_x, \int_use:N \l__celtic_y)} + \tl_if_empty:NTF \l__celtic_crossing_tl + { + \tl_set:Nx \l__celtic_crossing_tl {(\int_use:N \l__celtic_x, \int_use:N \l__celtic_y)} + } + { + \tl_clear:c {crossing used \int_use:N \l__celtic_x - \int_use:N \l__celtic_y} + } + \int_incr:N \l__celtic_cross_int + \int_set:Nn \l__celtic_lout {(90 - \l__celtic_dx * 45) * \l__celtic_dy} + } + } + \tl_set_eq:NN \l__celtic_overpath_tl \l__celtic_path_tl + \tl_set:Nx \l__celtic_tmpa_tl {(\int_use:N \l__celtic_x, \int_use:N +\l__celtic_y)} + \tl_if_eq:NNT \l__celtic_crossing_tl \l__celtic_tmpa_tl + { + \tl_reverse:N \l__celtic_overpath_tl + \tl_set:Nx \l__celtic_overpath_tl {\tl_tail:N \l__celtic_overpath_tl} + \tl_split_after:NVn \l__celtic_overpath_tl \l__celtic_overpath_tl {)} + \tl_reverse:N \l__celtic_overpath_tl + } + \tl_if_eq:NNT \l__celtic_crossing_tl \l__celtic_start_tl + { + \tl_set:Nx \l__celtic_overpath_tl {\tl_tail:N \l__celtic_overpath_tl} + \tl_split_after:NVn \l__celtic_overpath_tl \l__celtic_overpath_tl {(} + } +} +\keys_define:nn { celtic } +{ + max~ steps .int_set:N = \l__celtic_max_steps_int, + flip .code:n = { + \int_set:Nn \l__celtic_flip_int {-1} + }, + width .int_set:N = \l__celtic_width_int, + height .int_set:N = \l__celtic_height_int, + size .code:n = { + \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} + }, + width .groups:n = { size }, + height .groups:n = { size }, + size .groups:n = { size }, + 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 + } + } + }, + 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 + } + } + }, + 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 + } + } + }, + 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 + } + } + }, + style .code:n = { + \tikzset {#1} + }, + at .code:n = { + \celtic_shift:n {#1} + }, + inner~ clip .fp_set:N = \l__celtic_inner_fp, + outer~ clip .fp_set:N = \l__celtic_outer_fp, +} +\DeclareDocumentCommand \CelticDrawPath { m } +{ + \group_begin: + \pgfscope + \seq_clear:N \l__celtic_path_seq + \seq_clear:N \l__celtic_overpath_seq + \seq_clear:N \l__celtic_component_seq + \seq_clear:N \l__celtic_crossing_seq + \int_set:Nn \l__celtic_flip_int {1} +\int_compare:nT {\char_value_catcode:n {`\|} = 13} +{ + \tl_set_eq:NN \l__celtic_bar_tl \l__celtic_active_bar_tl +} + \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} +} + } + \keys_set_groups:nnn { celtic } { size } {#1} + \keys_set_filter:nnn { celtic } { size } {#1} + \path[celtic~ bar/.try, celtic~ surround/.try] (0,0) rectangle (\int_use:N \l__celtic_width_int, \int_use:N \l__celtic_height_int); + \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 + { + \path[celtic~ bar/.try] (####1,##1-1) -- (####1,##1+1); + } + { + \path[celtic~ bar/.try] (####1-1,##1) -- (####1+1,##1); + } + } +} + } + \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} +{ + \celtic_generate_path:nnx {####1}{##1}{\int_eval:n {\l__celtic_flip_int*(2*\int_mod:nn{####1}{2} - 1)}} + } + } + \celtic_render_path: + \endpgfscope + \group_end: +} +\cs_new_nopar:Npn \celtic_generate_path:nnn #1#2#3 +{ + \bool_if:nF { + \tl_if_exist_p:c {crossing #1 - #2} + || + \tl_if_empty_p:c {crossing used #1 - #2} + } + { + \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} + \int_zero:N \l__celtic_int + \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} + } + { + \int_incr:N \l__celtic_int + \celtic_next_crossing: + \seq_put_left:NV \l__celtic_path_seq \l__celtic_path_tl + \seq_put_left:NV \l__celtic_overpath_seq \l__celtic_overpath_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 + } + \int_compare:nT {\l__celtic_int > \l__celtic_max_steps_int} + { + \msg_warning:nn {celtic} { max~ steps } + } + } +} +\cs_generate_variant:Nn \celtic_generate_path:nnn {nnx} +\cs_new_nopar:Npn \celtic_render_path: +{ + \seq_map_inline:Nn \l__celtic_path_seq + { + \seq_pop:NN \l__celtic_component_seq \l__celtic_tmpa_tl + \seq_put_right:NV \l__celtic_component_seq \l__celtic_tmpa_tl + \path[celtic~ path/.try, celtic~ path~ \tl_use:N \l__celtic_tmpa_tl/.try] ##1; + } + \group_begin: + \pgfscope + \tikzset{celtic~ path/.try} + \tl_use:c {tikz@double@setup} + \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 + \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)} + \seq_map_inline:Nn \l__celtic_overpath_seq + { + \seq_pop:NN \l__celtic_crossing_seq \l__celtic_crossing_tl + \seq_pop:NN \l__celtic_component_seq \l__celtic_tmpa_tl + \seq_put_right:NV \l__celtic_component_seq \l__celtic_tmpa_tl + \pgfscope + \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); + \path[celtic~ path/.try, celtic~ path~ \tl_use:N \l__celtic_tmpa_tl/.try, double~ background] ##1; + \endpgfscope + \pgfscope + \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); + \path[celtic~ path/.try, celtic~ path~ \tl_use:N \l__celtic_tmpa_tl/.try,double~ foreground] ##1; + \endpgfscope + } +} +\ExplSyntaxOff +\tikzset{ + double background/.code={% + \begingroup + \tikz@double@setup + \global\pgf@xa=\pgflinewidth + \endgroup + \expandafter\tikz@semiaddlinewidth\expandafter{\the\pgf@xa}% + \tikz@addmode{\tikz@mode@doublefalse}% + }, + 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}% + }, +} +%% +%% 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 +%% +%% +%% End of file `tikzlibraryceltic.code.tex'. |