summaryrefslogtreecommitdiff
path: root/macros/luatex/latex/luasseq
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/luatex/latex/luasseq
Initial commit
Diffstat (limited to 'macros/luatex/latex/luasseq')
-rw-r--r--macros/luatex/latex/luasseq/README6
-rw-r--r--macros/luatex/latex/luasseq/luasseq.dtx992
-rw-r--r--macros/luatex/latex/luasseq/luasseq.ins4
-rw-r--r--macros/luatex/latex/luasseq/luasseq.lua894
-rw-r--r--macros/luatex/latex/luasseq/luasseq.pdfbin0 -> 224059 bytes
5 files changed, 1896 insertions, 0 deletions
diff --git a/macros/luatex/latex/luasseq/README b/macros/luatex/latex/luasseq/README
new file mode 100644
index 0000000000..b6cb5d9204
--- /dev/null
+++ b/macros/luatex/latex/luasseq/README
@@ -0,0 +1,6 @@
+Version 2015-07-16 contains compatibility fixed for lualatex bundled with TeXLive 2014.
+
+Required for use: luasseq.lua and luasseq.sty
+
+To get luasseq.sty: lualatex luasseq.ins (requires luasseq.dtx)
+To get the documentation luasseq.pdf: lualatex luasseq.dtx
diff --git a/macros/luatex/latex/luasseq/luasseq.dtx b/macros/luatex/latex/luasseq/luasseq.dtx
new file mode 100644
index 0000000000..349ee9f9df
--- /dev/null
+++ b/macros/luatex/latex/luasseq/luasseq.dtx
@@ -0,0 +1,992 @@
+%
+% ^^A This is file luasseq.dtx. First LaTeX the accompanying file luasseq.ins
+% ^^A to produce the package file luasseq.sty.
+% ^^A Then LaTeX this file to produce the documentation of the luasseq package.
+% ^^A
+% ^^A (c) 2003-2010 Tilman Bauer. Freely distributable under the LaTeX Project Public License (LPPL)
+% ^^A
+% \ifthenelse{0=1}{
+%<*never>
+\documentclass{ltxdoc}
+\usepackage{luasseq}
+\EnableCrossrefs
+\CodelineIndex
+\RecordChanges
+\begin{document}
+\DocInput{luasseq.dtx} \PrintIndex \PrintChanges
+\end{document}
+%</never>
+% }{}
+
+%
+% \changes{v1.0}{November 12, 2003}{First release of code and documentation}
+% \changes{v1.01}{November 15, 2003}{Bug fix with clipping after a ssgoto}
+% \changes{v1.02}{November 18, 2003}{Bug fix that would mess up page numbering in some cases}
+% \changes{v1.03}{December 11, 2003}{Added examples to documentation, bug fix in grid drawing}
+% \changes{v1.04}{January 19, 2004}{Up to nine drops per square now possible, added customization of the grid}
+% \changes{v1.05}{December 9, 2004}{Added support for different packing strategies; support for dotted lines}
+% \changes{v1.06}{August 1, 2005}{sseqxstep=0 will now suppress printing of horizontal label; similarly sseqystep=0 and vertical labels}
+% \changes{v2.0a}{July 16, 2007}{Completely recoded the graphics for using pgf instead of xyPic. Many new features. Consult the manual.}
+% \changes{v2.0}{April 9, 2009}{bug fix with arrows leaving the chart}
+% \ifthenelse{0=1}{
+%<*package>
+\NeedsTeXFormat{LaTeX2e}
+
+\ProvidesPackage{luasseq}[2010/11/06 ver 2.1]
+
+\RequirePackage{ifthen,calc,pifont}
+\RequirePackage{pgf}[2006/01/01]
+\RequirePackage{xkeyval}
+\usepgflibrary{snakes}
+
+\newboolean{debug}
+\setboolean{debug}{false}
+
+\DeclareOption{ps}{\errmessage{sseq: ps/nops is deprecated}}
+\DeclareOption{nops}{\errmessage{sseq: ps/nops is deprecated}}
+\DeclareOption{dvips}{\errmessage{sseq: deprecated option dvips ignored}}
+\DeclareOption{xdvi}{\errmessage{sseq: deprecated option xdvi ignored}}
+\DeclareOption{color}{\errmessage{sseq: deprecated option color ignored}}
+\DeclareOption{nocolor}{\errmessage{sseq: deprecated option nocolor ignored}}
+\DeclareOption{debug}{\setboolean{debug}{true}}
+
+\ProcessOptions
+
+%</package>
+% }{}
+%
+% \title{Typesetting spectral sequences in \LaTeX\ with luasseq.sty}
+% \author{Tilman Bauer\footnote{tilman@alum.mit.edu}}
+% \date{November 6, 2010}
+%
+% \MakeShortVerb{\|}
+%
+% \maketitle
+%
+% \section{Introduction}
+%
+% The present package, \texttt{luasseq}, facilitates the typesetting of mathematical objects called \emph{spectral sequence charts} (henceforth simply called ``chart''). It is a re-coded and largely code-compatible version of the older package |sseq| with new functionality and much higher typesetting speed and lower memory requirements. It requires to be run with |luatex|, a \TeX extension by the Lua programming language nowadays included in many \TeX distributation.
+%
+% From a typographical point of view, a chart is a two-dimensional grid with integer coordinates; at every position (x,y), there may be any number of symbols (usually dots, little circles or boxes, digits etc.), possibly decorated with labels, and between any two such symbols may or may not be a connection---e.~g., a line, an arrow, or some curved line.
+%
+% The \texttt{luasseq} package is built on top of the pgf package by Till Tantau. Previous versions of |sseq| (pre-2.0) were based on the graphics package xy-Pic; the current version produces higher quality output and allows for more customization, at the cost of requiring a fairly recent \TeX distribution (or, at least, the packages |pgf| and |xkeyval| should be installed and the former should be no older than from 2006) as well as LuaTeX.
+%
+% This package automates the following functions:
+%
+% \begin{itemize}
+% \item Automatic drawing of the grid and the axis labels;
+% \item Clipping. Anything outside the displayed portion of the chart is clipped away. This has the advantage that a large chart, which does not fit on a page, can be cut into smaller pieces which contain exactly the same \texttt{sseq} code, but different clipping regions.
+% \item Arranging. Multiple symbols dropped at the same integer coordinates will be automatically arranged so that they usually do not overlap. The algorithm for doing this is rather primitive, but still powerful enough for most applications.
+% \item Simplified ``turtle graphics'' syntax. Every primitive element of a chart is typeset with a macro defined by \texttt{sseq}.
+% \item Control structures (loops, if/then, etc.) are allowed inside the \texttt{sseq} environment.
+% \end{itemize}
+%
+%
+% \section{Loading}
+%
+% The |luasseq| package is loaded with
+%
+% |\usepackage{luasseq}|.
+%
+% All options from previous versions of |sseq| are deprecated.
+%
+% \section{Basic usage}
+%
+% \DescribeEnv{sseq}
+% A spectral sequence is typeset by the code
+%
+% |\begin{sseq}[|\meta{options...}|]{|\meta{x-range}|}{|\meta{y-range}|}|
+%
+% \meta{sseq commands...}
+%
+% |\end{sseq}|
+%
+% In the simplest case, a range is of the form \meta{min}|...|\meta{max}, where \meta{min} and \meta{max} are two integers with \meta{min} $\leq$ \meta{max}.
+%
+% Thus |\begin{sseq}{2...5}{-3...-1}\end{sseq}| will typeset
+% \[\begin{sseq}{2...5}{-3...-1}\end{sseq}.\]
+%
+% It is also possible for ranges to be a comma-separated list of ranges of the above form, e.g. |0...3,8...10|. In this case, the chart is broken into several pieces. Here is an example:
+%
+% \noindent\begin{minipage}{\textwidth/2}
+% \begin{verbatim}\begin{sseq}{-1...3,5...8,20...24}
+% {0...2,4...4}
+%\end{sseq}\end{verbatim}\end{minipage}\begin{minipage}{\textwidth/2}\begin{sseq}{-1...3,5...8,20...24}{0...2,4...4}\end{sseq}\bigskip\end{minipage}
+%
+% The minimum value of one block always has to be at least two greater than the maximum of the previous block; |0...2,3...5| is illegal.
+%
+%
+% The following options are defined for the |sseq| environment. Options in bold face are the default setting.
+% \begin{description}
+% \item[|grid=|]\meta{none,\textbf{crossword},go,dots,chess} Select an option of drawing the background grid.
+%
+% \begin{tabular}{ccccc}
+% none & crossword & go & dots & chess\\
+% \begin{sseq}[entrysize=2mm,labels=none,grid=none]{4}{4}\end{sseq} &
+% \begin{sseq}[entrysize=2mm,labels=none,grid=crossword]{4}{4}\end{sseq} &
+% \begin{sseq}[entrysize=2mm,labels=none,grid=go]{4}{4}\end{sseq} &
+% \begin{sseq}[entrysize=2mm,labels=none,grid=dots]{4}{4}\end{sseq} &
+% \begin{sseq}[entrysize=2mm,labels=none,grid=chess]{4}{4}\end{sseq}
+% \end{tabular}
+%
+% With the |chess| option, all squares with coordinates (x,y) with x+y even are white.
+% \item[|gridstroke=|\meta{thickness}] For the grid types |crossword| and |go|, this sets the line width. The default is .1pt. A good option is to set it to the resolution of your output device.
+%
+% \item[|gapsize=|\meta{size},|xgapsize=|\meta{size},|ygapsize=|\meta{size}] This sets the size of the gap between two pieces in a range (i.e. in the above example, the distance between the $3$-column and the $5$-column). The |gapsize| option sets both |xgapsize| and |ygapsize| to the same given value. The default is 3mm.
+%
+% \item[|entrysize=|\meta{size}] Specify the size of each square of the grid. The default is 4mm.
+% \item[|labels=|\meta{labels},|xlabels=|\meta{labels},|ylabels=|\meta{labels}] Specify how labels on the x- and y-axis are drawn, respectively. The |labels| option sets both |xlabels| and |ylabels| to the same. Possible values are |none|, |numbers|, or an explicit list of semicolon-separated labels |{|\meta{$x_1$}|;|\meta{$x_2$}|;|\dots|}| which will be typeset in math mode. If the range consists of more than one block, the labels for the separate blocks are separated by a comma; see the example below. The default is |numbers|. Inside the label strings, one can use the following placeholder:
+% \begin{description}
+% \item[|\&n|] will be replaced by the coordinate
+% \item[|\&c|] will be replaced by the number of the piece in a multi-piece range (starting with 0)
+% \item[|\&i|] will be replaced by the index within the current piece (starting with 0)
+% \end{description}
+% \item[|labelstep=|\meta{step},|xlabelstep=|\meta{step},|ylabelstep=|\meta{step}] Frequently it is not desirable that every label is printed, but only every second or third label. This can be done by setting this option to a positive integer. The default is 2.
+%
+% The following example illustrates how labels can be customized:
+%
+% \noindent\begin{minipage}{\textwidth*2/3}
+% \begin{verbatim}\begin{sseq}[xlabelstep=1,ylabelstep=4,
+% xlabels={x_{&n},y_1;y_2;y_3}]
+% {1...3,10...11}{0...4}
+%\end{sseq}\end{verbatim}\end{minipage}\begin{minipage}{\textwidth/3}\begin{sseq}[xlabelstep=1,ylabelstep=4,xlabels={x_{&n},y_1;y_2;y_3}]{1...3,10...11}{0...4}\end{sseq}\end{minipage}
+% \item[|leak=|\meta{size},|xleak=|\meta{size},|yleak=|\meta{size}] When a line is drawn from within the visible range of the chart to a point outside, or vice versa, this line will protrude beyond the boundaries of the grid. These values define how far; the default is one third of |gapsize|. Do not set this to a value larger than half of |gapsize|.
+% \item[|arrows=|\meta{arrow type}] Sets the default arrow type to use in the spectral sequence. Here are some default arrow types:
+%
+%\begin{sseq}[grid=none,labels=none]{0...3}{-2...0}
+% \ssdrop{} \ssmove 3 0 \ssdrop{\;to} \ssstroke[arrowto=to] \ssmove {-3}{-1}
+% \ssdrop{} \ssmove 3 0 \ssdrop{\;stealth} \ssstroke[arrowto=stealth] \ssmove {-3}{-1}
+% \ssdrop{} \ssmove 3 0 \ssdrop{\;latex} \ssstroke[arrowto=latex]
+%\end{sseq}
+%
+% Other arrow types can be defined by the user or loaded from a library; see the pgf package documentation for details.
+%
+% \item[|packing=|] \meta{\textbf{auto},horizontal,vertical,diagonal} Specify which algorithm you want to use to arrange multiple objects in a grid square. The following charts illustrate the effect:
+%
+% \begin{sseq}[packing=auto,ylabels={\hbox to 1.8cm{auto}},xlabelstep=1,entrysize=.6cm]{1...5}{0...0}
+% \ssmove 1 0 \ssdropbull\ssmove 1 0 \ssdropbull\ssdropbull
+% \ssmove 1 0 \ssdropbull\ssdropbull\ssdropbull
+% \ssmove 1 0 \ssdropbull\ssdropbull\ssdropbull\ssdropbull
+% \ssmove 1 0 \ssdropbull\ssdropbull\ssdropbull\ssdropbull\ssdropbull
+% \end{sseq}
+% \hspace{1cm}
+% \begin{sseq}[packing=diagonal,ylabels={\hbox to 1.8cm{diagonal}},xlabelstep=1,entrysize=.6cm]{1...5}{0...0}
+% \ssmove 1 0 \ssdropbull\ssmove 1 0 \ssdropbull\ssdropbull
+% \ssmove 1 0 \ssdropbull\ssdropbull\ssdropbull
+% \ssmove 1 0 \ssdropbull\ssdropbull\ssdropbull\ssdropbull
+% \ssmove 1 0 \ssdropbull\ssdropbull\ssdropbull\ssdropbull\ssdropbull
+% \end{sseq}
+% \\[.5cm]
+% \begin{sseq}[packing=vertical,ylabels={\hbox to 1.8cm{vertical}},xlabelstep=1,entrysize=.6cm]{1...5}{0...0}
+% \ssmove 1 0 \ssdropbull\ssmove 1 0 \ssdropbull\ssdropbull
+% \ssmove 1 0 \ssdropbull\ssdropbull\ssdropbull
+% \ssmove 1 0 \ssdropbull\ssdropbull\ssdropbull\ssdropbull
+% \ssmove 1 0 \ssdropbull\ssdropbull\ssdropbull\ssdropbull\ssdropbull
+% \end{sseq}
+% \hspace{1cm}
+% \begin{sseq}[packing=horizontal,ylabels={\hbox to 1.8cm{horizontal}},xlabelstep=1,entrysize=.6cm]{1...5}{0...0}
+% \ssmove 1 0 \ssdropbull\ssmove 1 0 \ssdropbull\ssdropbull
+% \ssmove 1 0 \ssdropbull\ssdropbull\ssdropbull
+% \ssmove 1 0 \ssdropbull\ssdropbull\ssdropbull\ssdropbull
+% \ssmove 1 0 \ssdropbull\ssdropbull\ssdropbull\ssdropbull\ssdropbull
+% \end{sseq}
+%
+% \bigskip
+% \emph{Notice for advanced users:} you can define your own packing algorithm, say |mypack|, by defining a Lua function |sseq_packing_mypack(i,n)| which returns a pair of coordinates (in \TeX{} scaled points) indicating the offset of the center of object |i| out of a total of |n| dropped objects from the lower left corner of the square.
+% \end{description}
+%
+% \section{sseq commands}
+%
+% Inside an |sseq| environment there is defined a virtual cursor, which starts out at position $(0,0)$ (even if that position is not within the visible range!). Most drawing commands are relative to the current cursor position; this facilitates reusage of |sseq| code when a certain pattern has to be repeated, as is often the case in mathematical spectral sequences.
+%
+%
+% \ifthenelse{0=1}{
+%
+%
+
+\directlua0{dofile(kpse.find_file("luasseq.lua","lua"))}
+
+
+ \definecolor{sseqgr}{gray}{0.65}
+ \definecolor{sslightgr}{gray}{0.9}
+ \definecolor{ssred}{rgb}{1,0,0}
+ \definecolor{ssblack}{rgb}{0,0,0}
+\newcommand{\ssconncolor}{ssblack}
+\newcommand{\sslabelcolor}{ssblack}
+\newcommand{\ssplacecolor}{ssblack}
+
+%% ^^A Initialization
+\newcount\sseqxstart \sseqxstart=0
+\newcount\sseqystart \sseqystart=0
+\newdimen\sseq@dimen % ^^A To convert dimensions to scaled points (in TeX)
+\newbox\sseqbox % ^^A To compute the size of dropped objects (in TeX)
+
+
+
+
+
+\long\def\for #1=#2\to#3\do#4\od{#1=#2 \sseq@for #1\to #3\do #4\od}
+\long\def\sseq@for #1\to #2\do #3\od{%
+ \ifnum #1>#2 \else
+ #3\relax
+ \advance #1 by 1
+ \def\tempcmd{\sseq@for #1\to #2\do #3\od}%
+ \expandafter\tempcmd
+ \fi
+}
+
+\def\@eval#1#2{%
+ \edef\tempcmd{\noexpand#1#2}%
+ \tempcmd
+}
+
+%
+%
+%
+% ^^A This replaces \ssplacespecial in the xy version.
+% ^^A Arguments: 1,2 = x- and y-coordinates
+% ^^A 3 = pgf shape
+% ^^A 4 = pgf path usage
+% ^^A 5 = contents
+%
+\def\ss@placespecial#1#2#3#4#5{
+\directlua0{sseq_drop_object(#1,#2,[[#3]],[[#4]],[[#5]],[[\ssplacecolor]])}}
+
+\newcommand{\ssplace}[3]{
+ \ss@placespecial{#1}{#2}{rectangle}{discard}{#3}
+}
+
+\newcommand{\ssplaceboxed}[3]{
+ \ss@placespecial{#1}{#2}{rectangle}{stroke}{\ensuremath{#3}}
+}
+
+\newcommand{\ssplacecircled}[3]{
+ \ss@placespecial{#1}{#2}{circle}{stroke}{\ensuremath{#3}}
+}
+
+% }{}
+%
+% \DescribeMacro{\ssmoveto}
+% To move the cursor to the absolute position (x,y), use |\ssmoveto{x}{y}|.
+%
+% \ifthenelse{0=1}{
+\newcommand{\ssmoveto}[2]{\directlua0{sseq_moveto(#1,#2)}}
+% }{}
+% \DescribeMacro{\ssmove}
+% To move the cursor relative to the current position by (x,y), use |\ssmove{x}{y}|.
+%
+% \iffalse
+\newcommand{\ssmove}[2]{\directlua0{sseq_move(#1,#2)}}
+% \fi
+%
+%
+% \DescribeMacro{\ssdrop}
+% The command |\ssdrop[|\meta{options}|]{|\meta{mathcode}|}| will display \emph{mathcode} at the current cursor position. The argument is always interpreted in math mode.
+% The following options can be given:
+% \begin{description}
+% \item[|circled|] A circle is drawn around the object.
+% \item[|boxed|] A box is drawn around the object.
+% \item[|color=|\meta{color}] The object is drawn in the specified color. Any \LaTeX{} color can be used, e.g. predefined colors such as |black|, |blue|, |PineGreen|, etc., or user defined rgb colors.
+% \item[|name=|\meta{name}] This is equivalent to issuing |\ssname{|\meta{name}|}| after |\ssdrop|, see below.
+% \end{description}
+% If the argument is |\bullet|, |\circle|, or |\square|, the object is replaced by a better-spaced graphics primitive.
+%
+% \ifthenelse{0=1}{
+\define@key{sseqdrop}{circled}[true]{\newif\ifKV@sseqdrop@circled\csname KV@sseqdrop@circled#1\endcsname}
+\define@key{sseqdrop}{boxed}[true]{\newif\ifKV@sseqdrop@boxed\csname KV@sseqdrop@boxed#1\endcsname}
+\define@key{sseqdrop}{color}{\def\sseq@color{#1}}
+\define@key{sseqdrop}{name}{\def\sseq@name{#1}}
+%
+\def\ssdrop{\@ifnextchar[\sseq@drop{\sseq@drop[]}}
+\def\sseq@drop[#1]#2{
+ \presetkeys{sseqdrop}{circled=false,boxed=false,color=\ssplacecolor,name=\relax}{}
+ \setkeys{sseqdrop}{#1}
+ \ifKV@sseqdrop@circled
+ \directlua0{sseq_drop_object_here("circle","stroke",[[#2]],"\sseq@color")}
+ \else
+ \ifKV@sseqdrop@boxed
+ \directlua0{sseq_drop_object_here("rectangle","stroke",[[#2]],"\sseq@color")}
+ \else
+ \directlua0{sseq_drop_object_here("rectangle","discard",[[#2]],"\sseq@color")}
+ \fi
+ \fi
+ \def\temp{\relax}
+ \ifx\sseq@name\temp \else
+ \@eval\ssname{{\sseq@name}}
+ \fi
+}
+% }{}
+%
+% ^^A The following are commands purely kept for compatibility.
+%
+% \iffalse
+%
+\newcommand{\ssdropbull}{\ssdrop{\bullet}}
+\newcommand{\ssdropboxed}[1]{\ssdrop[boxed]{#1}}
+\newcommand{\ssdropcircled}[1]{\ssdrop[circled]{#1}}
+% \fi
+%
+% \DescribeMacro{\ssname}
+% |\ssname{|\meta{name}|}| gives the object most recently dropped the name \meta{name}. If the previous command is one of the drop commands, then it refers to that object; if it is not, then if there is one and only one object at the current cursor position, it refers to that object; if that is also not the case, an error message is generated. \emph{New in luasseq:} The argument |name| is a space-separated list of alphanumerical strings, such as |3 alpha beta2 beta2|; the order of the strings is irrelevant, thus this name is equal to |beta2 alpha 3 beta2|.
+%
+% \iffalse
+\def\ssname#1{\directlua0{sseq_name("#1")}}
+% \fi
+%
+% \DescribeMacro{\ssgoto}
+% After an object has been given a \meta{name} with |\ssname|, the cursor can be moved back to that object at any time by issuing |\ssgoto{|\meta{name}|}|. This becomes necessary when there is more than one object in one position.
+%
+% \iffalse
+\newcommand{\ssgoto}[1]{\directlua0{sseq_goto("#1")}}
+% \fi
+%
+% \DescribeMacro{\ssprefix}
+% Often the mechanism provided by |\ssname|/|\ssgoto| is not flexible enough to deal with repeated code. In that case, |\ssprefix{|\meta{prefix}|}| defines a ``multiplier'' for all names that follow; i.e. a |\ssname{|\meta{name}|}| after such a command will really define a name \meta{prefix} \meta{name} (with a space in between, so that the order is irrelevant). However, since |\ssgoto| also observes the prefix, |\ssgoto{|\meta{name}|}| will still work. |\ssprefix| commands can be iterated; the prefices are then concatenated.
+%
+% \iffalse
+\newcommand{\ssprefix}[1]{\directlua0{sseq_prefix("#1")}}
+% \fi
+%
+% \DescribeMacro{\ssresetprefix}
+% This command resets the prefix defined by (a sequence of) |\ssprefix| to the empty prefix.
+%
+% \iffalse
+\newcommand{\ssresetprefix}{\directlua0{sseq_reset_prefix()}}
+% \fi
+%
+% \DescribeMacro{\ssabsgoto}
+% This is a version of |\ssgoto| that ignores the current prefix.
+%
+% \ifthenelse{0=1}{
+\newcommand{\ssabsgoto}[1]{\directlua0{sseq_abs_goto("#1")}}
+% }{}
+%
+% \DescribeMacro{\ssdroplabel}
+% This command decorates the previously typeset object with a label. It is used in the form
+% |\ssdroplabel[|\meta{options...}|]{|\meta{label}|}|.
+%
+% The \meta{label} will then be typeset next to the most recently dropped object (for a definition for what that is, exactly, consult the description of |\ssname|). If you specify one of |U|,|LU|,|RU|,|L|,|R|,|LD|,|RD|,|D| as an option, the label is positioned relative to the object it labels (default: |U|=up).
+% As in |\ssdrop|, an option |color=|\meta{color} will typeset the label in the \LaTeX{} color \meta{color}.
+%
+% \ifthenelse{0=1}{
+\define@key{sseqlabel}{color}{\def\sseq@color{#1}}
+\define@key{sseqlabel}{pos}{\def\sseq@position{#1}}
+\define@key{sseqlabel}{U}[]{\def\sseq@position{U}}
+\define@key{sseqlabel}{LU}[]{\def\sseq@position{LU}}
+\define@key{sseqlabel}{L}[]{\def\sseq@position{L}}
+\define@key{sseqlabel}{LD}[]{\def\sseq@position{LD}}
+\define@key{sseqlabel}{D}[]{\def\sseq@position{D}}
+\define@key{sseqlabel}{RD}[]{\def\sseq@position{RD}}
+\define@key{sseqlabel}{R}[]{\def\sseq@position{R}}
+\define@key{sseqlabel}{RU}[]{\def\sseq@position{RU}}
+%
+\def\ssdroplabel{\@ifnextchar[\sseq@droplabel{\sseq@droplabel[]}}
+\def\sseq@droplabel[#1]#2{
+ \def\sseq@position{L}
+ \presetkeys{sseqlabel}{color=\ssplacecolor}{}
+ \setkeys{sseqlabel}{#1}
+ \directlua0{sseq_drop_label("\sseq@position","\sseq@color",[[#2]])}
+}
+% }{}
+%
+% \DescribeMacro{\ssdropextension}
+% The command |\ssdropextenstion[|\meta{options...}|]| has only optional arguments and is rather specialized. It refers to a previously dropped object (see |\ssname|), draws a circle or box around it (default: a circle, can be changed by giving the option |boxed|, and considers that circle a new object. Thus it produces a new object that is attached to the original object, and not subject to the packing algorithm that tries to make objects non-overlapping. Further options are |color| and |name| with the same usage as in |\ssdrop|.
+%
+% \ifthenelse{0=1}{
+\define@key{sseqdropext}{circled}[true]{\newif\ifKV@sseqdrop@circled\csname KV@sseqdrop@circled#1\endcsname}
+\define@key{sseqdropext}{boxed}[true]{\newif\ifKV@sseqdrop@boxed\csname KV@sseqdrop@boxed#1\endcsname}
+\define@key{sseqdropext}{color}{\def\ssplacecolor{#1}}
+\define@key{sseqdropext}{name}{\def\sseq@name{#1}}
+%
+\def\ssdropextension{\@ifnextchar[\sseq@dropextension{\sseq@dropextension[]}}
+\def\sseq@dropextension[#1]{
+ \edef\sseq@savecolor{\ssplacecolor}
+ \def\sseq@name{\relax}
+ \presetkeys{sseqdropext}{circled=false,boxed=false}{}
+ \setkeys{sseqdrop}{#1}
+ \ifKV@sseqdrop@boxed
+ \directlua0{sseq_drop_extension("rectangle","stroke","\sseq@color")}
+ \else
+ \directlua0{sseq_drop_extension("circle","stroke","\sseq@color")}
+ \fi
+
+ \def\temp{\relax}
+ \ifx\sseq@name\temp \else
+ \@eval\ssname{{\sseq@name}}
+ \fi
+}
+%
+% }{}
+%
+%
+% \DescribeMacro{\ssstroke}
+% There are two ways of typesetting connections between objects. One of them is the command |\ssstroke|, which
+% requires that the cursor recently moved from one object to another. It takes no non-optional arguments and typesets a
+% line between the two objects. Example: Suppose there are two objects, which have been given the names |a| and |b|
+% by |\ssname|. Drawing a line between them is achieved by the command |\ssgoto{a} \ssgoto{b} \ssstroke|.
+%
+% The following options can be given in the form |\ssstroke[options]|:
+% \begin{description}
+% \item[|color=|color] the connection is drawn in the given \LaTeX{} color
+% \item[|curve=|value] the connection is curved to the left by an amount proportional to the value given.
+% \item[{|dashed|[=dashing type]}] the connection is drawn in a dashed style. The optional \emph{dashing type} is an
+% expression of the form |{{a}{b}...}|, where each of a, b, \dots is a length (like 2pt or 3mm). The line then consists of
+% a stroke of length |a|, followed by a gap of length |b|, followed by a stroke of length ... etc.
+% \item[{|dotted|[=dashing type]}] the connection is drawn in a dotted style. If the optional dashing type is given,
+% this option behaves exactly like |dashed|.
+% \item[{|arrowfrom|[=arrow style]}] An arrow is drawn at the beginning of the line. The global arrow style can be overridden
+% by specifying an arrow style.
+% \item[{|arrowto|[=arrow style]}] An arrow is drawn at the end of the line.
+% \item[|void|] This option says that the target of the connection is not another object; instead, the connection should just point into the correct direction.
+% \end{description}
+%
+% \ifthenelse{0=1}{
+%
+\define@key{sseqstroke}{color}{\def\sseq@color{#1}}
+\define@key{sseqstroke}{curve}{\def\sseq@curve{#1}}
+\define@key{sseqstroke}{dashed}[{{2pt}{2pt}}]{\def\sseq@dashing{#1}}
+\define@key{sseqstroke}{dotted}[{{.5pt}{2pt}}]{\def\sseq@dashing{#1}}
+\define@key{sseqstroke}{arrowfrom}[\sseq@defaultarrowstyle]{\def\sseq@arrowfrom{\ssinversearrowhead[#1]}}
+\define@key{sseqstroke}{arrowto}[\sseq@defaultarrowstyle]{\def\sseq@arrowto{\ssarrowhead[#1]}}
+\define@key{sseqstroke}{void}[true]{\newif\ifKV@sseqstroke@void\csname KV@sseqstroke@void#1\endcsname}
+%
+\def\ssstroke{\@ifnextchar[\sseq@stroke{\sseq@stroke[]}}
+\def\sseq@stroke[#1]{
+ \def\sseq@dashing{}
+ \def\sseq@curve{}
+ \def\sseq@arrowfrom{}
+ \def\sseq@arrowto{}
+ \presetkeys{sseqstroke}{void=false,color=\ssconncolor}{}
+ \setkeys{sseqstroke}{#1}
+ \ifKV@sseqstroke@void % ^^A a void line
+ \directlua0{sseq_late_connection("\sseq@dashing","\sseq@curve","\sseq@color",false,true)}
+ \else
+ \directlua0{sseq_late_connection("\sseq@dashing","\sseq@curve","\sseq@color",false,false)}
+ \fi
+ \sseq@arrowfrom
+ \sseq@arrowto
+}
+% }{}
+%
+% ^^A Keep these for compatibility
+% \ifthenelse{0=1}{
+\newcommand{\sscurve}[1]{\sseq@stroke[curve=#1]}
+\newcommand{\ssdashedstroke}{\sseq@stroke[dashed]}
+\newcommand{\ssdashedcurve}[1]{\sseq@stroke[dashed,curve=#1]}
+\newcommand{\ssdottedstroke}{\sseq@stroke[dotted]}
+\newcommand{\ssdottedcurve}[1]{\sseq@stroke[dotted,curve=#1}
+% }{}
+%
+% \DescribeMacro{\ssarrowhead}
+% \DescribeMacro{\ssinversearrowhead}
+% Instead of specifying |arrowfrom| or |arrowto| in |ssstroke|, you can issue these commands afterwards to typeset an arrow head onto the beginning resp. end of the connection most recently typeset. There is one optional parameter |[|\meta{arrow style}|]| which selects the arrow tips of style \meta{arrow style}, cf. the documentation of |\ssstroke|.
+%
+% \ifthenelse{0=1}{
+\newcommand{\ssarrowhead}[1][\sseq@defaultarrowstyle]{\directlua0{sseq_add_arrow("arrowto","#1")}}
+\newcommand{\ssinversearrowhead}[1][\sseq@defaultarrowstyle]{\directlua0{sseq_add_arrow("arrowfrom","#1")}}
+% }{}
+%
+% \DescribeMacro{\ssline}
+% A second way of producing connections is |\ssline[|\meta{options...}|]{|\meta{x}|}{|\meta{y}|}|. This command draws a connection from the most recent object (cf. |\ssname|) to an object at relative position (\meta{x},\meta{y}), and it moves that cursor to that new position. If |\ssline| is followed by a drop command, then the line is attached to this newly dropped object (note the slightly out-of-order execution!), no matter how many other objects there are at the target position. However, if it is not followed by a drop command, then there has to be one and only one object at the target position, otherwise an error message is generated. The options are exactly the same as for |\ssstroke|.
+%
+% \ifthenelse{0=1}{
+%
+\define@key{sseqline}{color}{\def\sseq@color{#1}}
+\define@key{sseqline}{curve}{\def\sseq@curve{#1}}
+\define@key{sseqline}{dashed}[{{2pt}{2pt}}]{\def\sseq@dashing{#1}}
+\define@key{sseqline}{dotted}[{{.5pt}{2pt}}]{\def\sseq@dashing{#1}}
+\define@key{sseqline}{arrowfrom}[\sseq@defaultarrowstyle]{\def\sseq@arrowfrom{\ssinversearrowhead[#1]}}
+\define@key{sseqline}{arrowto}[\sseq@defaultarrowstyle]{\def\sseq@arrowto{\ssarrowhead[#1]}}
+\define@key{sseqline}{void}[true]{\newif\ifKV@sseqline@void\csname KV@sseqline@void#1\endcsname}
+%
+\def\ssline{\@ifnextchar[\sseq@line{\sseq@line[]}}
+\def\sseq@line[#1]#2#3{
+ \def\sseq@dashing{}
+ \def\sseq@curve{}
+ \edef\sseq@arrowfrom{}
+ \edef\sseq@arrowto{}
+ \presetkeys{sseqline}{void=false,color=\ssconncolor}{}
+ \setkeys{sseqline}{#1}
+ \ifKV@sseqline@void % ^^A a void line
+ \directlua0{sseq_assert_source(); sseq_finish_pos(); sseq_move(#2,#3); sseq_late_connection("\sseq@dashing","\sseq@curve","\sseq@color",false,true)}
+ \else
+ \directlua0{sseq_open_connection("\sseq@dashing","\sseq@curve","\sseq@color",false,false); sseqposx = sseqposx+#2; sseqposy = sseqposy+#3}
+ \fi
+ \sseq@arrowfrom
+ \sseq@arrowto
+}
+\newcommand{\sscurvedline}[3]{\ssline[curve=#3]{#1}{#2}}
+\newcommand{\ssdashedline}[2]{\ssline[dashed]{#1}{#2}}
+\newcommand{\sscurveddashedline}[3]{\ssline[curve=#3,dashed]{#1}{#2}}
+% }{}
+%
+% \DescribeMacro{\ssarrow}
+% |\ssarrow[|\meta{options...}|]{|\meta x|}{|\meta y|}| is an abbreviation for\\
+% |\ssline[|\meta{options...}|,arrowto]{|\meta x|}{|\meta y|}|
+%
+% \ifthenelse{0=1}{
+\newcommand{\ssarrow}[3][]{
+ \ssline[arrowto,#1]{#2}{#3}
+}
+\newcommand{\sscurvedarrow}[3]{\ssline[arrowto,curve=#3]{#1}{#2}}
+\newcommand{\ssdashedarrow}[2]{\ssline[arrowto,dashed]{#1}{#2}}
+\newcommand{\sscurveddashedarrow}[3]{\ssline[arrowto,dashed,curve=#3]{#1}{#2}}
+%
+%
+\def\ssvoidline{\@ifnextchar[\sseq@voidline{\sseq@voidline[]}}
+\def\sseq@voidline[#1]#2#3{
+ \def\sseq@dashing{}
+ \def\sseq@curve{}
+ \edef\sseq@arrowfrom{}
+ \edef\sseq@arrowto{}
+ \presetkeys{sseqline}{color=\ssconncolor}{}
+ \setkeys{sseqline}{#1}
+ \directlua0{sseq_void_line("\sseq@dashing","\sseq@curve","\sseq@color",#2,#3)}
+ \sseq@arrowfrom
+ \sseq@arrowto
+}
+
+\newcommand{\ssvoidarrow}[2]{\ssvoidline{#1}{#2}\ssarrowhead}
+\newcommand{\ssinversevoidarrow}[2]{\ssvoidline{#1}{#2}\ssinversearrowhead}
+
+% }{}
+%
+% \DescribeMacro{\ssbullstring}
+% |\ssbullstring{x}{y}{n}| is a shortcut for |\ssdrop{\bullet}| followed by $n-1$ copies of |\ssline{x}{y} \ssdrop{\bullet}|.
+% \iffalse
+\newcommand{\ssbullstring}[3]{
+\directlua0{sseq_bullstring(#1,#2,#3,[[\ssplacecolor]])}
+}
+% \fi
+%
+% \DescribeMacro{\ssinfbullstring}
+% |\ssinfbullstring{x}{y}{n}| is a shortcut for |\ssdrop{\bullet}| followed by $n-1$ copies of |\ssline{x}{y} \ssdrop{\bullet}|, followed by |\ssarrow[void]{x}{y}|. The cursor finishes on the last bullet.
+%
+% \iffalse
+\newcommand{\ssinfbullstring}[3]{
+\ssbullstring{#1}{#2}{#3}
+\ssvoidarrow{#1}{#2}
+}
+% \fi
+%
+% \DescribeMacro{\ssgrayout}
+% (new in |luasseq|) This command, which has as only optional argument a color, can be given at any point in the |sseq| code and changes the color of the most recent object (cf. |\ssname|) to the color given (gray by default), along with all lines connected to this object. Any line drawn to that object after issuing |ssgrayout| will not be affected. This is used for typesetting differentials in spectral sequences.
+%
+% \iffalse
+\newcommand{\ssgrayout}[1][gray]{\directlua0{sseq_grayout("#1")}}
+% \fi
+%
+% \section{Examples}
+%
+%
+% \newtheorem{ex}{Example}
+%
+%\begin{ex}
+%The following code generates a $5 \times 5$ grid with labels between $-2$ and $2$. The size of every square is $(.8cm)^2$, and labels are written on every square. The grid is chess-style. A bullet is drawn at coordinate (0,0).
+%
+%\noindent
+%\begin{minipage}{\textwidth/2}
+%\begin{verbatim}
+%\begin{sseq}[grid=chess,labelstep=1,
+% entrysize=.8cm]{-2...2}{-2...2}
+%\ssdrop{\bullet}
+%\end{sseq}
+%\end{verbatim}
+%\end{minipage}
+%\begin{minipage}{\textwidth/2}
+%\def\sseqgridstyle{\ssgridchess}
+%\begin{sseq}[grid=chess,labelstep=1,entrysize=.8cm]{-2...2}{-2...2}
+%\ssdropbull
+%\end{sseq}
+%\end{minipage}
+%\end{ex}
+%
+%
+%\begin{ex}
+%This example demonstrates how to move the cursor and drop objects and labels. Note how the last bullet, which is dropped at position (8,4), is outside the grid and thus clipped. The grid style is the default (|crossword|). \bigskip
+%
+%\noindent
+%\begin{minipage}{\textwidth/2}
+%\begin{verbatim}
+%\begin{sseq}{0...6}{0...6}
+%\ssdrop{\bullet}
+%\ssdroplabel[U]{(0,0)}
+%\ssmove 2 1
+%\ssdrop{\clubsuit}
+%\ssdropextension
+%\ssdropextension
+%\ssdropextension
+%\ssdroplabel[RU]{(2,1)}
+%\ssmove 0 4
+%\ssdropcircled{8}
+%\ssmoveto 5 4
+%\ssdropboxed{\spadesuit}
+%\ssdroplabel[R]{(5,4)}
+%\ssmove 3 0
+%\ssdrop{\bullet}
+%\end{sseq}
+%\end{verbatim}
+%\end{minipage}
+%\begin{minipage}{\textwidth/2}
+%\begin{sseq}{0...6}{0...6}
+%\ssdrop{\bullet}
+%\ssdroplabel[U]{(0,0)}
+%\ssmove 2 1
+%\ssdrop{\clubsuit}
+%\ssdropextension
+%\ssdropextension
+%\ssdropextension
+%\ssdroplabel[RU]{(2,1)}
+%\ssmove 0 4
+%\ssdropcircled{8}
+%\ssmoveto 5 4
+%\ssdropboxed{\spadesuit}
+%\ssdroplabel[R]{(5,4)}
+%\ssmove 3 0
+%\ssdrop{\bullet}
+%\end{sseq}
+%\end{minipage}
+%\end{ex}
+%
+%\begin{ex}
+%This example illustrates the different ways of drawing connections.
+%\bigskip
+%
+%\noindent
+%\begin{minipage}{.6\textwidth}
+%\begin{verbatim}
+%\begin{sseq}[grid=go]{0...9}{0...6}
+%\ssdrop{\bullet}
+%\ssmove 4 4
+%\ssdrop{\bullet} \ssstroke
+%\ssstroke[curve=-.5]
+%\ssstroke[curve=.1,dashed] \ssarrowhead
+%\ssmove 4 0 \ssdropcircled{T}
+%\ssstroke
+%\ssmoveto 0 6
+%\ssdrop{\bullet}
+%\ssline {1} {-1} \ssdrop{\bullet}
+%\ssline[curve=.2] 7 {-1}
+%\end{sseq}
+%\end{verbatim}
+%\end{minipage}
+%\begin{minipage}{.4\textwidth}
+%\begin{sseq}[grid=go]{0...9}{0...6}
+%\ssdrop{\bullet}
+%\ssmove 4 4
+%\ssdrop{\bullet} \ssstroke
+%\ssstroke[curve=-.5]
+%\ssstroke[curve=.1,dashed] \ssarrowhead
+%\ssmove 4 0 \ssdropcircled{T}
+%\ssstroke
+%\ssmoveto 0 6
+%\ssdrop{\bullet}
+%\ssline {1} {-1} \ssdrop{\bullet}
+%\ssline[curve=.2] 7 {-1}
+%\end{sseq}
+%\end{minipage}
+%\end{ex}
+%
+%\begin{ex}
+%This sample code shows how to use names for objects dropped in spectral sequence; this is particularly useful when more than one item is dropped at one position. It also demonstrates void arrows, which do not need a target.
+%\bigskip
+%
+%\noindent
+%\begin{minipage}{\textwidth*2/3}
+%\begin{verbatim}
+%\begin{sseq}[grid=dots]{0...4}{0...4}
+%\ssdrop{\bullet} \ssname{a} \ssvoidarrow 0 1
+%\ssdrop{\bullet} \ssname{b} \ssvoidarrow 0 1
+%\ssdrop{\bullet} \ssname{c} \ssvoidarrow 0 1
+%\ssdropextension \ssname{d}
+%\ssmove 4 4
+%\ssdrop{\bullet} \ssname{e}
+%\ssdrop{\bullet} \ssname{f}
+%\ssgoto a \ssgoto f \ssstroke
+%\ssgoto e \ssstroke
+%\ssgoto d \sscurve{.2}
+%\ssline 4 0 \ssdrop{\bullet}
+%\end{sseq}
+%\end{verbatim}
+%\end{minipage}
+%\begin{minipage}{\textwidth/3}
+%
+%\begin{sseq}[grid=dots]{0...4}{0...4}
+%\ssdrop{\bullet} \ssname{a}\ssvoidarrow 0 1
+%\ssdrop{\bullet} \ssname{b}\ssvoidarrow 0 1
+%\ssdrop{\bullet} \ssname{c}\ssvoidarrow 0 1
+%\ssdropextension \ssname{d}
+%\ssmove 4 4
+%\ssdrop{\bullet} \ssname{e}
+%\ssdrop{\bullet} \ssname{f}
+%\ssgoto a \ssgoto f \ssstroke
+%\ssgoto e \ssstroke
+%\ssgoto d \sscurve{.2}
+%\ssline 4 0 \ssdrop{\bullet}
+%\end{sseq}
+%\end{minipage}
+%\end{ex}
+%
+%\begin{ex}
+% This final example shows how to take advantage of loops, prefices, and |\ssgrayout|
+%
+%\noindent
+%\begin{minipage}[t]{\textwidth*1/3}
+%\begin{verbatim}
+%\newcount\cnti
+%\def\drawstring#1#2{
+% \ifnum#2=1
+% \ssdrop{\bullet}
+% \ssname{#1}
+% \else
+% \ssdrop{\bullet}
+% \ssname{#1}
+% \ssline 1 0
+% \ssprefix{i}
+% \cnti=#2
+% \advance \cnti by -1
+% \drawstring{#1}
+% {\the\cnti}
+% \fi
+%}
+%\end{verbatim}
+%\end{minipage}
+%\begin{minipage}[t]{\textwidth*1/3}
+%\begin{verbatim}
+%\def\drawlines#1#2#3{
+% \ifnum#3>0
+% \cnti=#3
+% \ssgoto{#1} \ssgoto{#2}
+% \ssstroke \ssarrowhead
+% \ssprefix{i}
+% \advance \cnti by -1
+% \drawlines{#1}{#2}
+% {\the\cnti}
+% \fi
+%}
+%\end{verbatim}
+%\end{minipage}
+%\begin{minipage}[t]{\textwidth*1/3}
+%\begin{verbatim}
+%\begin{sseq}[grid=none]
+% {0...8}{0...3}
+%\ssmoveto 0 3
+%\drawstring{a}{8}
+%\ssmoveto 0 3
+%\ssresetprefix
+%\drawstring{b}{8}
+%\ssmoveto 0 0
+%\ssresetprefix
+%\drawstring{c}{8}
+%\ssresetprefix
+%\drawlines{c}{a}{8}
+%\ssresetprefix
+%\drawlines{b}{i i c}{6}
+%\ssresetprefix
+%\ssgoto{c}
+%\ssgrayout[red]
+%\ssgoto{c i i i i}
+%\ssgrayout[green]
+%\end{sseq}
+%\end{verbatim}
+%\end{minipage}
+%
+% \bigskip
+%
+% The result is shown in the following chart:
+%\newcount\cnti
+%\def\drawstring#1#2{
+% \ifnum#2=1
+% \ssdrop{\bullet} \ssname{#1}
+% \else
+% \ssdrop{\bullet} \ssname{#1}
+% \ssline 1 0
+% \ssprefix{i}
+% \cnti=#2
+% \advance \cnti by -1
+% \drawstring{#1}
+% {\the\cnti}
+% \fi
+%}
+%\def\drawlines#1#2#3{
+% \ifnum#3>0
+% \cnti=#3
+% \ssgoto{#1} \ssgoto{#2}
+% \ssstroke \ssarrowhead
+% \ssprefix{i}
+% \advance \cnti by -1
+% \drawlines{#1}{#2}
+% {\the\cnti}
+% \fi
+%}
+%\begin{tabular}{l}
+%\begin{sseq}[grid=none]{0...8}{0...3}
+%\ssmoveto 0 3
+%\drawstring{a}{8}
+%\ssmoveto 0 3
+%\ssresetprefix
+%\drawstring{b}{8}
+%\ssmoveto 0 0
+%\ssresetprefix
+%\drawstring{c}{8}
+%\ssresetprefix
+%\drawlines{c}{a}{8}
+%\ssresetprefix
+%\drawlines{b}{i i c}{6}
+%\ssresetprefix
+%\ssgoto{c} \ssgrayout[red]
+%\ssgoto{c i i i i} \ssgrayout[green]
+%\end{sseq}
+%\end{tabular}
+%\end{ex}
+%
+%\section{Final remarks}
+%
+% This package has been extremely helpful for my own mathematical work, and it most likely carries the characteristics of a tool initially developed for my own purposes only. Before any published version, there was a version of |sseq| which was much less powerful; and what is worse, this version is not fully upward compatible with the previous one. (Every object that was dropped was forgotten right afterwards; thus connections could not properly connect objects but were always drawn from the center of the box corresponding to a coordinate to the center of the box corresponding to the target coordinate, resulting in fairly ugly pictures.) The published version 1.0 of |sseq| used xy-Pic and had most of the functionality of the current package, but was extremely slow and a memory hog. Version 2.0 of |sseq| was recoded using the graphics package pgf, improving the typesetting greatly. The current version 2.1 of luasseq migrated most of the \TeX code to Lua, which greatly improved typesetting speed and memory requirements, while enabling new features such as |\ssgrayout|, more flexible labelling and packing algorithms, and, again, type quality.
+%
+% Many things remain to be desired:
+%
+% \begin{itemize}
+% \item While objects are placed next to each other, no attempt is made not to make connections overlap
+% \item Labelling lines between objects is not supported.
+% \item Have some features that make repetitions of sseq code redundant (which occurs for example when an $E^r$ term of a spectral sequence has a polynomial generator).
+% \end{itemize}
+%
+% Given time and leisure, I might or might not implement one or more of these improvements and make them available; of course, I would be even more happy if somebody else did it. (Needless to say, I would request that I be informed of and sent the enhancements.) I do guarantee that all further versions of |luasseq| that might or might not be written by me will be compatible with the documented code written for this version.
+%
+% \endinput
+%
+%
+%
+% ^^A options that can be passed to sseq
+%
+\define@key{sseq}{grid}{
+ \directlua0{sseqgriddrawer = sseq_grid_#1}
+}
+%
+\define@key{sseq}{entrysize}{
+ \sseq@dimen=#1
+ \directlua0{sseqentrysize=\number\sseq@dimen} % In LUA, it's all in scaled points.
+}
+
+\define@key{sseq}{labels}{
+ \setkeys{sseq}{xlabels=#1,ylabels=#1}
+}
+\define@key{sseq}{xlabels}{
+ \def\temp{#1}
+ \def\temptwo{none}
+ \ifx\temp\temptwo
+ \directlua0{sseqxlabels = sseq_parse_label_list("")}
+ \else
+ \def\temptwo{numbers}
+ \ifx\temp\temptwo
+ \directlua0{sseqxlabels = sseq_parse_label_list("&n")}
+ \else
+ \directlua0{sseqxlabels = sseq_parse_label_list([[#1]])}
+ \fi
+ \fi
+}
+\define@key{sseq}{ylabels}{
+ \def\temp{#1}
+ \def\temptwo{none}
+ \ifx\temp\temptwo
+ \directlua0{sseqylabels = sseq_parse_label_list("")}
+ \else
+ \def\temptwo{numbers}
+ \ifx\temp\temptwo
+ \directlua0{sseqylabels = sseq_parse_label_list("&n")}
+ \else
+ \directlua0{sseqylabels = sseq_parse_label_list([[#1]])}
+ \fi
+ \fi
+}
+
+\define@key{sseq}{xlabelstep}{
+\directlua0{sseqxstep=#1}
+}
+\define@key{sseq}{ylabelstep}{
+\directlua0{sseqystep=#1}
+}
+
+\define@key{sseq}{labelstep}{\setkeys{sseq}{xlabelstep=#1}\setkeys{sseq}{ylabelstep=#1}}
+
+\define@key{sseq}{gapsize}{\setkeys{sseq}{xgapsize=#1}\setkeys{sseq}{ygapsize=#1}}
+\define@key{sseq}{xgapsize}{
+\sseq@dimen=#1
+\directlua0{sseqxgap=\number\sseq@dimen}
+}
+\define@key{sseq}{ygapsize}{
+\sseq@dimen=#1
+\directlua0{sseqygap=\number\sseq@dimen}
+}
+\define@key{sseq}{leak}{\setkeys{sseq}{xleak=#1}\setkeys{sseq}{yleak=#1}}
+\define@key{sseq}{xleak}{
+\sseq@dimen=#1
+\directlua0{sseqxleak=\number\sseq@dimen}
+}
+\define@key{sseq}{yleak}{
+\sseq@dimen=#1
+\directlua0{sseqyleak=\number\sseq@dimen}
+}
+
+\define@key{sseq}{gridstroke}{
+\sseq@dimen=#1
+\directlua0{sseqgridstrokethickness=\number\sseq@dimen}
+}
+
+\define@key{sseq}{arrows}{
+\def\sseq@defaultarrowstyle{#1}
+\directlua0{sseqdefaultarrowstyle="#1"}
+}
+
+\define@key{sseq}{packing}{
+\directlua0{sseqpacking = sseq_pack_#1}
+}
+%
+% ^^A Defaults
+%
+\def\sseq@defaultarrowstyle{to}
+
+\directlua0{sseq_set_defaults()}
+
+\newenvironment{sseq}[3][]{%
+\begingroup
+%
+\directlua0{sseq_init()}
+%
+% ^^A Start the PGF environment
+\begin{pgfpicture}
+%
+% ^^A Initialize some local counters and dimensions
+%
+%
+% ^^A Parse arguments
+%
+%
+\setkeys{sseq}{#1}
+%
+%
+%
+\directlua0{sseq_setup_ranges("#2","#3",\the\sseqxstart,\the\sseqystart)}
+%
+%
+\directlua0{sseq_drawgrid()}
+%
+\directlua0{sseq_drawlabels()}
+%
+% ^^A Move to turtle to (0,0)
+\directlua0{sseqboxno = \the\sseqbox} % ^^A Needed to measure the size of entries
+}
+%
+%
+% ^^A PART FOR \end{sseq}
+%
+{
+\relax
+\ssmoveto 0 0
+\directlua0{sseq_position_objects(); sseq_dump_code()}
+\end{pgfpicture}
+\endgroup
+}
+%
+% \Finale
diff --git a/macros/luatex/latex/luasseq/luasseq.ins b/macros/luatex/latex/luasseq/luasseq.ins
new file mode 100644
index 0000000000..1c1418e1a5
--- /dev/null
+++ b/macros/luatex/latex/luasseq/luasseq.ins
@@ -0,0 +1,4 @@
+\def\batchfile{luasseq.ins}
+\input docstrip.tex
+\generateFile{luasseq.sty}{t}{\from{luasseq.dtx}{package}}
+\endbatchfile
diff --git a/macros/luatex/latex/luasseq/luasseq.lua b/macros/luatex/latex/luasseq/luasseq.lua
new file mode 100644
index 0000000000..79082efce7
--- /dev/null
+++ b/macros/luatex/latex/luasseq/luasseq.lua
@@ -0,0 +1,894 @@
+-- to do: clean up dtx file of remnants of classical sseq code
+-- to do: documentation
+
+function sseq_init()
+-- sseqobject is a 2D-array containing arrays of all the nodes that are dropped at (x,y)
+-- Thus the first object dropped at (2,3) is sseqobject[2][3][1].
+-- The object itself is a dictionary, containing:
+-- name a given name
+-- code the TeX code (in math mode) to typeset
+-- extends if it's an extension, then what it extends
+-- color the color
+-- nodetype the pgf node type (circle etc)
+-- cmd pgf code for drawing it -- with one %s for the color
+-- posx,posy the absolute position on the picture (in sp) -- determined at the end
+ sseqobject = {}
+-- sseqname is a dictionary containing all the given names of dropped objects
+-- Its value is a dictionary, containing
+-- x, y, n the triple index in the sseqobject array
+ sseqname = {}
+-- sseqlabel is an array of labels of sseqobjects
+-- Its values are dictionaries, containing
+-- x,y,n the triple the label belongs to
+-- pos one of L,LU,U,RU,R,RD,D,LD
+-- code the TeX code (in math mode) to typeset
+-- color the color
+ sseqlabel = {}
+-- sseqconnection is an array containing all connection (lines)
+-- the connection itself is a dictionary, containing:
+-- from an array (x,y,n) if n=0 then source void
+-- to an array (x,y,n) if n=0 then target void
+-- color the color
+-- curving a number denoting the curving factor
+-- dashing the pgf dashing code
+-- arrowfrom the arrowstyle at from (or nil)
+-- arrowto the arrowstyle at to (or nil)
+ sseqconnection = {}
+--
+-- Initialize some global variables
+--
+ sseqcurrentindex = nil
+ sseqpreviousindex = nil
+ sseqopenconnection = nil
+
+ sseqxstart = 0 -- minimal x
+ sseqystart = 0 -- minimal y
+
+ sseqgriddrawer = sseq_grid_crossword
+ sseqgridstrokethickness = 6554 -- 0.1pt in sp
+
+ sseqxlabels = sseq_parse_label_list("&n")
+ sseqylabels = sseq_parse_label_list("&n")
+
+ sseqprefix = {}
+ sseqposx, sseqposy = 0,0
+ sseqcurrabsx, sseqcurrabsy = 0,0
+ sseq_set_defaults()
+end
+
+function sseq_set_defaults()
+ sseqentrysize = 745860 -- 0.4 cm in scaled points
+ sseqxgap = 559409 -- 0.3 cm in scaled points
+ sseqygap = 559409
+ sseqxstep = 2 -- every other label is drawn
+ sseqystep = 2
+ sseqdefaultarrowstyle = "to"
+ ssequsescolor = true
+ sseqpacking = sseq_pack_auto
+end
+
+
+-- parselabelrange: input = a string of the form a...b,c...d, etc.
+-- defstart: the default start index
+-- return = an array of dictionaries
+-- min a number
+-- max a number
+function parselabelrange(s,defstart)
+ local res = {}
+ local mini,maxi,found
+ for rng in string.gmatch(s.."," , "([^,]+)") do
+ found,_,mini,maxi = string.find(rng,"([+-]?[0-9]+)%.%.%.([+-]?[0-9]+)")
+ if not found then
+ mini = defstart
+ maxi = tonumber(rng)
+ if not maxi then error("invalid range : "..rng) end
+ maxi = maxi+defstart-1
+ else
+ mini = tonumber(mini)
+ maxi = tonumber(maxi)
+ end
+ table.insert(res,{min = mini, max = maxi})
+ end
+ return res
+end
+
+function sseq_setup_ranges(xr,yr,defxstart,defystart)
+ sseqxrange = parselabelrange(xr,defxstart)
+ sseqyrange = parselabelrange(yr,defystart)
+end
+
+function sseq_get_rangepart(range,x,gap)
+ local pos = 0
+ for j,rng in ipairs(range) do
+ if x >= rng.min and x <= rng.max then
+ return pos,pos+(rng.max+1-rng.min)*sseqentrysize
+ else
+ pos = pos+(rng.max+1-rng.min)*sseqentrysize+gap
+ end
+ end
+end
+
+function sseq_getabsoluteposition(range,x,gap)
+ local pos = 0
+ for j,rng in ipairs(range) do
+ if x >= rng.min and x <= rng.max then
+ return pos+sseqentrysize*(x-rng.min),false
+ end
+ pos = pos+(rng.max+1-rng.min)*sseqentrysize+gap
+ end
+end
+
+function sseq_getcoords(x,y)
+ local xpos,xout,ypos,yout
+ xpos = sseq_getabsoluteposition(sseqxrange,x,sseqxgap)
+ ypos = sseq_getabsoluteposition(sseqyrange,y,sseqygap)
+ return xpos, ypos
+end
+
+function sseq_grid_none()
+end
+
+function sseq_grid_crossword(x,y,width,height)
+ tex.print("\\pgfsetlinewidth{"..sseqgridstrokethickness.."sp}")
+ tex.print("\\pgfpathgrid[stepx="..sseqentrysize.."sp,stepy="..sseqentrysize.."sp]{\\pgfpointorigin}{\\pgfpoint{"..width*sseqentrysize.."sp}{"..height*sseqentrysize.."sp}}")
+ tex.print("\\pgfusepath{stroke}")
+end
+
+function sseq_grid_go(x,y,width,heigh)
+ tex.print("\\pgfsetlinewidth{"..sseqgridstrokethickness.."sp}")
+ tex.print("\\pgftransformxshift{"..(sseqentrysize/2).."sp}")
+ tex.print("\\pgftransformyshift{"..(sseqentrysize/2).."sp}")
+ tex.print("\\pgfpathgrid[stepx="..sseqentrysize.."sp,stepy="..sseqentrysize.."sp]{\\pgfpoint{"..(-sseqentrysize/2).."sp}{"..(-sseqentrysize/2).."sp}}{\\pgfpoint{"..(width*sseqentrysize-sseqentrysize/2).."sp}{"..(height*sseqentrysize-sseqentrysize/2).."sp}}")
+ tex.print("\\pgfusepath{stroke}")
+end
+
+function sseq_grid_dots(x,y,width,height)
+ tex.print("\\pgfsetlinewidth{1pt}")
+ tex.print("\\pgfsetdash{{1pt}{"..(sseqentrysize-65536).."sp}}{"..(sseqentrysize/2+32768).."sp}")
+ tex.print("\\pgftransformxshift{"..(sseqentrysize/2).."sp}")
+ tex.print("\\pgftransformyshift{"..(sseqentrysize/2).."sp}")
+ tex.print("\\pgfpathgrid[stepx="..sseqentrysize.."sp,stepy="..sseqentrysize.."sp]{\\pgfpoint{"..(-sseqentrysize/2).."sp}{"..(-sseqentrysize/2).."sp}}{\\pgfpoint{"..(width*sseqentrysize-sseqentrysize/2).."sp}{"..(height*sseqentrysize-sseqentrysize/2).."sp}}")
+ tex.print("\\pgfusepath{stroke}")
+end
+
+function sseq_grid_chess(x,y,width,height)
+ tex.print("\\pgfsetcolor{sslightgr}")
+ if ((x+y) % 2) == 1 then -- invert everything by first drawing a solid gray rectangle
+ -- and then draw the grid in white. This way, even bidegree
+ -- is always white.
+ tex.print("\\pgfpathrectangle{\\pgfpoint{0sp}{0sp}}{\\pgfpoint{"
+ ..(width*sseqentrysize).."sp}{"..(height*sseqentrysize).."sp}}")
+ tex.print("\\pgfusepath{fill}")
+ tex.print("\\pgfsetcolor{white}")
+ end
+
+ tex.print("\\pgfsetlinewidth{"..sseqentrysize.."sp}")
+
+ tex.print("\\pgfsetdash{{"..sseqentrysize.."sp}{"..sseqentrysize.."sp}}{"..sseqentrysize.."sp}")
+
+
+ tex.print("\\pgftransformxshift{"..(sseqentrysize/2).."sp}")
+ tex.print("\\pgftransformyshift{"..(sseqentrysize/2).."sp}")
+ tex.print("\\pgfpathgrid[stepx="..(sseqentrysize*2).."sp,stepy="..(sseqentrysize*2).."sp]{\\pgfpoint{"..(-sseqentrysize/2).."sp}{"..(-sseqentrysize/2).."sp}}{\\pgfpoint{"..(width*sseqentrysize-sseqentrysize/2).."sp}{"..(height*sseqentrysize-sseqentrysize/2).."sp}}")
+ tex.print("\\pgfusepath{stroke}")
+end
+
+function sseq_drawgrid() -- draws the background grid. This seems like pgf patterns are made
+ -- for this purpose, but you can't specify a phase for patterns,
+ -- so they are useless except for ornamental purposes.
+ local xmin,ymin,width,heigt
+ for x,xrng in ipairs(sseqxrange) do
+ for y,yrng in ipairs(sseqyrange) do
+ xmin, ymin = sseq_getcoords(xrng.min,yrng.min)
+ tex.print("\\begin{pgfscope}")
+ tex.print("\\pgftransformshift{\\pgfpoint{"..xmin.."sp}{"..ymin.."sp}}")
+ width = (xrng.max+1)-xrng.min
+ height = (yrng.max+1)-yrng.min
+ sseqgriddrawer(xrng.min,yrng.min,width,height)
+ tex.print("\\end{pgfscope}")
+ end
+ end
+end
+
+-- A label list has the form x1;x2;...;xn,y1;y2;...;yn,... where x1 etc are labels
+-- A label may contain the placeholders:
+-- &n = actual coordinate
+-- &c = number of chunk (begin with 0)
+-- &i = index within the chunk (begin with 0)
+
+function sseq_parse_label_list(s)
+ local res = {}
+ local chunk = 0
+ local index
+
+ for rng in string.gmatch(s.."," , "([^,]*),") do
+ res[chunk] = {}
+ index = 0
+ for label in string.gmatch(rng..";", "([^;]*);") do
+ res[chunk][index] = label
+ index = index+1
+ end
+ chunk = chunk+1
+ end
+ return res
+end
+
+function sseq_format_label(label, n,c,i)
+ local res
+ res = string.gsub(label,"&n",n)
+ res = string.gsub(res,"&c",c)
+ res = string.gsub(res,"&i",i)
+ res = string.gsub(res,"&&","&")
+ return res
+end
+
+function sseq_label_fromlist(n,c,i,list)
+-- will return list[c][i] unless this is out of range, then take the last one given
+ local chunks = #list
+ local chunklen
+ local chunk
+ if list[0] then chunks = chunks+1 else return "" end
+ chunk = list[math.min(chunks-1,c)]
+ chunklen = #chunk
+ if chunk[0] then chunklen = chunklen+1 end
+ return sseq_format_label(chunk[math.min(chunklen-1,i)],n,c,i)
+end
+
+
+function sseq_draw_horizontal_labels(range)
+ local k
+
+ if sseqxstep == 0 then return end -- old-fashioned way of disabling labels
+ for c,rng in ipairs(range) do
+ k=0
+ for i=rng.min,rng.max,sseqxstep do
+ x = sseq_getcoords(i,0)
+ label = sseq_label_fromlist(i,c-1,k,sseqxlabels)
+ -- bug fix with bounding box sizes in pgf
+-- tex.print("\\sbox\\sseq@labelbox{\\strut\\ensuremath{"..label.."}}")
+-- tex.print("\\dimen0=\\ht\\sseq@labelbox")
+-- tex.print("\\advance\\dimen0 by \\dp\\sseq@labelbox")
+-- tex.print("\\pgf@protocolsizes{0pt}{-\\dimen0}")
+ tex.print("\\pgftext[top,at=\\pgfpoint{"..(x+sseqentrysize/2).."sp}{0sp}]{\\ensuremath{\\strut "..label.."}}")
+ k=k+sseqxstep
+ end
+ end
+end
+
+function sseq_draw_vertical_labels(range)
+ local k
+
+ if sseqystep == 0 then return end -- old-fashioned way of disabling labels
+ for c,rng in ipairs(range) do
+ k=0
+ for i=rng.min,rng.max,sseqystep do
+ _,y = sseq_getcoords(0,i)
+ label = sseq_label_fromlist(i,c,k,sseqylabels)
+ tex.print("\\pgftext[right,at=\\pgfpoint{-2pt}{"..(y+sseqentrysize/2).."sp}]{\\ensuremath{"..label.."}}")
+ k=k+sseqystep
+ end
+ end
+end
+
+
+function sseq_drawlabels()
+ sseq_draw_horizontal_labels(sseqxrange)
+ sseq_draw_vertical_labels(sseqyrange)
+end
+
+function sseq_getdroplist(x,y)
+ return sseqobject[x] and sseqobject[x][y]
+end
+
+function sseq_openposition()
+ local l = sseq_getdroplist(sseqposx,sseqposy)
+ if not l then
+ error(string.format("sseq: cannot open position (%d,%d): nothing dropped yet",sseqposx,sseqposy))
+ elseif #l ~= 1 then
+ error(string.format("sseq: cannot open position (%d,%d): multiple drops",sseqposx,sseqposy))
+ else
+ sseqcurrentindex = {sseqposx,sseqposy,1}
+ end
+end
+
+function sseq_assert_source()
+ sseq_flush_connection()
+ if not sseqcurrentindex then
+ sseq_openposition()
+ end
+end
+
+function sseq_finish_pos()
+ sseq_flush_connection()
+ if sseqcurrentindex then
+ sseq_conclude_connection()
+ sseqpreviousindex, sseqcurrentindex = sseqcurrentindex,nil
+ end
+end
+
+function sseq_drop_and_open(x,y)
+ if not sseqobject[x] then sseqobject[x] = {} end
+ if not sseqobject[x][y] then sseqobject[x][y] = {} end
+ table.insert(sseqobject[x][y],{})
+ sseqcurrentindex = {x,y,#sseqobject[x][y]}
+end
+
+function sseq_drop_object(x,y,shape,pathusage,content,col)
+ local obj
+ sseq_finish_pos()
+ sseq_drop_and_open(x,y)
+ obj = sseqobject[x][y][sseqcurrentindex[3]]
+ obj.code = content
+ obj.color = col
+ obj.nodetype = shape
+
+
+ sseq_conclude_connection()
+ -- now do the optimizations such as \bullet -> drawn black circle etc
+ if (pathusage == "fill") or (obj.code == "\\bullet ") then
+ if (obj.nodetype == "circle") or (obj.code == "\\bullet ") then
+ obj.nodetype = "circle"
+ obj.cmd = "\\pgfsetfillcolor{%s}\\pgfpathqcircle{2pt}\\pgfusepathqfill"
+ obj.radius = 131072 -- 2pt in sp
+ obj.wd,obj.ht = 262144,262144 -- 4pt in sp
+ else
+ obj.cmd = "\\pgfsetfillcolor{%s}\\pgfsys@rect{-2pt}{-2pt}{4pt}{4pt}\\pgfsys@fill"
+ obj.wd,obj.ht = 262144,262144
+ obj.radius = 370727
+ end
+ elseif (obj.code == "\\circ ") then
+ obj.nodetype = "circle"
+ obj.cmd = "\\pgfsetstrokecolor{%s}\\pgfsetfillcolor{white}\\pgfsetdash{}{0pt}\\pgfpathqcircle{2pt}\\pgfusepathqfillstroke"
+ obj.radius = 131072 -- 2pt in sp
+ obj.wd,obj.ht = 262144,262144 -- 4pt in sp
+ elseif(obj.code == "\\square ") then
+ obj.nodetype="rectangle"
+ obj.cmd = "\\pgfsetstrokecolor{%s}\\pgfsetdash{}{0pt}\\pgfsys@rect{-2pt}{-2pt}{4pt}{4pt}\\pgfsys@stroke"
+ obj.wd,obj.ht = 262144,262144
+ obj.radius = 370727
+ else
+ obj.cmd = "\\pgfsetstrokecolor{%s}\\pgfsetfillcolor{white}"..string.format("\\pgfnode{%s}{center}{\\color{%%s}\\ensuremath{%s}}{}{\\pgfusepath{fill,%s}}",obj.nodetype,obj.code,pathusage)
+ tex.print (string.format("\\setbox%d=\\hbox{\\pgfinterruptpicture\\ensuremath{%s}\\endpgfinterruptpicture}",sseqboxno,obj.code))
+ tex.print("\\directlua0{sseq_register_size()}")
+ end
+end
+
+function sseq_drop_extension(shape,pathusage,col)
+ local obj,refobj
+ sseq_assert_source()
+ table.insert(sseqobject[sseqposx][sseqposy],{})
+ obj = sseqobject[sseqposx][sseqposy][#sseqobject[sseqposx][sseqposy]]
+ obj.code = "" -- no TeX code to typeset
+ obj.extends = sseqcurrentindex[3]
+ obj.color = col
+ obj.nodetype = shape
+ refobj = sseqobject[sseqcurrentindex[1]][sseqcurrentindex[2]][sseqcurrentindex[3]]
+
+ sseqcurrentindex[3] = #sseqobject[sseqposx][sseqposy]
+ sseq_conclude_connection()
+
+ if (shape=="circle") then
+ obj.radius = refobj.radius+65536 -- add 1pt in sp
+ obj.wd,obj.ht = 2*obj.radius,2*obj.radius
+ obj.cmd = "\\pgfsetstrokecolor{%s}\\pgfpathqcircle{"..obj.radius.."sp}\\pgfusepath{stroke,"..pathusage.."}"
+ else
+ obj.wd,obj.ht = refobj.wd+131072,refobj.ht +131072 -- add 1 pt at each side
+ obj.radius = 0.5*math.sqrt(obj.wd*obj.wd+obj.ht*obj.ht)
+ obj.cmd = "\\pgfsetstrokecolor{%s}\\pgfsetshapeinnerxsep{"..(.5*obj.wd).."sp}\\pgfsetshapeinnerysep{"..(.5*obj.ht).."sp}\\pgfnode{rectangle}{center}{}{}{\\pgfusepath{stroke,"..pathusage.."}}"
+ end
+end
+
+function sseq_register_size()
+ local currobj = sseqobject[sseqcurrentindex[1]][sseqcurrentindex[2]][sseqcurrentindex[3]]
+ currobj.wd = tex.box[sseqboxno].width
+ currobj.ht = (tex.box[sseqboxno].height+tex.box[sseqboxno].depth)
+ currobj.dp = tex.box[sseqboxno].depth
+ currobj.radius = 0.5*math.sqrt(currobj.wd*currobj.wd+currobj.ht*currobj.ht)
+end
+
+function sseq_drop_object_here(shape,pathusage,content,col)
+ sseq_drop_object(sseqposx,sseqposy,shape,pathusage,content,col)
+end
+
+function sseq_bullstring(x,y,n,col)
+ if n==0 then return end
+ sseq_drop_object_here("circle","fill","\\bullet",col)
+ for i=2,n do
+ sseq_move(x,y)
+ sseq_drop_object_here("circle","fill","\\bullet",col)
+ sseq_late_connection("","",col,false,false)
+ end
+end
+
+function sseq_moveto(x,y)
+ sseq_conclude_connection()
+ sseq_finish_pos()
+ sseqposx = x
+ sseqposy = y
+end
+
+function sseq_grayout(col)
+ sseq_assert_source()
+ sseqobject[sseqcurrentindex[1]][sseqcurrentindex[2]][sseqcurrentindex[3]].color = col
+ for _,conn in pairs(sseqconnection) do
+ if (conn.from[1] == sseqcurrentindex[1] and conn.from[2] == sseqcurrentindex[2] and conn.from[3] == sseqcurrentindex[3]) or (conn.to[1] == sseqcurrentindex[1] and conn.to[2] == sseqcurrentindex[2] and conn.to[3] == sseqcurrentindex[3]) then
+ conn.color = col
+ end
+ end
+end
+
+function sseq_move(x,y)
+ sseq_moveto(sseqposx+x,sseqposy+y)
+end
+
+-- New prefix model: sseqprefix is a stack of lists
+-- sseq_begin_prefix_context pushes an empty prefix onto that stack
+-- sseq_end_prefix_context pops the last prefix off the stack
+-- A prefix is an (unordered) list of names
+
+function uses_old_prefix_model(pref)
+ if #pref == 0 then return true end
+ if type(pref[1]) == "table" then return false end
+ return true
+end
+
+function stringtolist(pref,name)
+ local res = {}
+ -- check if we're using the old or the new model
+ if uses_old_prefix_model(pref) then
+ for i,p in ipairs(pref) do table.insert(res,p) end
+ else -- new prefix model
+ for i,l in ipairs(pref) do
+ for j,p in ipairs(l) do table.insert(res,p) end
+ end
+ end
+ for w in string.gmatch(name,"%w+") do
+ table.insert(res,w)
+ end
+ table.sort(res)
+ return res
+end
+
+function sseq_set_global_name(name)
+ local namestring = table.concat(name)
+ sseq_assert_source()
+ if sseqname[namestring] then
+ error("sseq: duplicate name "..namestring)
+ else
+ sseqname[namestring] = {sseqcurrentindex[1],sseqcurrentindex[2],sseqcurrentindex[3]}
+ end
+end
+
+function sseq_global_name(name)
+ return stringtolist(sseqprefix,name)
+end
+
+function sseq_name(name)
+ sseq_set_global_name(sseq_global_name(name))
+end
+
+function sseq_global_goto(name)
+ local namestring = table.concat(name)
+ sseq_conclude_connection()
+ sseq_finish_pos()
+ if not sseqname[namestring] then
+ error("sseq: goto name does not exist: '"..namestring.."'")
+ else
+ sseqcurrentindex = { sseqname[namestring][1],sseqname[namestring][2],sseqname[namestring][3] }
+ sseqposx,sseqposy = sseqname[namestring][1], sseqname[namestring][2]
+ end
+end
+
+function sseq_abs_goto(name)
+ sseq_global_goto(stringtolist({},name))
+end
+
+function sseq_prefix(pref)
+ if uses_old_prefix_model(sseqprefix) then
+ sseqprefix = stringtolist(sseqprefix,pref)
+ else
+ sseqprefix[#sseqprefix] = stringtolist(sseqprefix[#sseqprefix],pref)
+ end
+end
+
+function sseq_begin_prefix_context()
+ if (#sseqprefix > 0) and uses_old_prefix_model(sseqprefix) then
+ error("sseq: you cannot use stacked prefixes when there's a non-stacked active prefix")
+ else
+ table.insert(sseqprefix,{})
+ end
+end
+
+
+function sseq_end_prefix_context()
+ if uses_old_prefix_model(sseqprefix) then
+ error("sseq: no prefix context to end")
+ else
+ table.remove(sseqprefix,#sseqprefix)
+ end
+end
+
+-- sseq_reset_prefix is deprecated. It'll throw an error if we ever used
+-- sseq_begin_prefix_context (because that would be incompatible)
+function sseq_reset_prefix()
+ if uses_old_prefix_model(sseqprefix) then
+ sseqprefix = {}
+ else
+ error("sseq: you cannot use \\ssresetprefix if you use stacked prefixes.")
+ end
+end
+
+function sseq_goto(name)
+ sseq_global_goto(sseq_global_name(name))
+end
+
+function sseq_flush_connection()
+end
+
+function sseq_conclude_connection()
+ if not sseqopenconnection then return end
+ if not sseqcurrentindex then
+ sseq_openposition()
+ end
+ sseqopenconnection.to = { sseqcurrentindex[1],sseqcurrentindex[2],sseqcurrentindex[3] }
+ table.insert(sseqconnection,sseqopenconnection)
+ sseqopenconnection = nil
+end
+
+-- immediately register a connection between sseqpreviousindex and sseqcurrentindex
+function sseq_late_connection(dash,bending,col,sourcevoid,targetvoid)
+ local newconn
+
+ sseq_flush_connection() -- finish previous connection
+ if not targetvoid then sseq_assert_source() end
+ if (not sseqpreviousindex) or (not targetvoid and (sseqpreviousindex[3] == 0)) then
+ error("sseq: connection without well-defined source")
+ end
+ newconn = { color = col, dashing = dash }
+ if(bending ~= "") then newconn.curving = bending end
+ if sourcevoid then
+ newconn.from = {sseqpreviousindex[1],sseqpreviousindex[2],0}
+ else
+ newconn.from = {sseqpreviousindex[1],sseqpreviousindex[2],sseqpreviousindex[3]}
+ end
+ if targetvoid then
+ newconn.to = {sseqposx,sseqposy,0}
+ else
+ newconn.to = {sseqcurrentindex[1],sseqcurrentindex[2],sseqcurrentindex[3]}
+ end
+ table.insert(sseqconnection,newconn)
+end
+
+function sseq_void_line(dash,bending,col,x,y)
+ local newconn = { color = col, dashing = dash }
+
+ if(bending ~= "") then newconn.curving = bending end
+ sseq_assert_source();
+ newconn.from = {sseqcurrentindex[1],sseqcurrentindex[2],sseqcurrentindex[3]}
+ newconn.to = {sseqcurrentindex[1]+x,sseqcurrentindex[2]+y,0}
+ table.insert(sseqconnection,newconn)
+end
+
+function sseq_open_connection(dash,bending,col,x,y)
+ local newconn = { color = col, dashing = dash }
+
+ if(bending ~= "") then newconn.curving = bending end
+ sseq_assert_source(); sseq_finish_pos()
+ newconn.from = {sseqpreviousindex[1],sseqpreviousindex[2],sseqpreviousindex[3]}
+ sseqopenconnection = newconn
+end
+
+function sseq_add_arrow(fromto,type)
+ if sseqopenconnection then
+ sseqopenconnection[fromto] = type
+ else
+ sseqconnection[#sseqconnection][fromto] = type
+ end
+end
+
+function sseq_drop_label(p,col,label)
+ sseq_assert_source()
+ table.insert(sseqlabel,{x = sseqcurrentindex[1], y = sseqcurrentindex[2], n = sseqcurrentindex[3],
+ color = col, code = label, pos = p})
+end
+
+
+function sseq_pack_diagonal(i,n)
+ return sseqentrysize/2+sseqentrysize*(n-i)/4,
+ sseqentrysize/2-sseqentrysize*(n-i)/4
+end
+
+function sseq_pack_horizontal(i,n)
+ return sseqentrysize/2+sseqentrysize*(i-1)/4,
+ sseqentrysize/2
+end
+function sseq_pack_vertical(i,n)
+ return sseqentrysize/2,
+ sseqentrysize/2-sseqentrysize*(n-i)/4
+end
+
+sseqautopackdata = { {{.5,.5}}, -- one
+ {{.25,.75},{.75,.25}}, -- two
+ {{.167,.833},{.5,.5},{.833,.167}}, -- three
+ {{.167,.75},{.389,.25},{.611,.75},{.833,.25}} -- four
+ }
+
+function sseq_pack_auto(i,n) -- return offset of the ith square out of n.
+ if n > 4 then return sseq_pack_diagonal(i,n) end
+ dat = sseqautopackdata[n][i]
+ return dat[1]*sseqentrysize,dat[2]*sseqentrysize
+
+end
+
+function sseq_position_object_list(x,y,list)
+ local absx,absy = sseq_getcoords(x,y) -- the lower left corner of the square
+ local numobj = #list
+ local j = 1
+
+ if not absx or not absy then return end -- outside clipping area -- don't draw.
+
+ for i,obj in ipairs(list) do
+ if (obj.extends) then numobj = numobj-1 end
+ end
+
+ for i,obj in ipairs(list) do
+ if (obj.extends) then
+ obj.posx,obj.posy = list[obj.extends].posx,list[obj.extends].posy
+ else
+ obj.posx, obj.posy = sseqpacking(j,numobj)
+ obj.posx = obj.posx + absx
+ obj.posy = obj.posy + absy
+ j = j+1
+ end
+ if not obj.color then obj.color = "black" end
+ end
+end
+
+function sseq_position_objects()
+ for x,ylist in pairs(sseqobject) do
+ for y,olist in pairs(ylist) do
+ sseq_position_object_list(x,y,olist)
+ end
+ end
+end
+
+function sseq_dump_translation(x,y)
+ tex.print("\\pgfsys@transformshift{"..(x-sseqcurrabsx).."sp}{"..(y-sseqcurrabsy).."sp}")
+ sseqcurrabsx,sseqcurrabsy = x,y
+end
+
+function sseq_dump_object(x,y,obj)
+ if not obj.posx or not obj.posy then return end
+ sseq_dump_translation(obj.posx,obj.posy)
+ tex.print(string.format(obj.cmd,obj.color,obj.color))
+end
+
+--
+-- return the intersection of the vector to (fromx, fromy) with the boundary of obj
+--
+function sseq_correct_line_end(obj,fromx,fromy,curving)
+ local distsq,posx,posy,deltax,deltay,dirx,diry
+ posx,posy = obj.posx,obj.posy
+
+ dirx = posx-fromx
+ diry = posy-fromy
+
+ if(curving) then
+ dirx,diry = dirx/2-curving*diry,diry/2+curving*dirx
+ end
+
+ if obj.nodetype == "circle" then
+ dist = math.sqrt(dirx*dirx + diry*diry)
+ return posx - obj.radius*(dirx)/dist,
+ posy - obj.radius*(diry)/dist
+ else
+ deltax = dirx/diry*obj.ht/2 -- no problem with infinity in LUA
+ deltay = diry/dirx*obj.wd/2
+ if(math.abs(deltax) <= obj.wd/2) then -- boundary point on one of the horizontal lines
+ if fromy > posy then
+ return posx + deltax, posy + obj.ht/2
+ else
+ return posx - deltax, posy - obj.ht/2
+ end
+ else
+ if fromx > posx then
+ return posx + obj.wd/2, posy + deltay
+ else
+ return posx - obj.wd/2, posy - deltay
+ end
+ end
+ end
+end
+
+--
+-- return the intersection of the vector from (x,y) to the clipped object at
+-- coordinate (clipi,clipj) with the boundary of the grid segment containing (i,j)
+--
+function sseq_fix_clipped_connection(clipi,clipj,i,j,x,y)
+ local minx,maxx,miny,maxy,deltax,deltay
+
+ minx,maxx = sseq_get_rangepart(sseqxrange,i,sseqxgap)
+ miny,maxy = sseq_get_rangepart(sseqyrange,j,sseqygap)
+ minx = minx - sseqxleak
+ maxx = maxx + sseqxleak
+ miny = miny - sseqyleak
+ maxy = maxy + sseqyleak
+
+ deltax = 1e10*(clipi-i)
+ deltay = 1e10*(clipj-j)
+ -- compute intersection of the vector (deltax, deltay) based at (x,y)
+ -- with the rectangle minx,miny,maxx,maxy
+ if(x+deltax > maxx) then deltax,deltay = maxx-x, deltay * (maxx-x)/deltax end
+ if(x+deltax < minx) then deltax,deltay = minx-x, deltay*(minx-x)/deltax end
+ if(y+deltay > maxy) then deltax,deltay = deltax * (maxy-y)/deltay, maxy-y end
+ if(y+deltay < miny) then deltax,deltay = deltax * (miny-y)/deltay, miny-y end
+ return x+deltax,y+deltay
+end
+
+function sseq_dump_connection(conn)
+ local fromx, fromy, tox, toy, ctrlx,ctrly, helpx, helpy
+ local fromobj,toobj
+
+ -- possibilities: * from, to not in the displayed range
+ -- * from[3] = 0 or to[3] = 0 (void line)
+ -- or any combination.
+
+ -- if source or target is clipped, will make it a void line
+ --
+
+ -- TODO: locations outside displayed range are incorrect
+
+ fromx,fromy = sseq_getcoords(conn.from[1],conn.from[2])
+ tox,toy = sseq_getcoords(conn.to[1],conn.to[2])
+
+ if ((not fromx) or (not fromy) or (conn.from[3] == 0))
+ and ((not tox) or (not toy) or (conn.to[3] == 0)) then
+ -- forget it -- source and target are clipped and/or void
+ return
+ end
+
+ if ((not fromx) or (not fromy)) then -- source clipped: we can be sure the target is regular
+ toobj = sseqobject[conn.to[1]][conn.to[2]][conn.to[3]]
+ tox,toy = toobj.posx, toobj.posy
+ fromx,fromy = sseq_fix_clipped_connection(conn.from[1],conn.from[2],conn.to[1],conn.to[2],tox,toy)
+ elseif ((not tox) or (not toy)) then -- target clipped: we can be sure the source is regular
+ fromobj = sseqobject[conn.from[1]][conn.from[2]][conn.from[3]]
+ fromx,fromy = fromobj.posx, fromobj.posy
+ tox,toy = sseq_fix_clipped_connection(conn.to[1],conn.to[2],conn.from[1],conn.from[2],fromx,fromy)
+ elseif conn.from[3] == 0 then -- source not clipped but void
+ toobj = sseqobject[conn.to[1]][conn.to[2]][conn.to[3]]
+ fromx = fromx+(toobj.posx-tox)
+ fromy = fromy+(toobj.posy-toy)
+ tox,toy = toobj.posx, toobj.posy
+ elseif conn.to[3] == 0 then -- target not clipped but void
+ fromobj = sseqobject[conn.from[1]][conn.from[2]][conn.from[3]]
+ tox = tox+(fromobj.posx-fromx)
+ toy = toy+(fromobj.posy-fromy)
+ fromx,fromy = fromobj.posx, fromobj.posy
+ else -- both source and target regular
+ fromobj = sseqobject[conn.from[1]][conn.from[2]][conn.from[3]]
+ fromx,fromy = fromobj.posx, fromobj.posy
+ toobj = sseqobject[conn.to[1]][conn.to[2]][conn.to[3]]
+ tox,toy = toobj.posx, toobj.posy
+ end
+
+
+ if fromobj then -- we have to be more careful where it ends
+ fromx,fromy = sseq_correct_line_end(fromobj,tox,toy,conn.curving)
+ end
+ if toobj then
+ tox,toy = sseq_correct_line_end(toobj,fromx,fromy,conn.curving and -conn.curving)
+ end
+
+ if conn.curving then
+ ctrlx = tox/2+fromx/2-conn.curving*(toy-fromy)
+ ctrly = toy/2+fromy/2+conn.curving*(tox-fromx)
+ end
+
+ if conn.arrowfrom or conn.arrowto or conn.curving then -- got to use slow code
+ tex.print("\\pgfsetdash{"..conn.dashing.."}{0pt}")
+ tex.print("\\pgfsetstrokecolor{"..conn.color.."}")
+ if (fromx and fromy) then
+ if (tox and toy) then
+ tex.print(string.format("\\pgfpathmoveto{\\pgfpoint{%dsp}{%dsp}}",fromx,fromy))
+ if conn.curving then
+ tex.print(string.format("\\pgfpathquadraticcurveto{\\pgfpoint{%dsp}{%dsp}}{\\pgfpoint{%dsp}{%dsp}}",ctrlx,ctrly,tox,toy))
+ else
+ tex.print(string.format("\\pgfpathlineto{\\pgfpoint{%dsp}{%dsp}}",tox,toy))
+ end
+ if conn.arrowfrom then
+ tex.print("\\pgfsetarrowsstart{"..conn.arrowfrom.."}")
+ else
+ tex.print("\\pgfsetarrowsstart{}")
+ end
+ if conn.arrowto then
+ tex.print("\\pgfsetarrowsend{"..conn.arrowto.."}")
+ else
+ tex.print("\\pgfsetarrowsend{}")
+ end
+ tex.print("\\pgfusepath{stroke}")
+ end
+ end
+ else
+ tex.print("\\pgfsetdash{"..conn.dashing.."}{0pt}")
+ tex.print("\\pgfsetstrokecolor{"..conn.color.."}")
+ if (fromx and fromy) then
+ if (tox and toy) then
+ tex.print(string.format("\\pgfsys@moveto{%dsp}{%dsp}",fromx,fromy))
+ tex.print(string.format("\\pgfsys@lineto{%dsp}{%dsp}",tox,toy))
+ tex.print("\\pgfsys@stroke")
+ end
+ end
+ end
+end
+
+local labelpositioninrect = { U = {0,.5,"bottom"},
+ UL = {-.5,.5,"bottom,right"},
+ LU = {-.5,.5,"bottom,right"},
+ L = {-.5,0,"right"},
+ DL = {-.5,-.5,"top,right"},
+ LD = {-.5,-.5,"top,right"},
+ D = {0,-.5,"top"},
+ DR = {.5,-.5,"top,left"},
+ RD = {.5,-.5,"top,left"},
+ R = {.5,0,"left"},
+ UR = {.5,.5,"bottom,left"},
+ RU = {.5,.5,"bottom,left"} }
+local labelpositionincirc = { U = {0,1,"bottom"},
+ UL = {-.71,.71,"bottom,right"},
+ LU = {-.71,.71,"bottom,right"},
+ L = {-1,0,"right"},
+ DL = {-.71,-.71,"top,right"},
+ LD = {-.71,-.71,"top,right"},
+ D = {0,-1,"top"},
+ DR = {.71,-.71,"top,left"},
+ RD = {.71,-.71,"top,left"},
+ R = {1,0,"left"},
+ UR = {.71,.71,"bottom,left"},
+ RU = {.71,.71,"bottom,left"} }
+
+function sseq_dump_label(label)
+ local labelledobj = sseqobject[label.x][label.y][label.n]
+ local posx,posy
+
+ if (not labelledobj.posx) or (not labelledobj.posy) then return end
+ tex.print("\\color{"..label.color.."}")
+ if labelledobj.nodetype == "circle" then
+ posx = labelledobj.posx+labelpositionincirc[label.pos][1]*labelledobj.radius
+ posy = labelledobj.posy+labelpositionincirc[label.pos][2]*labelledobj.radius
+ sseq_dump_translation(posx,posy)
+ tex.print(string.format("\\pgftext[%s]{\\ensuremath{%s}}",labelpositioninrect[label.pos][3],label.code))
+ else
+ posx = labelledobj.posx+labelpositioninrect[label.pos][1]*labelledobj.wd
+ posy = labelledobj.posy+labelpositioninrect[label.pos][2]*labelledobj.ht
+ sseq_dump_translation(posx,posy)
+ tex.print(string.format("\\pgftext[%s]{\\ensuremath{%s}}",labelpositioninrect[label.pos][3],label.code))
+ end
+end
+
+-- Write out all the pgf code to produce the chart
+function sseq_dump_code()
+ if (not sseqxleak) then sseqxleak = 0.3*sseqxgap end
+ if (not sseqyleak) then sseqyleak = 0.3*sseqygap end
+ tex.print("\\makeatletter")
+ tex.print("\\pgfset{inner sep=0pt}")
+ -- connections
+ for _,conn in pairs(sseqconnection) do
+ sseq_dump_connection(conn)
+ end
+ -- objects
+ tex.print("\\color{white}") -- background fill color
+ for x,ylist in pairs(sseqobject) do
+ for y,olist in pairs(ylist) do
+ for z,obj in ipairs(olist) do
+ sseq_dump_object(x,y,obj)
+ end
+ end
+ end
+ -- labels
+ for _,label in pairs(sseqlabel) do
+ sseq_dump_label(label)
+ end
+ tex.print("\\makeatother")
+end
+
diff --git a/macros/luatex/latex/luasseq/luasseq.pdf b/macros/luatex/latex/luasseq/luasseq.pdf
new file mode 100644
index 0000000000..6e487fcde6
--- /dev/null
+++ b/macros/luatex/latex/luasseq/luasseq.pdf
Binary files differ