diff options
-rw-r--r-- | Master/texmf-dist/doc/latex/timechart/README.md | 16 | ||||
-rw-r--r-- | Master/texmf-dist/doc/latex/timechart/timechart-doc.pdf | bin | 0 -> 797483 bytes | |||
-rw-r--r-- | Master/texmf-dist/source/latex/timechart/timechart.dtx | 3458 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/timechart/timechart.ins | 38 | ||||
-rw-r--r-- | Master/texmf-dist/tex/latex/timechart/timechart.sty | 1615 | ||||
-rwxr-xr-x | Master/tlpkg/bin/tlpkg-ctan-check | 2 | ||||
-rw-r--r-- | Master/tlpkg/tlpsrc/collection-pictures.tlpsrc | 1 | ||||
-rw-r--r-- | Master/tlpkg/tlpsrc/timechart.tlpsrc | 0 |
8 files changed, 5129 insertions, 1 deletions
diff --git a/Master/texmf-dist/doc/latex/timechart/README.md b/Master/texmf-dist/doc/latex/timechart/README.md new file mode 100644 index 00000000000..8a4830fa6ad --- /dev/null +++ b/Master/texmf-dist/doc/latex/timechart/README.md @@ -0,0 +1,16 @@ +# `timechart` - A package for drawing chronological charts + +## Description + +This package provides for the easy creation of chronological charts which show visually the relative historical +positions of people and events. Each event or period can be specified by a single line of LaTeX code comprising +(possibly uncertain) start and finish dates and a label, and the package takes care of indicating the uncertainties and +whether intervals extend beyond the specified bounds of the chart. + +## Author + +This package is by Alan J. Cain: a.j.cain (AT) gmail.com + +## Licence + +Released under the LaTeX Project Public License v1.3c or later: https://www.latex-project.org/lppl.txt diff --git a/Master/texmf-dist/doc/latex/timechart/timechart-doc.pdf b/Master/texmf-dist/doc/latex/timechart/timechart-doc.pdf Binary files differnew file mode 100644 index 00000000000..31b778e9de7 --- /dev/null +++ b/Master/texmf-dist/doc/latex/timechart/timechart-doc.pdf diff --git a/Master/texmf-dist/source/latex/timechart/timechart.dtx b/Master/texmf-dist/source/latex/timechart/timechart.dtx new file mode 100644 index 00000000000..82048a452a8 --- /dev/null +++ b/Master/texmf-dist/source/latex/timechart/timechart.dtx @@ -0,0 +1,3458 @@ +% \iffalse meta-comment +% +% Copyright (C) 2025 Alan J. Cain +% +% This file may be distributed and/or modified under the conditions of the LaTeX Project Public Licence, either version +% 1.3 of this licence or (at your option) any later version. The latest version of this licence is in: +% +% http://www.latex-project.org/lppl.txt +% +% and version 1.3c or later is part of all distributions of LaTeX version 2008-05-04 or later. +% +% \fi +% +% \iffalse +%<*driver> +\PassOptionsToPackage{inline}{enumitem} +\documentclass{l3doc} + + + +% Make space for larger numbers in contents + +\makeatletter +\ExplSyntaxOn + +\cs_gset:Npn \l@subsection { \@dottedtocline{2}{2.5em}{2.8em} } +\cs_gset:Npn \l@subsubsection { \@dottedtocline{3}{5.3em}{4em} } + +\ExplSyntaxOff +\makeatother + + + +% Link formatting + +\usepackage{xcolor} + +\definecolor{linkcolor}{rgb}{0.0,0.4,0.7} +\colorlet{citecolor}{linkcolor} +\colorlet{urlcolor}{linkcolor} + +\hypersetup{ + linkcolor=linkcolor,% + citecolor=citecolor,% + urlcolor=urlcolor,% +} + +\newcommand*\fullref[2]{% + \hyperref[#2]{#1\penalty 200\ \ref*{#2}}% +} + + + +% Use siunitx for typesetting dimensions + +\usepackage{siunitx} +\sisetup{ + mode=match, +} +\DeclareSIUnit\point{pt} + + + +% Listings and grammars + +\usepackage{listings} +\lstset{ + language=[LaTeX]TeX, + basicstyle=\small\ttfamily, + basewidth=0.5em, + numbers=left, + numberstyle=\scriptsize\sffamily, +} + +\usepackage{simplebnf} + + + +% Macros for describing keys + +\newlist{optionlist}{description}{1} +\setlist[optionlist]{ + leftmargin=3em, + style=unboxed, + labelsep=1em, + font=\descriptionitemcolon, + nosep, +} +\newcommand*{\descriptionitemcolon}[1]{\kern 1em #1:} + + +\newcommand*\key[1]{\texttt{#1}} +\newcommand*\val[1]{\texttt{#1}} +\newcommand*\keyval[2]{\texttt{#1=#2}} + +\usepackage{xcolor} +\definecolor{keycolor}{rgb}{0.8,0.0,0.0} +\definecolor{keyvaluecolor}{rgb}{0.0,0.65,0.0} + +\ExplSyntaxOn + +\NewDocumentEnvironment{describekey}{ m m m }{ + \group_begin: + \group_begin: + \noindent{\textcolor{keycolor}{\texttt{#1}}} + \str_if_empty:nF{#2} + { + \skip_set:Nn{\parfillskip}{0pt} + \str_case:nnF{#2} + { + {style} + { + \hfil + (style,~initially~#3) + } + } + { + \textcolor{keyvaluecolor}{\texttt{=}\meta{#2}} + \str_if_empty:nF{#3} + { + \hfil + (default\nobreakspace\texttt{#3}) + } + } + } + \par + \group_end: + \nobreak + \skip_add:Nn{\leftskip}{\parindent} + \noindent\ignorespaces +}{ + \par + \group_end: +} + +\ExplSyntaxOff + + +\newcommand*\mcode[1]{\texttt{#1}} +\newcommand*\param[1]{\texttt{\##1}} + + + +% Other macros + +\newcommand*\TikZ{\texorpdfstring{Ti\textit{k}Z}{TikZ}} +\newcommand*\ISO{\textsc{iso}} + + + +% Timechart + +\usepackage{timechart} + +\input{timechart-example1-setup.tex} + + + +\begin{document} +\DocInput{\jobname.dtx} +\end{document} +%</driver> +% \fi +% +% +% +% \GetFileInfo{timechart.sty} +% +% +% +% \title{^^A +% \pkg{timechart} ^^A +% --- A package for drawing chronological charts^^A +% \thanks{This file describes \fileversion, last revised \filedate.}^^A +% } +% +% \author{^^A +% Alan J. Cain^^A +% } +% +% \date{Released \filedate} +% +% \maketitle +% +% +% +% \begin{abstract} +% This package provides for the easy creation of chronological charts which show visually the relative historical +% positions of people and events. Each event or period can be specified by a single line of \LaTeX\ code comprising +% (possibly uncertain) start and finish dates and a label, and the package takes care of indicating the uncertainties +% and whether intervals extend beyond the specified bounds of the chart. +% \end{abstract} +% +% +% +% \tableofcontents +% +% +% +% \begin{documentation} +% +% \setcounter{secnumdepth}{2} +% +% +% \section{Introduction} +% +% The \pkg{timechart} package provides a system for the easy creation of chronological charts --- of the type pioneered +% by Joseph Priestley (1733--1804) in his `Chart of Biography' and more famously in his `New Chart of +% History'\footnote{\textsc{url}:~\url{https://commons.wikimedia.org/wiki/File:A_New_Chart_of_History_color.jpg}} --- +% which can show visually the relative historical positions of people and events. An example of what \pkg{timechart} can +% be used to produce is shown in \fullref{Figure}{fig:example1} on page~\pageref{fig:example1}. +% +% \begin{figure}[p] +% \centering +% \input{timechart-example1-timechart.tex} +% \caption{Timechart showing the lifetimes of Roman emperors from \timechartmakebeforeyear{50} to +% \timechartmakeafteryear{500}. Marks on each lifetime indicate the beginning (and, where relevant, the end) of that +% emperor's reign. Colours generally indicate dynasties, with shades of green indicating periods when the imperial +% power shifted between many short-reigning emperors.} +% \label{fig:example1} +% \end{figure} +% +% Essentially (and this was a design requirement), \emph{only one line} of \LaTeX\ code is required for each interval +% (which, in the case of \fullref{Figure}{fig:example1}, are mostly lifetimes). The \pkg{timechart} package takes care +% of indicating ranges of possible dates by suitable fading from or to transparency. It also handles indicators to show +% that intervals continue outside the specified scope of the chart. +% +% \pkg{timechart} was developed from, and replaced, a set of macros used to create the chronological charts in the +% author's book \textit{Form \& Number: A History of Mathematical Beauty}, which is available on the Internet Archive +% under a Creative Commons licence.\footnote{\textsc{url}: +% \url{https://archive.org/details/cain_formandnumber_ebook_large}} +% +% +% +% \paragraph*{Licence.} \noindent\pkg{timechart} is released under the LaTeX Project Public Licence v1.3c or +% later.\footnote{\textsc{url}: \url{https://www.latex-project.org/lppl.txt}} +% +% +% +% \paragraph*{Acknowledgements.} The author thanks Tânia Paulista for reading and commenting on an earlier draft of the +% documentation. +% +% +% +% \section{Requirements} +% +% \pkg{timechart} requires PGF/\TikZ\ and a \LaTeX\ kernel with \pkg{expl3} support (any kernel version since 2020-02-02 +% should suffice). +% +% +% +% \section{Installation} +% +% To install manually, run \texttt{tex timechart.ins} and copy the file \file{timechart.sty} to somewhere \LaTeX\ can +% find it. +% +% +% +% \section{Getting started} +% +% The \pkg{timechart} package is loaded as usual via \cs{usepackage}\texttt{\{timechart\}}. There are no package +% options. +% +% The small example in \fullref{Section}{sec:example} illustrates the basic principles of \pkg{timechart}. +% \fullref{Section}{sec:full-example} shows the full code used to produce the large example in \fullref{Figure}{fig:example1}. +% +% +% +% +% +% \section{Example} +% \label{sec:example} +% +% This section illustrates how to create the small chart shown in \fullref{Figure}{fig:example2-v2} on +% page~\pageref{fig:example2-v2}. +% +% The basic environment is \env{timechart}, which includes the specification of the start and finish years. The start +% year \timechartmakebeforeyear{50} is specified as \mcode{-50}, the finish year \timechartmakeafteryear{75} as +% \mcode{75}. Each interval in the chart is specified using the \cs{timechartinterval} command, which takes three +% mandatory parameters: a start year, a finish year, and a label. The following code produces the flawed preliminary +% version shown in \fullref{Figure}{fig:example2-v1}. +% +% \lstinputlisting{timechart-example2-v1.tex} +% +% \begin{figure}[ht] +% \centering +% \input{timechart-example2-v1.tex} +% \medskip +% \caption{Flawed preliminary version of a chart showing the lifetimes of Roman emperors of the Julio-Claudian +% dynasty. (The final version is shown in \fullref{Figure}{fig:example2-v2}.)} +% \label{fig:example2-v1} +% \end{figure} +% +% This first attempt result illustrates some of the principles of \pkg{timechart}. Each interval has been placed on its +% own line. More precisely, the \(y\) coordinate of the first interval is \(0\) and each use of \cs{timechartinterval} +% increments the `current \(y\) coordinate' by a specified amount. (Various commands are available to set the \(y\) +% coordinate manually or to reset it automatically when it passes certain bounds; see +% \fullref{Subsection}{subsec:positioning}.) Horizontally, the chart starts and finishes at the specified years. The topmost +% interval, indicating Augustus' life, has been truncated with an indicator that it begins before the specified start +% year of the chart. Vertically, the chart has been sized to fit around the specified intervals. +% +% But this version is hardly satisfactory, for many reasons. The chart finishes between two minor ticks on the axis, +% because the intervals between major and minor ticks default respectively to 10 years and 50 years. The black intervals +% and text do not contrast well with the black axis and grid. The serif font is perhaps not best suited to label the +% intervals. And the label `Augustus' has been lost, since labels are by default placed on the left of intervals. To +% rectify these problems, some changes are necessary, all of which can be made using key-value syntax in an optional +% parameter to the \env{timechart} environment or the \cs{timechartinterval} command: +% \begin{enumerate} +% \item Set the intervals between major and minor ticks to 5 years and 25 years respectively, by setting +% \keyval{minor tick interval}{5} and \keyval{major tick interval}{25}. +% \item Change the colour of the grid to light grey by appending \keyval{draw}{lightgray} to the \key{grid} style +% \item Change the colour of the axis to grey by appending \keyval{draw}{gray} to the \key{axis line}, \key{minor +% tick}, and \key{major tick} styles. +% \item Change the font used for interval labels to small san-serif by appending \keyval{node +% font}{\cs{sffamily}\cs{small}} to the \key{interval label} style +% \item Change the colour of the intervals by defining a style \key{julioclaudian} that sets \keyval{interval bar +% color}{red!80!black} and applying it to each interval via its optional argument. (While \key{interval bar +% color} could be set locally for each interval, it is better to define a style that corresponds to the +% semantic meaning of the colour: in this case, a single dynasty.) +% \item Use the \key{right} key to place some labels on the right +% \end{enumerate} +% +% The result is the following code, which produces \fullref{Figure}{fig:example2-v2}. +% +% \lstinputlisting{timechart-example2-v2.tex} +% +% \begin{figure}[ht] +% \centering +% \input{timechart-example2-v2.tex} +% \caption{Chart showing the lifetimes of Roman emperors of the Julio-Claudian dynasty.} +% \label{fig:example2-v2} +% \end{figure} +% +% +% +% \section{Configuration} +% +% All \pkg{timechart} configuration, global or local, is via PGF keys, so some familiarity with their use is beneficial; +% see the PGF/\TikZ\ manual. +% +% Configuration keys for \pkg{timechart} are contained in \key{/timechart/} in the PGF keys hierarchy. The +% \meta{options} passed to the \env{timechart} environment or any of the commands \cs{timechartinterval}, +% \cs{timecharttext}, or \cs{timechartspace} are processed within \key{/timechart/} (since \key{/timechart/.cd} is +% executed before keys are processed). +% +% The user may wish to define PGF styles for different kinds of interval within a chart. For example, one could +% define styles \key{science} and \key{art} that set a particular colour for the interval, and write +% \cs{timechartinterval}\key{[science]}\marg{birth}\marg{death}\marg{name} or +% \cs{timechartinterval}\key{[art]}\marg{birth}\marg{death}\marg{name} to distinguish visually the lifetimes of +% various scientists and artists. +% +% +% +% \section{Specifying dates and date ranges} +% \label{sec:dates-and-ranges} +% +% Using \pkg{timechart} requires specification of dates and date ranges for the start and finish of each interval, both +% of which may be uncertain. +% +% The basic specification of a date uses \ISO~8601 format \mcode{YYYY-MM-DD}. This format specifies a date with +% day-level precision; use \mcode{YYYY-MM} and \mcode{YYYY} for month- and year-level precision. If the date is prefixed +% by \mcode{-}, it is treated as the corresponding date before the epoch. (This is a difference with \ISO~8601, in which +% \texttt{0} represents \timechartmakebeforeyear{1}, \texttt{-1} represents \timechartmakebeforeyear{2}, and so on.) So +% (assuming that one is using \textsc{bce}/\textsc{ce}) one uses \mcode{-100} to indicate \timechartmakebeforeyear{100} +% and \mcode{100} to indicate \timechartmakeafteryear{100}. (The era indicators `\textsc{bce}' and `\textsc{ce}' appear +% on the axis. Alternative era indicators --- or a different epoch --- can be specified; see +% \fullref{Section}{sec:era-indicators}.) +% +% A date can be prefixed with a \mcode{c} to indicate `circa', such as \mcode{c-100} for `circa +% \timechartmakebeforeyear{100}' and \mcode{c100} for `circa \timechartmakeafteryear{100}'. When an interval is drawn in +% a chart, `circa' will be indicated by automatically creating (or extending) a range according to the value of the key +% \key{circa uncertainty} (see \fullref{Subsection}{subsec:intervals}). +% +% A date range comprises two dates (each with or without \mcode{c}) separated by a slash \mcode{/}, with the first date +% being earlier (or equal to) the second date. (The slash indicates a range of dates per \ISO~8601.) A date +% range can be used to indicate a broader uncertainty than the default `circa', or to indicate a definite range within +% which an interval starts or ends. +% +% \begin{description} +% \item[Examples of correctly formatted dates and date ranges:] \mcode{-50}, \mcode{100}, \mcode{c-50}, +% \mcode{c100}, \mcode{-50/100}, \mcode{c-50 / +100}, \mcode{-50/c100}, \mcode{c-50/c100}, \mcode{-585-05-28}, +% \mcode{1947-12-01}, \mcode{1989-11}. +% +% \item[Examples of incorrectly formatted date and date ranges:] \mcode{100?}, \mcode{100CE}, \mcode{100BCE}, +% \mcode{-50-100}, \mcode{100/-50}. +% \end{description} +% +% \noindent That is, the syntax for dates and date ranges is per the following (not-quite-formal) grammar: +% +% \smallskip +% +% \begin{bnf} +% \meta{cdate-or-crange} ::= \meta{cdate} +% | \meta{crange} ;; +% \meta{cdate} ::= \meta{date} +% | \mcode{c}\meta{date} ;; +% \meta{date} ::= \meta{pdate} +% | \mcode{-}\meta{pdate} ;; +% \meta{pdate} ::= \meta{year}\mcode{-}\meta{month}\mcode{-}\meta{day} +% | \meta{year}\mcode{-}\meta{month} +% | \meta{year} ;; +% \meta{crange} ::= \meta{\(\text{date}_1\)}\mcode{/}\meta{\(\text{date}_2\)} +% | \mcode{c}\meta{\(\text{date}_1\)}\mcode{/}\meta{\(\text{date}_2\)} +% | \meta{\(\text{date}_1\)}\mcode{/}\mcode{c}\meta{\(\text{date}_2\)} +% | \mcode{c}\meta{\(\text{date}_1\)}\mcode{/}\mcode{c}\meta{\(\text{date}_2\)} ~~~(with \(\text{date}_1 \leq \text{date}_2\)) ;; +% \end{bnf} +% +% \smallskip +% +% \noindent The bounds of the \env{timechart} environment (see \fullref{Section}{sec:timechart-env}) must satisfy +% \meta{cdate} in this grammar (although only the \meta{year} is used); the start and finish dates of an +% \cs{timechartinterval} command (see \fullref{Subsection}{subsec:intervals}) must satisfy \meta{cdate-or-crange}; the +% parameter of an \cs{timecharttext} command (see \fullref{Subsection}{subsec:text}) must satisfy \meta{date}. +% +% \medskip +% \noindent \textit{Note.} For performance reasons, the date parser does only limited error-checking. Months outside the +% range from \mcode{01} to \mcode{12} or days outside the range of the specified month will be ignored. Otherwise +% malformed dates or date ranges may produce obscure error messages or unexpected results. +% +% +% +% \section{\env{timechart} environment} +% \label{sec:timechart-env} +% +% \begin{function}{timechart} +% \begin{syntax} +% \cs{begin}\texttt{\{}\env{timechart}\texttt{\}}\oarg{options}\marg{start}\marg{finish} +% \meta{content} +% \cs{end}\texttt{\{}\env{timechart}\texttt{\}} +% \end{syntax} +% This is the main environment for creating a chronological chart. The mandatory arguments \meta{start} and +% \meta{finish} specify the first and last years of the chart. These can be dates with circa indicators (that is, they +% satisfy \meta{cdate} in the grammar in \fullref{Section}{sec:dates-and-ranges}), but the circa specifier has no +% effect and only the `year' part of the date is used. The optional argument \meta{options} supplies PGF keys that +% apply to the entire chart. +% +% The \meta{content} comprises commands like \cs{timechartinterval}, \cs{timecharttext}, \cs{timechartspace}, +% commands for positioning, as described in \fullref{Section}{sec:within}, and the user's own \TikZ\ code. +% \end{function} +% +% +% +% \subsection{General configuration of the \env{timechart} environment} +% +% \begin{describekey}{/timechart/width}{dimension}{\cs{textwidth}} +% The width of the chart. This refers to the width of the grid and axis of the chart, not +% including intervals that pass beyond the specified limits of the chart, or axis labels that protrude beyond the +% width of the axis itself. +% \end{describekey} +% +% +% \begin{describekey}{/timechart/tolerance}{dimension}{5pt} +% The length by which an interval is allowed to pass beyond the limits of the chart before it `counts' as doing so +% and the appropriate indicator is drawn. +% \end{describekey} +% +% +% \begin{describekey}{/timechart/beyond length}{dimension}{5pt} +% The length of the indicator that an interval passes beyond the limits of the chart. +% \end{describekey} +% +% \begin{describekey}{/timechart/beyond x radius}{dimension}{4pt} +% The horizontal radius of the concave part of the indicator that an interval passes beyond the limits of the chart. +% (The vertical radius will be half the thickness of the bar.) +% \end{describekey} +% +% \begin{describekey}{/timechart/ystep}{dimension}{-10pt} +% The default length (positive or negative) by which the current \(y\) coordinate is automatically adjusted after each +% interval, text, or space is placed. +% \end{describekey} +% +% \begin{describekey}{/timechart/minor tick interval}{number}{10} +% The number of years (which must be positive) between each minor tick on the axis. +% \end{describekey} +% +% \begin{describekey}{/timechart/major tick interval}{number}{50} +% The number of years (which must be positive) between each major tick on the axis and each vertical line in the grid. +% \end{describekey} +% +% +% +% \subsection{Grid configuration} +% +% \begin{describekey}{/timechart/no grid}{}{} +% Do not draw the grid. +% \end{describekey} +% +% \begin{describekey}{/timechart/grid top ysep}{dimension}{3pt} +% Distance between the top of the grid and the topmost interval or space. +% \end{describekey} +% +% \begin{describekey}{/timechart/grid bottom ysep}{dimension}{3pt} +% Distance between the bottom of the grid and the bottommost interval or space. +% \end{describekey} +% +% \begin{describekey}{/timechart/grid}{style}{empty} +% Style for drawing the grid. +% \end{describekey} +% +% +% +% \subsection{Axis configuration} +% +% \begin{describekey}{/timechart/no axis}{}{} +% Do not draw the axis. +% \end{describekey} +% +% \begin{describekey}{/timechart/axis line}{style}{\keyval{line cap}{rect}} +% Style for drawing the axis line. +% \end{describekey} +% +% \begin{describekey}{/timechart/minor tick}{style}{empty} +% Style for drawing minor ticks. +% \end{describekey} +% +% \begin{describekey}{/timechart/minor tick length}{dimension}{1.5mm} +% Length of minor ticks. +% \end{describekey} +% +% \begin{describekey}{/timechart/major tick}{style}{empty} +% Style for drawing major ticks. +% \end{describekey} +% +% \begin{describekey}{/timechart/major tick length}{dimension}{3mm} +% Length of major ticks. +% \end{describekey} +% +% \begin{describekey}{/timechart/major tick label}{style}{as described below} +% Style for labels on the major ticks on the axis. The intial style essentially sets \keyval{inner sep}{0}, +% \keyval{outer sep}{0}, \keyval{anchor}{mid west}, \keyval{rotate}{90}. +% \end{describekey} +% +% +% +% \section{Within the \env{timechart} environment} +% \label{sec:within} +% +% +% +% +% \subsection{Intervals} +% \label{subsec:intervals} +% +% \begin{function}{\timechartinterval} +% \begin{syntax} +% \cs{timechartinterval}\oarg{options}\marg{start}\marg{finish}\marg{label} +% \end{syntax} +% This command creates an interval in the chart at the current \(y\) coordinate between the specified \meta{start} and +% \meta{finish}, with the given \meta{label}. These arguments are mandatory. Each of \meta{start} and \meta{finish} +% can be either a year or a range of years, possibly with circa markers. That is, each must satisfy +% \meta{cyear-or-crange} in the grammar in \fullref{Section}{sec:dates-and-ranges}. +% +% The optional argument \meta{options} specifies PGF keys within \key{/timechart/} that are applied locally to the +% interval. +% +% The current \(y\) coordinate will be adjusted according to \key{/timechart/ystep} unless \key{/timechart/no +% autostep} has been set. +% \end{function} +% +% +% +% \subsubsection{Interval configuration} +% +% \begin{describekey}{/timechart/no autostep}{}{} +% Do not automatically alter the current \(y\) coordinate by the amount specified in \key{/timechart/ystep}. +% \end{describekey} +% +% \begin{describekey}{/timechart/ref}{label}{none} +% Make the interval label a hyperlink to the position labelled by \meta{label}. +% \end{describekey} +% +% \begin{describekey}{/timechart/mark}{comma-separated list of years}{empty} +% Draw marks in the interval at the years contained in the list. Each entry in the list must be a definite year (that +% is, must satisfy \meta{year} in the grammar in \fullref{Section}{sec:dates-and-ranges}). The colour of marks can be +% specified using \key{/timechart/interval mark color}. \key{/timechart/marks} is a synonym for this key. +% \end{describekey} +% +% \begin{describekey}{/timechart/circa uncertainty}{number}{3} +% Treat a circa indicator \mcode{c} as indicating an uncertainity of \(\pm\meta{number}\). +% \end{describekey} +% +% \begin{describekey}{/timechart/interval minimum width}{dimension}{1pt} +% Ensure that any interval has a width of at least \meta{dimension}. This is useful to ensure that a single event is +% visible in the chart. +% +% If an interval is specified with start and finish ranges, and with \keyval{start range}{fade} and \keyval{finish +% range}{fade}, then the `certain' portion of the interval will also have width at least \meta{dimension}. (This +% restriction prevents a common rendering error where start and finish fadings around a `certain' interval of length +% \(0\) would not quite meet.) +% \end{describekey} +% +% \begin{describekey}{/timechart/interval bar color}{color}{black} +% Fill the interval bar with \meta{color}. +% \end{describekey} +% +% \begin{describekey}{/timechart/interval bar thickness}{dimension}{8pt} +% Set the vertical thickness of the interval bar to \meta{dimension}. +% \end{describekey} +% +% \begin{describekey}{/timechart/interval bar node name}{string}{bar node} +% Set the name of the node containing the interval bar to \meta{string}. +% \end{describekey} +% +% \begin{describekey}{/timechart/interval mark color}{color}{gray} +% Draw marks using \meta{color}. +% \end{describekey} +% +% \begin{describekey}{/timechart/interval label}{style}{empty} +% Style to apply to an interval label. +% \end{describekey} +% +% \begin{describekey}{/timechart/interval label centered}{style}{as below} +% Style to apply to an interval label placed centrally. Initially, this style executes the style +% \key{/timechart/interval label} and sets \keyval{text}{white}. The reason for a separate style for centred labels is +% that often a contrasting colour will be required. For instance, labels positioned to the left and right may be +% black, but if the bar is black, a centred label should be a light colour. +% \end{describekey} +% +% \begin{describekey}{/timechart/interval label centered background}{style}{as below} +% Style to apply to the `background' interval label placed centrally. Initially, this style executes +% \key{/timechart/interval label}. The `background' interval label is simply the usual label and is placed in the same +% location, but on a layer behind the bar and, unlike the label itself, is not clipped. The reason for this style is +% that if the bar is narrow, part of the label text (such as ascenders and/or descenders) may naturally extend beyond +% the bar itself and it may be useful that thse should appear in a different colour. +% \end{describekey} +% +% \begin{describekey}{/timechart/interval label baseline}{dimension}{-3pt} +% Position the baseline of the interval label \meta{dimension} below the current \(y\) coordinate (which is the +% midpoint of the interval bar). +% \end{describekey} +% +% \begin{describekey}{/timechart/interval label pos}{position}{left} +% Specify where to place the label relative to the interval bar: \meta{position} may be \val{left}, \val{center}, or +% \val{right}. The position \val{center} places the label at the midpoint of \emph{the visible segment of the solid +% part} of the interval bar (that is, not including fading at the start or finish of the bar, and not including part +% of the bart that would extend beyond the bounds of the chart). Further, a centred label is clipped to the size of +% the bar and a unclipped `background' copy of it is drawn behind the bar, so that the portion appearing `on' and +% `outside' the bar can have different styles. (See the keys \key{/timechart/interval label centered} and +% \key{/timechart/interval label centered background}.) +% \end{describekey} +% +% \begin{describekey}{/timechart/interval label node name}{string}{label node} +% Set the name of the node containing the interval label to \meta{string}. +% \end{describekey} +% +% \begin{describekey}{/timechart/start range}{range-type}{fade} +% Type of indication of the range where an interval may start. \meta{range-type} can be \val{fade}, which produces an +% indicator like \timechartlegendstartrange[legend item width=8mm], or \val{slant}, which produces +% \timechartlegendstartrange[legend item width=8mm,start range=slant]. +% \end{describekey} +% +% \begin{describekey}{/timechart/finish range}{range-type}{fade} +% Type of indication of the range where an interval may finish. \meta{range-type} can be \val{fade}, which produces an +% indicator like \timechartlegendfinishrange[legend item width=8mm], or \val{slant}, which produces +% \timechartlegendfinishrange[legend item width=8mm,finish range=slant]. +% \end{describekey} +% +% \begin{describekey}{/timechart/fade minimum width}{dimension}{0pt} +% No start or finish range indicator of type \val{fade} will be drawn if it is smaller than \meta{dimension}. Using +% this key is sometimes necessary because, under certain limited circumstances, a very narrow fading can cause +% PGF/\TikZ\ to produce a \texttt{dimension too large} error. +% \end{describekey} +% +% +% +% \subsection{Text} +% \label{subsec:text} +% +% \begin{function}{\timecharttext} +% \begin{syntax} +% \cs{timecharttext}\oarg{options}\marg{year}\marg{text} +% \end{syntax} +% Place \meta{text} in the time chart at the current \(y\) coordinate and at the horizontal position of \meta{year}, +% which must be a definite year (that is, must satsify \meta{year} in the grammar in +% \fullref{Section}{sec:dates-and-ranges}). +% +% The optional argument \meta{options} specifies PGF keys within \key{/timechart/} that are applied locally. +% +% The current \(y\) coordinate will be adjusted according to \key{/timechart/ystep} unless \key{/timechart/no +% autostep} has been set. +% \end{function} +% +% +% +% \subsubsection{Text configuration} +% +% \begin{describekey}{/timechart/text node name}{string}{text node} +% Set the name of the node containing the text to \meta{string}. +% \end{describekey} +% +% \begin{describekey}{/timechart/text}{style}{empty} +% Style to apply to the text. +% \end{describekey} +% +% \begin{describekey}{/timechart/text baseline}{dimension}{-3pt} +% Position the baseline of the text \meta{dimension} below the current \(y\) coordinate. +% \end{describekey} +% +% \begin{describekey}{/timechart/text pos}{position}{left} +% Specify where to place the label relative to the given \meta{year}: \meta{position} may be \val{left}, \val{center}, +% or \val{right}. +% \end{describekey} +% +% +% +% \subsection{Space} +% +% \begin{function}{\timechartspace} +% \begin{syntax} +% \cs{timechartspace}\oarg{options} +% \end{syntax} +% Create a space in the time chart at the current \(y\) coordinate, with the same effect on vertical spacing as an +% interval. More precisely, the current \(y\) coordinate will be adjusted according to \key{/timechart/ystep} unless +% \key{/timechart/no autostep} has been set. +% +% The optional argument \meta{options} specifies PGF keys within \key{/timechart/} that are applied locally. +% \end{function} +% +% +% +% \subsection{Positioning} +% \label{subsec:positioning} +% +% The commands \cs{timechartinterval}, \cs{timecharttext}, and \cs{timechartspace} all act at the current \(y\) +% coordinate and change its value according to \key{/timechart/ystep} (unless \key{/timechart/no autostep} is used). +% There are several functions to set the current \(y\) coordinate and to have it reset automatically. +% +% \begin{function}{\timechartsety} +% \begin{syntax} +% \cs{timechartsety}\marg{dimension} +% \end{syntax} +% Set the current \(y\) coordinate to \meta{dimension}. +% \end{function} +% +% \begin{function}{\timechartsavey} +% \begin{syntax} +% \cs{timechartsavey} +% \end{syntax} +% Save the current \(y\) coordinate. If \cs{timechartresety} is used, the \(y\) coordinate resets to the last saved +% \(y\) coordinate. If the current \(y\) coordinate exceeds the minimum or maximum set by +% \cs{timechartsetyminimumautoreset} and \cs{timechartsetymaximumautoreset}, it will be reset to the last saved \(y\) +% coordinate. +% \end{function} +% +% \begin{function}{\timechartresety} +% \begin{syntax} +% \cs{timechartresety} +% \end{syntax} +% Reset the \(y\) coordinate to the last coordinate saved using \cs{timechartsavey}, or to \(0\) if there has been no +% use of \cs{timechartsavey} within the current \env{timechart} environment. +% \end{function} +% +% \begin{function}{\timechartsetyminimumautoreset, \timechartsetymaximumautoreset} +% \begin{syntax} +% \cs{timechartsetyminimumautoreset}\marg{min-coordinate} +% \cs{timechartsetymaximumautoreset}\marg{max-coordinate} +% \end{syntax} +% Set \(y\) coordinates that automatically trigger \cs{timechartresety} if the current \(y\) coordinate goes below +% \meta{min-coordinate} or above \meta{max-coordinate}. +% \end{function} +% +% \begin{function}{\timechartstepy} +% \begin{syntax} +% \cs{timechartstepy}\oarg{count} +% \end{syntax} +% Manually step the current \(y\) coordinate by \meta{count} times the value of \key{/timechart/ystep}. The default +% value of \meta{count} is 1. (The \key{/timechart/no autostep} does not affect \cs{timechartstepy}.) +% \end{function} +% +% +% +% \subsection{Completion} +% +% \begin{function}{\timechartfinish} +% Signal that the chart is complete and that the grid and axis should be drawn (unless the keys \key{/timechart/no +% grid} and/or \key{/timechart/no axis} have been used). It is not necessary to use this command: if it is not given, +% the grid and axis will be drawn at the end of the \env{timechart} environment. But after this command, the \TikZ\ +% nodes \texttt{grid} and \texttt{axis} are available, containing (respectively) the grid and the axis. These can be +% used in for further \TikZ\ drawing. +% +% Note that after \cs{timechartfinish}, none of the various \cs{timechart}\ldots\ commands are available inside that +% \env{timechart} environment. +% \end{function} +% +% +% +% \subsection{Shortcut keys} +% +% \begin{describekey}{/timechart/left}{}{} +% Equivalent to setting \key{/timechart/interval label pos} and \key{/timechart/text pos} to \val{left}. +% \end{describekey} +% +% \begin{describekey}{/timechart/center}{}{} +% Equivalent to setting \key{/timechart/interval label pos} and \key{/timechart/text pos} to \val{center}. +% \end{describekey} +% +% \begin{describekey}{/timechart/right}{}{} +% Equivalent to setting \key{/timechart/interval label pos} and \key{/timechart/text pos}{right} to \val{right}. +% \end{describekey} +% +% +% +% \section{Era indicators} +% \label{sec:era-indicators} +% +% \begin{function}{\timechartmakebeforeyear,\timechartmakeafteryear} +% \begin{syntax} +% \cs{timechartmakebeforeyear}\marg{number} +% \cs{timechartmakeafteryear}\marg{number} +% \end{syntax} +% Typeset \meta{number} (which should be a positive whole number) as a year before or after the epoch. By default, +% \cs{timechartmakebeforeyear}\marg{number} produces \timechartmakebeforeyear{\meta{number}} and +% \cs{timechartmakeafteryear}\marg{number} produces \timechartmakeafteryear{\meta{number}}. +% +% These commands are used for axis labels and can be re-defined by the user. For example, if \textsc{bc}/\textsc{ad} +% is preferred to \textsc{bce}/\textsc{ce}, the user can define +% \iffalse +%<*example> +% \fi +\begin{lstlisting} +\renewcommand*{\timechartmakebeforeyear}[1]{#1~\textsc{bc}} +\renewcommand*{\timechartmakeafteryear}[1]{\textsc{ad}~#1} +\end{lstlisting} +% \iffalse +%</example> +% \fi +% Similarly, if \textsc{ah}/\textsc{bh} is preferred, the user can define +% \iffalse +%<*example> +% \fi +\begin{lstlisting} +\renewcommand*{\timechartmakebeforeyear}[1]{#1~\textsc{bh}} +\renewcommand*{\timechartmakeafteryear}[1]{\textsc{ah}~#1} +\end{lstlisting} +% \iffalse +%</example> +% \fi +% \end{function} +% +% +% +% \section{Legend} +% \label{sec:keys} +% +% \pkg{timechart} supplies a number of auxiliary macros for creating a legend to explain, for example the significance +% of different colours of intervals. For example, \fullref{Figure}{fig:example1-legend} shows a suitable legend for +% \fullref{Figure}{fig:example1}. +% +% \begin{figure}[t] +% \centering +% \input{timechart-example1-legend.tex} +% \caption{Example legend for the timechart shown in \fullref{Figure}{fig:example1}.} +% \label{fig:example1-legend} +% \end{figure} +% +% The \cs{timechartlength}\ldots\ macros are \emph{not} meant to be used inside a \env{timechart} environment, but in +% locations such as running text or a \env{tabular} environment. +% +% \begin{function}{\timechartlegenditem} +% \begin{syntax} +% \cs{timechartlegenditem}\oarg{options} +% \end{syntax} +% Draw a bar suitable for use in a legend. \meta{options} specifies PGF keys that are applied within +% \key{/timechart}. The same PGF keys that affect interval bars affect the drawn bar, as do the keys listed below. +% \end{function} +% +% \begin{function}{\timechartlegendstartrange, \timechartlegendfinishrange} +% \begin{syntax} +% \cs{timechartlegendstartrange}\oarg{options} +% \cs{timechartlegendfinishrange}\oarg{options} +% \end{syntax} +% Draw a bar suitable for use in a legend, with a start or finish range. \meta{options} specifies PGF keys that are +% applied within \key{/timechart/}. The same PGF keys that affect interval bars affect the drawn bar, as do the +% keys listed below. +% \end{function} +% +% \begin{describekey}{/timechart/legend item width}{dimension}{9mm} +% When using macros \cs{timechartlegenditem}, \cs{timechartlegendstartrange}, or \cs{timechartlegendfinishrange}, draw +% a bar of total width \meta{dimension}. +% \end{describekey} +% +% \begin{describekey}{/timechart/legend item range width}{dimension}{3mm} +% When using \cs{timechartlegendstartrange} or \cs{timechartlegendfinishrange}, draw a bar with a range of width +% \meta{dimension}. +% \end{describekey} +% +% +% +% \section{Usage notes} +% +% \subsection{Additional \TikZ\ code} +% +% The \env{timechart} environment is a \env{tikzpicture}. The user can add any \TikZ\ code before, between, or after +% content created using the \cs{timechart}\ldots\ commands. Each use of \cs{timechartinterval} defines two nodes. One, +% by default named \val{bar node}, contains the interval bar; the other, by default named \val{label node}, contains the +% interval label. Similarly, text added using \cs{timecharttext} is contained in a node, by default named \val{text +% node}. (The default names are re-used, but can be changed using the keys \key{/timechart/interval bar node name}, +% \key{/timechart/interval label node name}, and \key{/timechart/text node name}.) The user can use these nodes to +% position extra content. +% +% If the \cs{timechartcomplete} command is used (after which the \cs{timechart}\ldots\ commands are no longer available +% within the \env{timechart} environment) the nodes \mcode{grid} and \mcode{axis}, which contain the grid and the axis, +% are also available. +% +% +% +% \subsection{\texorpdfstring{`year zero'}{year zero}} +% +% Although calendars typically do not admit a `year zero' (for instance, \timechartmakebeforeyear{1} is immediately +% followed by \timechartmakeafteryear{1}, with no intervening `year zero'), \pkg{timechart} does allow \val{0} for the +% \meta{start} or \meta{finish} of the \env{timechart} environment or as the \meta{start} or \meta{finish} of +% \cs{timechartinterval} or the \meta{year} of \cs{timecharttext}. But `year zero' is indicated on the axis by a special +% epoch marker showing the last year before and first year after the epoch. +% +% +% +% \subsection{\texorpdfstring{\texttt{dimension too large}}{dimension too large} error} +% +% Under certain limited circumstances, a very narrow fading can cause PGF/\TikZ\ to produce a \texttt{dimension too +% large} error. This error can be triggered by a range of type \val{fade} at the start or end of an interval when the +% range is small compared to the width of the chart. In this case, the fading is unlikely to be visible, so one can +% simply set \key{/timechart/fade minimum width} to a suitable small positive value, which will stop the fading from +% being drawn and so prevent the error. +% +% +% +% \section{Appendix: full example source} +% \label{sec:full-example} +% +% This section contains the necessary source code to produce the example timechart and legend shown in +% \fullref{Figures}{fig:example1} and \ref{fig:example1-legend} on pages \pageref{fig:example1} and +% \pageref{fig:example1-legend}. +% +% +% +% \subsection{Setup source} +% +% \lstinputlisting{timechart-example1-setup.tex} +% +% +% +% \subsection{Timechart source} +% +% \lstinputlisting[breaklines=true]{timechart-example1-timechart.tex} +% +% +% +% \subsection{Legend source} +% +% \lstinputlisting{timechart-example1-legend.tex} +% +% +% +% \end{documentation} +% +% +% +% \begin{implementation} +% +% \setcounter{secnumdepth}{7} +% +% +% +% \section{Implementation} +% +% \begin{macrocode} +%<*package> +%<@@=timechart> +% \end{macrocode} +% +% +% +% \subsection{Coding standard} +% +% This package makes extensive use of \pkg{pgfmath} computations. The usual \pkg{expl3} standard of ending variables +% with a type indicator (\texttt{\_bool}, \texttt{\_int}, etc.) is therefore adapted as follows: +% \begin{itemize} +% \item[\texttt{\_year}] Stores a year, which could in principle be fractional. +% \item[\texttt{\_pgf}] Stores a length calculated by \pkg{pgfmath}. (Unlike \texttt{\_dim}, there is no underlying +% dimension register.) +% \item[\texttt{\_x}] Stores a raw \(x\) coordinate (not in \TikZ's XY-coordinate system). +% \item[\texttt{\_y}] Stores a raw \(y\) coordinate (not in \TikZ's XY-coordinate system). +% \item[\texttt{\_text}] Stores text (not an \pkg{expl3} string). +% \end{itemize} +% +% +% +% \subsection{Initial set-up} +% +% Package identification/version information. +% \begin{macrocode} +\NeedsTeXFormat{LaTeX2e}[2020-02-02] +\ProvidesExplPackage{timechart}{2025-02-15}{0.51} + {Typesetting chronological charts} +% \end{macrocode} +% +% +% +% \subsection{Load TikZ} +% +% \begin{macrocode} +\RequirePackage{tikz} +% \end{macrocode} +% In the remainder of the package, only a limited subset of \TikZ\ is used, and PGF code is preferred. For PGF keys, it +% is necessary to use \texttt{\textasciitilde} in place of a space. +% +% +% +% \subsection{Scratch variables} +% +% \begin{macro}{\l_@@_tmpa_bool,\l_@@_tmpb_bool,\l_@@_tmpc_bool,\l_@@_tmpd_bool} +% Scratch boolean variables. +% \begin{macrocode} +\bool_new:N\l_@@_tmpa_bool +\bool_new:N\l_@@_tmpb_bool +\bool_new:N\l_@@_tmpc_bool +\bool_new:N\l_@@_tmpd_bool +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\l_@@_tmpa_dim,\l_@@_tmpb_dim,\l_@@_tmpc_dim,\l_@@_tmpd_dim} +% Scratch dimension variables, reusing \cs{l_tmpa_dim} and \cs{l_tmpb_dim} with uniform names. +% \begin{macrocode} +\cs_set_eq:NN\l_@@_tmpa_dim\l_tmpa_dim +\cs_set_eq:NN\l_@@_tmpb_dim\l_tmpb_dim +\dim_new:N\l_@@_tmpc_dim +\dim_new:N\l_@@_tmpd_dim +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Generic auxiliary functions} +% +% \begin{macro}{\@@_make_ref:NN} +% Make hyperreference from text, if the supplied target is non-empty. +% \begin{arguments} +% \item Reference for hyperlink target, or empty. +% \item Text. +% \end{arguments} +% \begin{macrocode} +\cs_new:Npn\@@_make_ref:NN #1#2 + { + \str_if_empty:NTF #1 + { #2 } + { \hyperref[#1]{#2} } + } +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{PGF auxiliary functions} +% +% \begin{macro}{@@_pgfmathsetbool:nn} +% Set an \pkg{expl3} boolean variable to the outcome of a \pkg{pgfmath} comparison. This macro is simply a wrapper +% around \cs{pgfmathsetmacro} using \mcode{ifthenelse} and returning the boolean literal true or false. +% \begin{macrocode} +\cs_new:Npn\@@_pgfmathsetbool:nn #1#2 + { + \pgfmathsetmacro{#1}{ifthenelse(#2,"\c_true_bool","\c_false_bool")} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_if_equal:nnF}% +% Use \pkg{pgfmath} to check whether \param{1} and \param{2} are equal. If not, execute \param{3}. +% \begin{macrocode} +\cs_new:Npn\@@_if_equal:nnF #1#2#3 + { + \@@_pgfmathsetbool:nn{\l_@@_tmpa_bool}{#1==#2} + \bool_if:NF\l_@@_tmpa_bool{#3} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_pgfextractxy:nnn} +% Extract coordinates of \param{3} (a PGF point) to dimension variables \param{1} and \param{2}. This macro simply +% combines the functionality of \cs{pgfextractx} and \cs{pgfextracty}. +% \begin{macrocode} +\cs_new:Npn\@@_pgfextractxy:nnn #1#2#3 + { + \pgf@process{#3} + #1=\pgf@x\relax + #2=\pgf@y\relax + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_hsmash_pgfnode:nnnnn} +% Do the same as \cs{pgfnode} but only update the bounding box `vertically'. +% \begin{macrocode} +\cs_new:Npn\@@_hsmash_pgfnode:nnnnn #1#2#3#4#5 + { + \pgfinterruptboundingbox + \pgfnode{#1}{#2}{#3}{#4}{#5} + \pgfcoordinate + {@@_tmpa_coord} + {\pgfpointanchor{current~bounding~box}{south}} + \pgfcoordinate + {@@_tmpb_coord} + {\pgfpointanchor{current~bounding~box}{north}} + \endpgfinterruptboundingbox + \pgfextractx + {\l_@@_tmpa_dim} + {\pgfpointanchor{@@_tmpa_coord}{center}} + \pgfextractx + {\l_@@_tmpb_dim} + {\pgfpointanchor{@@_tmpb_coord}{center}} + \pgfpathmoveto{\pgfpoint{\l_@@_tmpa_dim}{0}} + \pgfpathmoveto{\pgfpoint{\l_@@_tmpb_dim}{0}} + \pgfusepath{discard} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_make_rectangle_node:nnnn} +% Make a node with south west corner \param{1}, north east corner \param{2}, and name \param{3}. \param{1} and +% \param{2} should be given as PGF points. \param{4} is a boolean literal indicating whether the path should be +% stroked. +% \begin{macrocode} +\cs_new:Npn\@@_make_rectangle_node:nnnn #1#2#3#4 + { + \group_begin: + \@@_pgfextractxy:nnn + {\l_@@_tmpa_dim}{\l_@@_tmpb_dim}{#1} + \@@_pgfextractxy:nnn + {\l_@@_tmpc_dim}{\l_@@_tmpd_dim}{#2} + \pgftransformshift{#1} + \pgfset{ + minimum~width=\l_@@_tmpc_dim-\l_@@_tmpa_dim, + minimum~height=\l_@@_tmpd_dim-\l_@@_tmpb_dim, + inner~sep=0, + outer~sep=0, + } + \bool_if:NTF #4 + { \pgfnode{rectangle}{south~west}{}{#3}{\pgfusepath{draw}} } + { \pgfnode{rectangle}{south~west}{}{#3}{\pgfusepath{discard}} } + \group_end: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\l_@@_left_nonclip_x,\l_@@_right_nonclip_x} +% In order to clip `only on one side', define coordinates for specifying the `other side' of the clipping path. +% \begin{macrocode} +\pgfmathsetmacro{\l_@@_left_nonclip_x}{-16000pt} +\pgfmathsetmacro{\l_@@_right_nonclip_x}{16000pt} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_set_style_line_width:nn} +% Set macro \param{1} to be the line width set by the PGF style \param{2}. Note that \cs{begingroup} and \cs{endgroup} +% are used here because of the definition of \cs{pgfmathsmuggle}. +% \begin{macrocode} +\cs_new:Npn\@@_set_style_line_width:nn #1#2 + { + \begingroup + \tikzset{#2} + \pgfmathsetlengthmacro{#1}{\pgflinewidth} + \pgfmathsmuggle #1 + \endgroup + } +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{PGF keys} +% +% All PGF keys for this package are under \key{/timechart/}. +% \begin{macrocode} +\pgfkeys{ + /timechart/.cd, +% \end{macrocode} +% Keys applicable to whole chart. +% \begin{macrocode} + width/.initial=\textwidth, + tolerance/.initial=5pt, + beyond~length/.initial=5pt, + beyond~x~radius/.initial=4pt, + ystep/.initial=-10pt, + minor~tick~interval/.initial=10, + major~tick~interval/.initial=50, +% \end{macrocode} +% Keys applicable to the grid. +% \begin{macrocode} + no~grid/.code = { \bool_set_false:N\l_@@_grid_bool }, + grid~top~ysep/.initial={3pt}, + grid~bottom~ysep/.initial={3pt}, + grid/.style={}, +% \end{macrocode} +% Keys applicable to the axis. +% \begin{macrocode} + no~axis/.code = { \bool_set_false:N\l_@@_axis_bool }, + axis~line/.style={ + line~cap=rect, + }, + axis~ysep/.initial=3pt, + minor~tick/.style={}, + minor~tick~length/.initial=1.5mm, + major~tick/.style={}, + major~tick~length/.initial=3mm, + major~tick~label/.style={ + inner~sep=0, + outer~sep=0, + anchor=mid~west, + rotate=90, + }, +% \end{macrocode} +% Keys applicable to intervals, texts, spaces, and legends. +% \begin{macrocode} + no~autostep/.code = { \bool_set_false:Nz\l_@@_autostep_bool }, + ref/.initial={}, + mark/.initial={}, + marks/.forward~to=/timechart/mark, + circa~uncertainty/.initial=3, + interval~minimum~width/.initial=1pt, + interval~bar~color/.initial=black, + interval~bar~thickness/.initial=8pt, + interval~bar~node~name/.initial = {bar~node}, + interval~mark~color/.initial=gray, + interval~label/.style={}, + interval~label~centered/.style={/timechart/interval~label,text=white}, + interval~label~centered~background/.style={/timechart/interval~label}, + interval~label~baseline/.initial=-3pt, + interval~label~pos/.is~choice, + interval~label~pos/left/.code + = { \int_set:Nn\l_@@_label_pos_int{0} }, + interval~label~pos/center/.code + = { \int_set:Nn\l_@@_label_pos_int{1} }, + interval~label~pos/right/.code + = { \int_set:Nn\l_@@_label_pos_int{2} }, + interval~label~node~name/.initial = {label~node}, + start~range/.is~choice, + start~range/fade/.code + = { \int_set:Nn\l_@@_start_range_type_int{0} }, + start~range/slant/.code + = { \int_set:Nn\l_@@_start_range_type_int{1} }, + finish~range/.is~choice, + finish~range/fade/.code + = { \int_set:Nn\l_@@_finish_range_type_int{0} }, + finish~range/slant/.code + = { \int_set:Nn\l_@@_finish_range_type_int{1} }, + fade~minimum~width/.initial = 0pt, +% \end{macrocode} +% Keys applicable only to texts. +% \begin{macrocode} + text~node~name/.initial = {text~node}, + text/.style={}, + text~baseline/.initial=-3pt, + text~pos/.is~choice, + text~pos/left/.code = { \int_set:Nn\l_@@_text_pos_int{0} }, + text~pos/center/.code = { \int_set:Nn\l_@@_text_pos_int{1} }, + text~pos/right/.code = { \int_set:Nn\l_@@_text_pos_int{2} }, +% \end{macrocode} +% Keys applicable only to legends. +% \begin{macrocode} + legend~item~width/.initial=9mm, + legend~item~range~width/.initial=3mm, +% \end{macrocode} +% Shortcuts for positioning. +% \begin{macrocode} + left/.code = { + \int_set:Nn\l_@@_label_pos_int{0} + \int_set:Nn\l_@@_text_pos_int{0} + }, + center/.code = { + \int_set:Nn\l_@@_label_pos_int{1} + \int_set:Nn\l_@@_text_pos_int{1} + }, + right/.code = { + \int_set:Nn\l_@@_label_pos_int{2} + \int_set:Nn\l_@@_text_pos_int{2} + }, +} +% \end{macrocode} +% +% \begin{macro}{\l_@@_grid_bool} +% Boolean indicating whether the grid will be drawn. This variable is by default true but can be set false via the +% \key{/timechart/no grid} PGF key. +% \begin{macrocode} +\bool_new:N\l_@@_grid_bool +\bool_set_true:N\l_@@_grid_bool +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\l_@@_axis_bool} +% Boolean indicating whether the axis will be drawn. This variable is by default true but can be set false via the +% \key{/timechart/no axis} PGF key. +% \begin{macrocode} +\bool_new:N\l_@@_axis_bool +\bool_set_true:N\l_@@_axis_bool +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\l_@@_autostep_bool} +% Boolean indicating whether to automatically step the \(y\) coordinate after an interval, text, or space. This +% variable is by default true but can be set false via the \key{/timechart/no autostep} PGF key. +% \begin{macrocode} +\bool_new:N\l_@@_autostep_bool +\bool_set_true:N\l_@@_autostep_bool +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\l_@@_label_pos_int} +% An integer to hold the interval label position. This is set via the \key{/timechart/interval label pos} PGF key. +% \begin{macrocode} +\int_new:N \l_@@_label_pos_int +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\l_@@_text_pos_int} +% An integer to hold the text position. This is set via the \key{/timechart/text pos} PGF key. +% \begin{macrocode} +\int_new:N \l_@@_text_pos_int +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\l_@@_start_range_type_int,\l_@@_finish_range_type_int} +% Integers to hold the type of the start/end ranges. These are set via the \key{/timechart/start range} and +% \key{/timechart/finish range} PGF keys. +% \begin{macrocode} +\int_new:N \l_@@_start_range_type_int +\int_new:N \l_@@_finish_range_type_int +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Main environment} +% +% \begin{macro}{timechart} +% The main environment. +% \begin{arguments} +% \item PGF keys to apply. +% \item Start year. +% \item End year. +% \end{arguments} +% \begin{macrocode} +\NewDocumentEnvironment{timechart}{ O{} m m } + { \@@_main_begin:nnn{#1}{#2}{#3} } + { \@@_main_end: } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_main_begin:nnn} +% This command uses values specified by PGF keys to make some necessary calculations to begin the chart. +% \begin{macrocode} +\cs_new:Npn \@@_main_begin:nnn #1#2#3 + { +% \end{macrocode} +% Process the supplied PGF keys and retrieve values that affect the chart as a whole. +% \begin{macrocode} + \pgfkeys{ + /timechart/.cd, + #1, + width/.get=\l_@@_width_pgf, + tolerance/.get=\l_@@_tolerance_pgf, + ystep/.get=\l_@@_ystep_pgf, + grid~top~ysep/.get=\l_@@_grid_top_ysep_pgf, + grid~bottom~ysep/.get=\l_@@_grid_bottom_ysep_pgf, + beyond~length/.get=\l_@@_beyond_length_pgf, + beyond~x~radius/.get=\l_@@_beyond_x_radius_pgf, + minor~tick~interval/.get=\l_@@_minor_tick_interval_year, + major~tick~interval/.get=\l_@@_major_tick_interval_year, + } +% \end{macrocode} +% Start the \TikZ\ picture and set up the necessary layers. +% \begin{macrocode} + \tikzpicture + \pgfdeclarelayer{grid} + \pgfdeclarelayer{labelbg} + \pgfsetlayers{grid,labelbg,main} +% \end{macrocode} +% Store the line width of the grid and axis, treating them as \qty{0}{\point} if they are disabled. +% \begin{macrocode} + \bool_if:NTF\l_@@_grid_bool + { + \@@_set_style_line_width:nn + {\l_@@_grid_line_width} + {/timechart/grid} + } + { \pgfmathsetlengthmacro{\l_@@_grid_line_width}{0} } + \bool_if:NTF\l_@@_axis_bool + { + \@@_set_style_line_width:nn + {\l_@@_axis_line_width} + {/timechart/axis~line} + \@@_set_style_line_width:nn + {\l_@@_major_tick_line_width} + {/timechart/major~tick} + \@@_set_style_line_width:nn + {\l_@@_minor_tick_line_width} + {/timechart/minor~tick} + } + { + \pgfmathsetlengthmacro{\l_@@_axis_line_width}{0} + \pgfmathsetlengthmacro{\l_@@_major_tick_line_width}{0} + \pgfmathsetlengthmacro{\l_@@_minor_tick_line_width}{0} + } +% \end{macrocode} +% Store the start and finish years (ignoring circa, month, day), and then set up the conversion from years to \(x\) +% coordinates. \cs{l_@@_x} is the \(x\)-distance corresponding to one year, and \mcode{yeartox} is the \pkg{pgfmath} +% function that does the conversion. +% \begin{macrocode} + \@@_parse_date:NNn\l_tmpa_bool\l_@@_start_year{#2} + \@@_parse_date:NNn\l_tmpa_bool\l_@@_finish_year{#3} + \pgfmathsetmacro{\l_@@_start_year} + {floor(\l_@@_start_year)} + \pgfmathsetmacro{\l_@@_finish_year} + {floor(\l_@@_finish_year)} + \pgfmathsetmacro{\l_@@_x} + { + ( + \l_@@_width_pgf + - max( + \l_@@_grid_line_width, + \l_@@_axis_line_width, + \l_@@_major_tick_line_width, + \l_@@_minor_tick_line_width + ) + )/(\l_@@_finish_year-\l_@@_start_year) + } + \pgfkeys{ + /pgf/declare~function={ + yeartox(\n)=\l_@@_x*(\n-\l_@@_start_year); + }, + } +% \end{macrocode} +% Calculate the start and finish \(x\) coordinates. +% \begin{macrocode} + \pgfmathsetmacro{\l_@@_start_x} + {yeartox(\l_@@_start_year)} + \pgfmathsetmacro{\l_@@_finish_x} + {yeartox(\l_@@_finish_year)} + \pgfmathsetmacro{\l_@@_start_tolerance_x}{ + \l_@@_start_x-(\l_@@_tolerance_pgf) + } + \pgfmathsetmacro{\l_@@_finish_tolerance_x}{ + \l_@@_finish_x+(\l_@@_tolerance_pgf) + } + \pgfmathsetmacro{\l_@@_start_beyond_x}{ + \l_@@_start_x-(\l_@@_beyond_length_pgf) + } + \pgfmathsetmacro{\l_@@_finish_beyond_x}{ + \l_@@_finish_x+(\l_@@_beyond_length_pgf) + } +% \end{macrocode} +% Set up tracking of current \(y\) coordinate. +% \begin{macrocode} + \pgfmathsetmacro{\l_@@_current_y}{0} + \pgfmathsetmacro{\l_@@_saved_y}{0} + \pgfmathsetmacro{\l_@@_auto_reset_minimum_y}{-16000pt} + \pgfmathsetmacro{\l_@@_auto_reset_maximum_y}{16000pt} +% \end{macrocode} +% Calculate some years used in loops. +% \begin{macrocode} + \pgfmathsetmacro{\l_@@_start_plus_year}{ + \l_@@_start_year+\l_@@_minor_tick_interval_year + } + \pgfmathsetmacro{\l_@@_start_plusplus_year}{ + \l_@@_start_year+(2*\l_@@_minor_tick_interval_year) + } + \pgfmathsetmacro{\l_@@_end_minus_year}{ + \l_@@_finish_year-\l_@@_minor_tick_interval_year + } +% \end{macrocode} +% Begin a group and make available the user commands \cs{timechart}\ldots. (The group will be ended by +% \cs{@@_main_end_user:}.) +% \begin{macrocode} + \group_begin: + \cs_set_eq:NN\timechartinterval\@@_interval_user:Ommm + \cs_set_eq:NN\timecharttext\@@_text_user:Omm + \cs_set_eq:NN\timechartspace\@@_space_user:O + \cs_set_eq:NN\timechartsety\@@_set_y_user:m + \cs_set_eq:NN\timechartsavey\@@_save_y_user: + \cs_set_eq:NN\timechartresety\@@_reset_y_user: + \cs_set_eq:NN\timechartsetyminimumautoreset + \@@_set_y_minimum_auto_reset_user:m + \cs_set_eq:NN\timechartsetymaximumautoreset + \@@_set_y_maximum_auto_reset_user:m + \cs_set_eq:NN\timechartstepy\@@_step_y_user:O + \cs_set_eq:NN\timechartfinish\@@_main_end_user: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_main_end:} +% Make sure the chart is complete and end the \TikZ\ picture. \cs{@@_main_end_user:} ends the group begun by +% \cs{@@_main_begin:nnn}, so whether the user has \emph{not} called it (as \cs{timechartfinish}) is equivalent to it +% being equal to \cs{timechartfinish}. +% \begin{macrocode} +\cs_new:Npn\@@_main_end: + { + \cs_if_eq:NNT\timechartfinish\@@_main_end_user: + { \@@_main_end_user: } + \endtikzpicture + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_main_end_user:} +% End the group begun by \cs{@@_main_begin:nnn}, draw the axis and grid, and set the bounding box. This macro is made +% available as \cs{timechartfinish} inside the \env{timechart} environment. +% \begin{macrocode} +\cs_new:Npn\@@_main_end_user: + { +% \end{macrocode} +% The aim here is to set the bounding box (1) to fit horizontally the axis \emph{not} including labels and the grid +% and (2) to fit vertically the axis including labels and the grid . All the `horizonatal' data is already known, and +% the `vertical' data is determined by the \emph{current} bounding box. So extract the `vertical' data and then reset +% the bounding box. +% \begin{macrocode} + \pgfextracty{\l_@@_tmpa_dim} + { \pgfpointanchor{current~bounding~box}{south} } + \pgfextracty{\l_@@_tmpb_dim} + { \pgfpointanchor{current~bounding~box}{north} } + \pgfresetboundingbox +% \end{macrocode} +% If the timechart is empty, then the extracted \(y\) coordinates of `north' and `south' anchors of the bounding box +% will be \qty{-16000}{\point} and \qty{16000}{\point} respectively. Test for this and treat them as both having \(y\) +% coordinate \qty{0}{\point} in this case. +% \begin{macrocode} + \dim_compare:nNnTF{\l_@@_tmpa_dim}>{\l_@@_tmpb_dim} + { + \pgfmathsetmacro{\l_@@_content_bottom_y}{0pt} + \pgfmathsetmacro{\l_@@_content_top_y}{0pt} + } + { + \pgfmathsetmacro{\l_@@_content_bottom_y} + {\l_@@_tmpa_dim} + \pgfmathsetmacro{\l_@@_content_top_y} + {\l_@@_tmpb_dim} + } +% \end{macrocode} +% Now draw the grid and axis if necessary and set the bounding box if not. +% \begin{macrocode} + \bool_if:NTF{\l_@@_grid_bool} + { + \@@_grid_draw: + \pgfmathsetmacro{\l_@@_axis_y} + { + \l_@@_content_top_y + + \l_@@_grid_top_ysep_pgf + + \pgfkeysvalueof{/timechart/axis~ysep} + } + } + { + \@@_nogrid_bounding_box_set: + \pgfmathsetmacro{\l_@@_axis_y} + { + \l_@@_content_top_y + +\pgfkeysvalueof{/timechart/axis~ysep} + } + } + \bool_if:NT{\l_@@_axis_bool} + { \@@_axis_draw: } +% \end{macrocode} +% Finally, end the group begun by \cs{@@_main_begin:nnn}. +% \begin{macrocode} + \group_end: + } +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Grid drawing} +% +% \begin{macro}{\@@_grid_draw:} +% Draw the grid of the chart, assuming that the \(y\) coordinates of the top and bottom of the content have +% been calculated and stored in \cs{l_@@_content_top_y} and \cs{l_@@_content_bottom_y}. +% \begin{macrocode} +\cs_new:Npn\@@_grid_draw: + { + \pgfmathsetmacro{\l_@@_grid_bottom_y}{ + \l_@@_content_bottom_y-\l_@@_grid_bottom_ysep_pgf + } + \pgfmathsetmacro{\l_@@_grid_top_y}{ + \l_@@_content_top_y+\l_@@_grid_top_ysep_pgf + } + \pgfonlayer{ grid } + \scope[/timechart/grid] + \foreach \year in { + \l_@@_start_plus_year, + \l_@@_start_plusplus_year, + ..., + \l_@@_end_minus_year + } { + \group_begin: +% \end{macrocode} +% Only draw gridlines at major ticks. +% \begin{macrocode} + \@@_pgfmathsetbool:nn{\l_@@_tmpa_bool} + { Mod(\year,\l_@@_major_tick_interval_year)==0 } + \bool_if:NT\l_@@_tmpa_bool + { + \pgftransformshift{ \pgfpoint{yeartox(\year)}{0} } + \pgfpathmoveto{ \pgfpoint{0}{\l_@@_grid_top_y} } + \pgfpathlineto{ \pgfpoint{0}{\l_@@_grid_bottom_y} } + \pgfusepath{ draw } + } + \group_end: + } +% \end{macrocode} +% Define and draw the grid node. +% \begin{macrocode} + \@@_make_rectangle_node:nnnn + { \pgfpoint{\l_@@_start_x}{\l_@@_grid_bottom_y} } + { \pgfpoint{\l_@@_finish_x}{\l_@@_grid_top_y} } + { grid } + { \c_true_bool } + \endscope + \endpgfonlayer + } +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Axis drawing} +% +% \begin{macro}{\@@_axis_draw:} +% Draw the axis, with large/small ticks and labels on appropriate years, assuming that the \(y\) coordinate has been +% calculated and stored in \cs{l_@@_axis_y}. +% \begin{macrocode} +\cs_new:Npn\@@_axis_draw: + { + \group_begin: + \pgfkeys{ + /timechart/minor~tick~length/.get=\@@_minor_tick_length_pgf, + /timechart/major~tick~length/.get=\@@_major_tick_length_pgf, + } +% \end{macrocode} +% Shift to the \(y\) coordinate of the axis line. +% \begin{macrocode} + \pgftransformshift{ \pgfpoint{0}{\l_@@_axis_y} } +% \end{macrocode} +% Work out the first and last years which will have a major tick (since these are marked with the era). +% \begin{macrocode} + \pgfmathsetmacro{\@@_start_major_tick_year} + { + \l_@@_start_year + -Mod( + \l_@@_start_year, + \l_@@_major_tick_interval_year + ) + } + \pgfmathsetmacro{\@@_start_major_tick_year} + { + ifthenelse( + \@@_start_major_tick_year<\l_@@_start_year, + \@@_start_major_tick_year + +\l_@@_major_tick_interval_year, + \@@_start_major_tick_year + ) + } + \pgfmathsetmacro{\@@_finish_major_tick_year} + { + \l_@@_finish_year + -Mod( + \l_@@_finish_year, + \l_@@_major_tick_interval_year + ) + } +% \end{macrocode} +% Loop over years and draw the appropriate ticks. +% \begin{macrocode} + \foreach \year in { + \l_@@_start_year, + \l_@@_start_plus_year, + ..., + \l_@@_finish_year + } { + \pgfmathsetmacro{\x}{yeartox(\year)} + \@@_pgfmathsetbool:nn + {\l_@@_tmpa_bool} + { Mod(\year,\l_@@_major_tick_interval_year)==0 } + \bool_if:NTF\l_@@_tmpa_bool + { \@@_axis_draw_labelled_major_tick:NN\x\year } + { \@@_axis_draw_minor_tick:N\x } + } + \@@_draw_axis_line +% \end{macrocode} +% Define the axis node. +% \begin{macrocode} + \pgfextracty{\l_@@_tmpa_dim} + { \pgfpointanchor{current~bounding~box}{north} } + \@@_make_rectangle_node:nnnn + { \pgfpoint{\l_@@_start_x}{0} } + { \pgfpoint{\l_@@_finish_x}{\l_@@_tmpa_dim} } + { axis } + { \c_false_bool } + \group_end: + } +% \end{macrocode} +% \end{macro} +% +% All the remaining axis-related macros (which begin \cs{@@_axis_draw_}) assume that a transformation has been applied +% so that the axis line is at \(y=0\). +% +% \begin{macro}{\@@_axis_draw_minor_tick:N} +% Draw an unlabelled tick at \(x\) coordinate \param{1}. +% \begin{macrocode} +\cs_new:Npn\@@_axis_draw_minor_tick:N #1 + { + \scope[/timechart/minor~tick] + \pgfpathmoveto{ \pgfpoint{#1}{0} } + \pgfpathlineto{ \pgfpoint{#1}{\@@_minor_tick_length_pgf} } + \pgfusepath{draw} + \endscope + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_axis_draw_labelled_major_tick:NN} +% Draw a labelled major tick at \(x\) coordinate \param{1}, with label for year \param{2}, using the special epoch +% marker if the year is 0, and showing the era if and only if the year is for the first or last major tick. This macro +% assumes that \cs{@@_start_major_tick_year} and \cs{@@_finish_major_tick_year} have been calculated. +% \begin{macrocode} +\cs_new:Npn\@@_axis_draw_labelled_major_tick:NN #1#2 + { + \@@_pgfmathsetbool:nn{\l_@@_tmpa_bool}{#2==0} + \bool_if:NTF \l_@@_tmpa_bool + { \@@_axis_draw_zero_tick:N #1 } + { + \@@_pgfmathsetbool:nn{\l_@@_tmpb_bool} + { #2==\@@_start_major_tick_year } + \@@_pgfmathsetbool:nn{\l_@@_tmpc_bool} + { #2==\@@_finish_major_tick_year } + \bool_set:Nn\l_@@_tmpd_bool + { \l_@@_tmpb_bool || \l_@@_tmpc_bool } + \@@_axis_draw_major_tick:N #1 + \@@_axis_draw_year_label:nnnnnn + { #1 } + { #2 } + { \l_@@_tmpd_bool } + { mid~west } + { 0 } + { \@@_major_tick_length_pgf+1mm } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_axis_draw_major_tick:N} +% Draw a major tick at \(x\) coordinate \param{1}. +% \begin{macrocode} +\cs_new:Npn\@@_axis_draw_major_tick:N #1 + { + \scope[/timechart/major~tick] + \pgfpathmoveto{ \pgfpoint{#1}{0} } + \pgfpathlineto{ \pgfpoint{#1}{\@@_major_tick_length_pgf} } + \pgfusepath{draw} + \endscope + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_axis_draw_zero_tick:N} +% Draw a special (labelled) tick for year zero at \(x=\param{1}\). +% \begin{macrocode} +\cs_new:Npn\@@_axis_draw_zero_tick:N #1 + { + \group_begin: + \pgftransformshift{ \pgfpoint{#1}{0} } +% \end{macrocode} +% The mark is a cross made up of four arcs and the miter joins between them. The parameter \cs{r} is the arc radius. +% The parameter \cs{a} is how many degrees should be trimmed from the start/end of a quarter-circle to form each arc. +% Thus the length of the miter is dependent on \cs{a}. +% \begin{macrocode} + \pgfmathsetmacro{\a}{5} + \pgfmathsetlengthmacro{\r}{1mm} + \pgfmathsetlengthmacro{\t}{\r*(cos(\a)-cos(90-\a))/(1-cos(90-\a))} +% \end{macrocode} +% The drawing process is: move to the start of the tick, draw the tick, draw the four arcs, then draw a small part of +% the tick again. The last step is not mathematically necessary for a smooth join, but ensures that the join +% \emph{appears} smooth. +% \begin{macrocode} + \scope[/timechart/major~tick,line~join=miter] + \pgfpathmoveto{\pgfpointorigin} + \pgfpathlineto{\pgfpoint{0}{\@@_major_tick_length_pgf}} + \pgfpatharc{0}{90-\a}{\t~and~\r} + \pgfpatharc{270+\a}{360-\a}{\r} + \pgfpatharc{180+\a}{270-\a}{\r} + \pgfpatharc{90+\a}{180-\a}{\t~and~\r} + \pgfpathlineto{\pgfpoint{0}{\@@_major_tick_length_pgf-1pt}} + \pgfusepath{draw} + \endscope +% \end{macrocode} +% There is no year 0, so label the zero mark with the 1 before and 1 after the epoch. +% \begin{macrocode} + \@@_axis_draw_year_label:nnnnnn + { 0 } + { -1 } + { \c_true_bool } + { base~west } + { -.5mm } + { 5.5mm } + \@@_axis_draw_year_label:nnnnnn + { 0 } + { 1 } + { \c_true_bool } + { north~west } + { .5mm } + { 5.5mm } + \group_end: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_axis_draw_line} +% Draw the axis line itself. +% \begin{macrocode} +\cs_new:Npn\@@_draw_axis_line + { + \scope[/timechart/axis~line] + \pgfpathmoveto{ \pgfpoint{\l_@@_start_x}{0} } + \pgfpathlineto{ \pgfpoint{\l_@@_finish_x}{0} } + \pgfusepath{draw} + \endscope + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_axis_draw_year_label:nnnnnn} +% Draw a year label. +% \begin{arguments} +% \item \(x\) coordinate. +% \item Year for label. +% \item Boolean literal indicating whether the era should be shown. +% \item Anchor for node. +% \item \(x\) offset (dimension). +% \item \(y\) offset (dimension). +% \end{arguments} +% \begin{macrocode} +\cs_new:Npn\@@_axis_draw_year_label:nnnnnn #1#2#3#4#5#6 + { + \group_begin: + \pgftransformshift{ \pgfpoint{#1+#5}{#6} } + \pgfmathtruncatemacro{\absyear}{ abs(#2) } + \scope[/timechart/major~tick~label] + \bool_if:NTF #3 + { + \@@_pgfmathsetbool:nn{\l_@@_tmpa_bool}{#2<0} + \bool_if:NTF\l_@@_tmpa_bool + { \cs_set_eq:NN\@@_make_year:n\timechartmakebeforeyear } + { \cs_set_eq:NN\@@_make_year:n\timechartmakeafteryear } + \@@_hsmash_pgfnode:nnnnn + {rectangle} + {#4} + {\@@_make_year:n{\absyear}} + {} + {} + } + { \pgfnode{rectangle}{#4}{\absyear}{}{} } + \endscope + \group_end: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\timechartmakebeforeyear,\timechartmakeafteryear} +% User-redefineable macros to format a year as before or after the epoch. +% \begin{macrocode} +\cs_new:Npn\timechartmakebeforeyear #1 + { + #1\nobreakspace\textsc{bce} + } +\cs_new:Npn\timechartmakeafteryear #1 + { + #1\nobreakspace\textsc{ce} + } +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Bounding box} +% +% \begin{macro}{\@@_nogrid_bounding_box_set:} +% Set the bounding box when no grid is being drawn. +% \begin{macrocode} +\cs_new:Npn\@@_nogrid_bounding_box_set: + { + \pgfpathmoveto + { \pgfpoint{\l_@@_start_x}{\l_@@_content_bottom_y} } + \pgfpathmoveto + { \pgfpoint{\l_@@_finish_x}{\l_@@_content_top_y} } + \pgfusepath{discard} + } +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Positioning} +% +% \begin{macro}{\@@_set_y_user:m} +% Set current \(y\) coordinate to \param{1}. This macro will be made available as \cs{timechartsety} in the +% \env{timechart} environment. +% \begin{macrocode} +\cs_new:Npn\@@_set_y_user:m #1 + { + \pgfmathsetmacro{\l_@@_current_y}{#1} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_save_y_user:} +% Save the current \(y\) coordinate. This macro will be made available as \cs{timechartsavey} in the +% \env{timechart} environment. +% \begin{macrocode} +\cs_new:Npn\@@_save_y_user: + { + \pgfmathsetmacro{\l_@@_saved_y}{\l_@@_current_y} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_reset_y_user:} +% Set the current \(y\) coordinate to the last saved coordinate. This macro will be made available as +% \cs{timechartsresety} in the \env{timechart} environment. +% \begin{macrocode} +\cs_new:Npn\@@_reset_y_user: + { + \pgfmathsetmacro{\l_@@_current_y}{\l_@@_saved_y} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_set_y_minimum_auto_reset_user:m} +% Set a \(y\) coordinate below which \cs{@@_step_y_user:} will automatically reset the currrent \(y\) coordinate to +% the last saved \(y\) coordinate. This macro will be made available as \cs{timechartssetyminimumautoreset} in the +% \env{timechart} environment. +% \begin{macrocode} +\cs_new:Npn\@@_set_y_minimum_auto_reset_user:m #1 + { + \pgfmathsetmacro{\l_@@_auto_reset_minimum_y}{#1} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_set_y_maximum_auto_reset_user:m} +% Set a \(y\) coordinate above which \cs{@@_step_y_user:} will automatically reset the currrent \(y\) coordinate to +% the last saved \(y\) coordinate. This macro will be made available as \cs{timechartssetymaximumautoreset} in the +% \env{timechart} environment. +% \begin{macrocode} +\cs_new:Npn\@@_set_y_maximum_auto_reset_user:m #1 + { + \pgfmathsetmacro{\l_@@_auto_reset_maximum_y}{#1} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_step_y_user:O} +% Increment the current \(y\) coordinate by \param{1} times the length specified in \key{/timechart/ystep}. This +% macro will be made available as \cs{timechartstepy} in the \env{timechart} environment. +% \begin{macrocode} +\NewDocumentCommand{\@@_step_y_user:O}{ O{1} } + { + \pgfmathsetmacro{\l_@@_current_y} + {\l_@@_current_y+#1*\l_@@_ystep_pgf} + \@@_pgfmathsetbool:nn{\l_@@_tmpa_bool} + { + or( + \l_@@_current_y<\l_@@_auto_reset_minimum_y, + \l_@@_current_y>\l_@@_auto_reset_maximum_y + ) + } + \bool_if:nT{\l_@@_tmpa_bool} + { \pgfmathsetmacro{\l_@@_current_y}{\l_@@_saved_y)} } + } +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Bounds checking} +% +% \begin{macro}{\@@_if_x_in_bounds:nT}% +% Check if \(x\) coordinate \param{1} is (strictly) within the bounds of the chart; if so, execute \param{2}. +% \begin{macrocode} +\cs_new:Npn\@@_if_x_in_bounds:nT #1#2 + { + \@@_pgfmathsetbool:nn{\l_@@_tmpa_bool}{ + and( + #1>=\l_@@_start_x, + #1<=\l_@@_finish_x + ) + } + \bool_if:NT\l_@@_tmpa_bool + {#2} + } + +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_if_x_in_tolerance_bounds_x:nT}% +% Check if \(x\) coordinate \param{1} is within the specified tolerance of the bounds of the chart; if so, execute +% \param{2}. +% \begin{macrocode} +\cs_new:Npn\@@_if_x_in_tolerance_bounds:nT #1#2 + { + \@@_pgfmathsetbool:nn{\l_@@_tmpa_bool}{ + and( + #1>=\l_@@_start_tolerance_x, + #1<=\l_@@_finish_tolerance_x + ) + } + \bool_if:NT\l_@@_tmpa_bool{#2} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_if_x_range_intersect_tolerance_bounds_x:nnT}% +% Check if the range between \(x\) coordinates \param{1} and \param{2} intersects the range of the bounds of the chart +% plus the specified tolerance; if so, execute \param{3}. +% \begin{macrocode} +\cs_new:Npn\@@_if_x_range_intersect_tolerance_bounds:nnT #1#2#3 + { + \@@_pgfmathsetbool:nn{\l_@@_tmpa_bool}{ + or( + or( + and( + #2>=\l_@@_start_tolerance_x, + #2<=\l_@@_finish_tolerance_x + ), + and( + #1>=\l_@@_start_tolerance_x, + #1<=\l_@@_finish_tolerance_x + ) + ), + and( + #1<\l_@@_start_tolerance_x, + #2>\l_@@_finish_tolerance_x + ) + ) + } + \bool_if:NT\l_@@_tmpa_bool + {#3} + } +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Date and date range parsing} +% +% \begin{macro}{\@@_parse_date_or_daterange:NNNNNn} +% Parse the text in \param{6}, which should represent a date or date range, into parameters \param{1}--\param{5}. +% \begin{arguments} +% \item range indicator boolean variable. +% \item minimum circa indicator boolean variable. +% \item minimum variable. +% \item maximum circa indicator boolean variable. +% \item maximum variable. +% \item text to parse. +% \end{arguments} +% \begin{macrocode} +\cs_new:Npn\@@_parse_date_or_daterange:NNNNNn #1#2#3#4#5#6 +{ + \bool_set:Nn #1 {\@@_is_nondaterange_p:w #6/\q_stop} + \bool_set_inverse:N #1 + \bool_if:nTF #1 + { \@@_parse_range:w #2#3#4#5\q_mark #6\q_stop } + { + \@@_parse_date:NNn #2#3{#6} + \bool_set_eq:NN #4#2 + \pgfmathsetmacro{#5}{#3} + } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_is_nondaterange_p:w} +% To be called in the form \cs{@@_is_nondaterange_p:w}\meta{text}\texttt{/}\cs{q_stop}. Return boolean true if and +% only if \meta{text} (known to be either a date or date range) contains a range marker. +% \begin{macrocode} +\cs_new:Npn\@@_is_nondaterange_p:w #1/#2\q_stop +{ + \tl_if_empty_p:n{#2} +} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\@@_parse_range:w} +% To be called in the form +% \cs{@@_parse_range:w}\meta{cmin}\meta{min}\meta{cmax}\meta{max}\cs{q_mark}\meta{text}\cs{q_stop}. Parse \meta{text} +% (known to represent a date range) into minimum circa indicator boolean variable \meta{cmin}, minimum variable +% \meta{min}, maximum circa indicator boolean variable \meta{cmax}, maximum variable \meta{max}. +% \begin{macrocode} +\cs_new:Npn\@@_parse_range:w #1#2#3#4\q_mark #5/#6\q_stop +{ + \_@@_parse_date:NNn #1#2{#5} + \_@@_parse_date:NNn #3#4{#6} +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_parse_date:NNn} +% Parse text (known to represent a date) into the supplied variables. Parameters \param{1} and \param{2} are the +% variables for (respectively) circa indicator boolean and date, and \param{3} is the text to be parsed: +% \begin{arguments} +% \item circa indicator boolean variable. +% \item date variable. +% \item text to parse. +% \end{arguments} +% \begin{macrocode} +\cs_new:Npn\@@_parse_date:NNn #1#2#3 +{ + \bool_set:Nn #1 { \@@_is_circa_p:w #3c\q_stop } + \bool_if:NTF #1 + { \@@_parse_circa_date:w #2\q_mark #3\q_stop } + { \@@_parse_noncirca_date:Nn #2{#3} } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_is_circa_p:w} +% To be called in the form \cs{@@_is_circa_p:w}\meta{text}\texttt{c}\cs{q_stop}. Return boolean true if and only if +% \meta{text} (known to be either a date or a date with a circa indicator) has a circa indicator. +% \begin{macrocode} +\cs_new:Npn\@@_is_circa_p:w #1c#2\q_stop +{ + \tl_if_empty_p:n{#1} +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_parse_circa_date:w} +% To be called in the form \cs{@@_parse_circa_date:w}\meta{var}\cs{q_mark}\meta{text}\cs{q_stop}. Parse \meta{text} +% (known to represent a circa date) into the supplied variable. \param{1} is the variable for the date and \param{2} +% is the text to be parsed. +% \begin{macrocode} +\cs_new:Npn\@@_parse_circa_date:w #1\q_mark c#2\q_stop +{ + \@@_parse_noncirca_date:Nn #1{#2} +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_parse_noncirca_date:w} +% To be called in the form \cs{@@_parse_noncirca_date:w}\meta{var}\cs{q_mark}\meta{text}\break\cs{q_stop}. Parse +% \meta{text} (known to represent a non-circa date) into the supplied variable. \param{1} is the variable for the date +% and \param{2} is the text to be parsed. +% \begin{macrocode} +\cs_new:Npn\@@_parse_noncirca_date:Nn #1#2 +{ + \bool_if:nTF { \@@_is_before_p:w #2-\q_stop } + { \@@_parse_before_date:w #1\q_mark #2\q_stop } + { \@@_parse_signed_date:w #1\q_mark #2-0-0\q_stop } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_is_before_p:w} +% To be called in the form \cs{@@_is_before_p:w}\meta{text}\texttt{-}\cs{q_stop}. Return boolean true if and only if +% \meta{text} (known to be a date without a circa indicator) begins with a \texttt{-}. +% \begin{macrocode} +\cs_new:Npn\@@_is_before_p:w #1-#2\q_stop +{ + \tl_if_empty_p:n{#1} +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_parse_before_date:w} +% To be called in the form \cs{@@_parse_before_date:w}\meta{var}\cs{q_mark}\meta{text}\cs{q_stop}. Parse \meta{text} +% (known to represent a date with a leading \texttt{-}) into the supplied variable. \param{1} is the variable for the +% date and \param{2} is the text to be parsed. +% \begin{macrocode} +\cs_new:Npn\@@_parse_before_date:w #1\q_mark-#2\q_stop +{ + \@@_parse_signed_date:w #1-\q_mark #2-0-0\q_stop +} +% \end{macrocode} +% \end{macro} +% +% Now comes that actual parsing of an ISO-format date \texttt{YYYY-MM-DD}. The following macros serve as lookup tables +% for the number of days in the \(n\)-th month and the number of days in the year up to the start of the \(n\)-th month. +% \begin{macrocode} +\cs_new:cpn{c_@@_year_days_pgf}{365} +\cs_new:cpn{c_@@_month_days_1_pgf}{31} +\cs_new:cpn{c_@@_month_days_2_pgf}{28} +\cs_new:cpn{c_@@_month_days_3_pgf}{31} +\cs_new:cpn{c_@@_month_days_4_pgf}{30} +\cs_new:cpn{c_@@_month_days_5_pgf}{31} +\cs_new:cpn{c_@@_month_days_6_pgf}{30} +\cs_new:cpn{c_@@_month_days_7_pgf}{31} +\cs_new:cpn{c_@@_month_days_8_pgf}{31} +\cs_new:cpn{c_@@_month_days_9_pgf}{30} +\cs_new:cpn{c_@@_month_days_10_pgf}{31} +\cs_new:cpn{c_@@_month_days_11_pgf}{30} +\cs_new:cpn{c_@@_month_days_12_pgf}{31} +\cs_new:cpn{c_@@_cumulative_days_1_pgf}{0} +\cs_new:cpn{c_@@_cumulative_days_2_pgf}{31} +\cs_new:cpn{c_@@_cumulative_days_3_pgf}{59} +\cs_new:cpn{c_@@_cumulative_days_4_pgf}{90} +\cs_new:cpn{c_@@_cumulative_days_5_pgf}{120} +\cs_new:cpn{c_@@_cumulative_days_6_pgf}{151} +\cs_new:cpn{c_@@_cumulative_days_7_pgf}{181} +\cs_new:cpn{c_@@_cumulative_days_8_pgf}{212} +\cs_new:cpn{c_@@_cumulative_days_9_pgf}{243} +\cs_new:cpn{c_@@_cumulative_days_10_pgf}{273} +\cs_new:cpn{c_@@_cumulative_days_11_pgf}{304} +\cs_new:cpn{c_@@_cumulative_days_12_pgf}{334} +% \end{macrocode} +% +% \begin{macro}{\@@_parse_signed_date:w} +% To be called in the form \cs{@@_parse_positive_date:w}\meta{var}\meta{sign}\cs{q_mark}\break +% \meta{text}\texttt{-0-0}\cs{q_stop}. Parse \meta{text} (known to represent a non-circa date) into the supplied +% variable. \param{1} is the variable for the date and \param{2} is possibly \texttt{-}. +% +% There is a trick in the parsing: +% \begin{enumerate} +% \item If \meta{text} has the form \meta{year}\texttt{-}\meta{month}\texttt{-}\meta{day}, then parameters +% \param{3}, \param{4}, and \param{5} will be, respectively, \meta{year}, \meta{month}, and +% \meta{day}\texttt{-0-0}. Thus \param{5} will be evaulated by \pkg{pgfmath} to \meta{day}. +% \item If \meta{text} has the form \meta{year}\texttt{-}\meta{month}, then parameters \param{3}, \param{4}, and +% \param{5} will be, respectively, \meta{year}, \meta{month}, and \texttt{0-0}. Thus \param{5} will be +% evaulated by \pkg{pgfmath} to \(0\). +% \item If \meta{text} is simply \meta{year}, then parameters \param{3}, \param{4}, and \param{5} will be, +% respectively, \meta{year}, \texttt{0}, and \texttt{0}. +% \end{enumerate} +% \begin{macrocode} +\cs_new:Npn\@@_parse_signed_date:w #1#2\q_mark #3-#4-#5\q_stop +{ + \pgfmathtruncatemacro{\@@_parsed_year_pgf}{#2#3} + \pgfmathtruncatemacro{\@@_parsed_month_pgf}{#4} + \pgfmathtruncatemacro{\@@_parsed_day_pgf}{#5} + \@@_pgfmathsetbool:nn{\l_tmpa_bool}{ + or( + \@@_parsed_month_pgf < 1, + \@@_parsed_month_pgf > 12, + ) + } + \bool_if:NTF\l_tmpa_bool + { +% \end{macrocode} +% \textit{Case: no valid month is given.} Use only the year. +% \begin{macrocode} + \pgfmathsetmacro{#1}{#2#3} + } + { +% \end{macrocode} +% \textit{Case: a valid month is given.} Get the number of days in the year, in the month, and in the year up to the +% month. Then check if the year is a leap year and, if so, make the appropriate adjustments. +% \begin{macrocode} + \cs_set_eq:NN\l_@@_year_days_pgf\c_@@_year_days_pgf + \cs_set_eq:Nc\l_@@_month_days_pgf + { c_@@_month_days_\@@_parsed_month_pgf _pgf } + \cs_set_eq:Nc\l_@@_cumulative_days_pgf + { c_@@_cumulative_days_\@@_parsed_month_pgf _pgf } + \@@_pgfmathsetbool:nn{\l_tmpa_bool}{ + or( + Mod(\@@_parsed_year_pgf,400) == 0, + and( + Mod(\@@_parsed_year_pgf,4) == 0, + Mod(\@@_parsed_year_pgf,100) != 0 + ) + ) + } + \bool_if:NT\l_tmpa_bool + { + \pgfmathsetmacro{\l_@@_year_days_pgf} + { \l_@@_year_days_pgf+1 } + \@@_pgfmathsetbool:nn{\l_tmpb_bool} + { \@@_parsed_month == 1 } + \bool_if:NF\l_tmpb_bool + { + \@@_pgfmathsetbool:nn{\l_tmpb_bool} + { \@@_parsed_month == 2 } + \bool_if:NF\l_tmpb_bool + { + \pgfmathsetmacro{\l_@@_month_days_pgf} + { \l_@@_month_days_pgf + 1 } + } + { + \pgfmathsetmacro{\l_@@_cumulative_days_pgf} + { \l_@@_cumulative_days_pgf + 1 } + } + } + } + \@@_pgfmathsetbool:nn{\l_tmpa_bool}{ + or( + \@@_parsed_day_pgf < 1, + \@@_parsed_day_pgf > \l_@@_month_days_pgf, + ) + } + \bool_if:NTF\l_tmpa_bool + { +% \end{macrocode} +% \textit{Sub-case: no valid day is given.} Use only the year and month. +% \begin{macrocode} + \pgfmathsetmacro{#1} + { + #2#3 + + \l_@@_cumulative_days_pgf/\l_@@_year_days_pgf + } + } + { +% \end{macrocode} +% \textit{Sub-case: a valid day is given.} Use the year, month, and day. +% \begin{macrocode} + \pgfmathsetmacro{#1} + { + #2#3 + + ( + \l_@@_cumulative_days_pgf + + \@@_parsed_day_pgf + )/\l_@@_year_days_pgf + } + } + } +} +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Interval drawing} +% +% \subsubsection{Preliminaries} +% +% \begin{macro}{ +% \l_@@_start_is_range_bool, +% \l_@@_startmin_circa_bool, +% \l_@@_startmax_circa_bool, +% \l_@@_finish_is_range_bool, +% \l_@@_finishmin_circa_bool, +% \l_@@_finishmax_circa_bool, +% } +% These boolean variables will be used to hold parsed data for the start and finish of an interval: whether it is a +% range, whether the beginning of that range is qualified by `circa', and whether the end of that range is qualified +% by `circa'. +% \begin{macrocode} +\bool_new:N\l_@@_start_is_range_bool +\bool_new:N\l_@@_startmin_circa_bool +\bool_new:N\l_@@_startmax_circa_bool +\bool_new:N\l_@@_finish_is_range_bool +\bool_new:N\l_@@_finishmin_circa_bool +\bool_new:N\l_@@_finishmax_circa_bool +% \end{macrocode} +% \end{macro} +% +% +% +% \subsubsection{Error message definition} +% +% \begin{macrocode} +\msg_new:nnn{timechart}{interval_dates_invalid} + { Invalid~interval~dates:~#1~to~#2 } +% \end{macrocode} +% +% +% +% \subsubsection{Main macros} +% +% \begin{macro}{\@@_interval_user:Ommm} +% Draw an interval. This macro will be made available as \cs{timechartinterval} inside the \env{timechart} +% environment. +% \begin{arguments} +% \item PGF keys under \key{/timechart/} to apply. +% \item Start year. +% \item Finish year. +% \item Label. +% \end{arguments} +% \begin{macrocode} +\NewDocumentCommand{\@@_interval_user:Ommm}{ O{} m m m } + { + \group_begin: +% \end{macrocode} +% Parse the start and finish dates or date ranges. +% \begin{macrocode} + \@@_parse_date_or_daterange:NNNNNn + \l_@@_start_is_range_bool + \l_@@_startmin_circa_bool\l_@@_startmin_year + \l_@@_startmax_circa_bool\l_@@_startmax_year + {#2} + \@@_parse_date_or_daterange:NNNNNn + \l_@@_finish_is_range_bool + \l_@@_finishmin_circa_bool\l_@@_finishmin_year + \l_@@_finishmax_circa_bool\l_@@_finishmax_year + {#3} +% \end{macrocode} +% Check the results of parsing and only proceed if they are valid. +% \begin{macrocode} + \@@_pgfmathsetbool:nn{\l_tmpa_bool}{ + and( + \l_@@_startmin_year <= \l_@@_startmax_year, + and( + \l_@@_startmax_year <= \l_@@_finishmin_year, + \l_@@_finishmin_year <= \l_@@_finishmax_year + ) + ) + } + \bool_if:NTF\l_tmpa_bool + { + \@@_interval_checked:nn{#1}{#4} + \group_end: + \bool_if:NT\l_@@_autostep_bool + { \@@_step_y_user:O } + } + { + \msg_error:nnnn{timechart}{interval_dates_invalid}{#2}{#3} + \group_end: + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_interval_checked:nn} +% Draw an interval using the parsed and checked years or year ranges. +% \begin{arguments} +% \item PGF keys under \key{/timechart/} to apply. +% \item Label. +% \end{arguments} +% \begin{macrocode} +\cs_new:Npn \@@_interval_checked:nn #1#2 + { +% \end{macrocode} +% Process keys supplied locally and retrieve the only value needed at this stage. +% \begin{macrocode} + \pgfqkeys{/timechart}{ + #1, + circa~uncertainty/.get=\l_@@_circa_uncertainty_year + } +% \end{macrocode} +% Do the minimum amount of calculation necessary to check whether any part of interval is visible. +% \begin{macrocode} + \bool_if:NTF\l_@@_startmin_circa_bool + { + \pgfmathsetmacro{\l_@@_start_extreme_x} + { yeartox(\l_@@_startmin_year + - \l_@@_circa_uncertainty_year) } + } + { + \pgfmathsetmacro{\l_@@_start_extreme_x} + { yeartox(\l_@@_startmin_year) } + } + \bool_if:NTF\l_@@_finishmax_circa_bool + { + \pgfmathsetmacro{\l_@@_finish_extreme_x} + { yeartox(\l_@@_finishmax_year + + \l_@@_circa_uncertainty_year) } + } + { + \pgfmathsetmacro{\l_@@_finish_extreme_x} + { yeartox(\l_@@_finishmax_year) } + } +% \end{macrocode} +% Draw the interval if some part of it is visible. +% \begin{macrocode} + \@@_if_x_range_intersect_tolerance_bounds:nnT + {\l_@@_start_extreme_x}{\l_@@_finish_extreme_x} + { \@@_draw_visible_interval:nn{#1}{#2} } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{@@_draw_visible_interval:nn} +% Draw an interval of which some part is known to be visible. +% \begin{arguments} +% \item PGF keys +% \item Label +% \end{arguments} +% \begin{macrocode} +\cs_new:Npn\@@_draw_visible_interval:nn #1#2 + { +% \end{macrocode} +% Retrieve PGF key values. +% \begin{macrocode} + \pgfqkeys{/timechart}{ + ref/.get=\l_@@_ref_text, + mark/.get=\l_@@_mark_text, + interval~minimum~width/.get=\l_@@_minimum_width_pgf, + interval~bar~color/.get=\l_@@_bar_color, + interval~bar~thickness/.get=\l_@@_bar_thickness_pgf, + interval~mark~color/.get=\l_@@_mark_color, + interval~label~baseline/.get=\l_@@_text_baseline_pgf, + interval~label~node~name/.get=\l_@@_interval_label_node_name, + } +% \end{macrocode} +% Do the remaining calculations. +% \begin{macrocode} + \pgfmathsetlengthmacro{\l_@@_bar_half_thickness_pgf} + {.5*\l_@@_bar_thickness_pgf} + \cs_set:Npn\l_@@_label_text{#2} + \bool_if:NTF\l_@@_startmax_circa_bool + { + \pgfmathsetmacro{\l_@@_start_definite_x} + { yeartox(\l_@@_startmax_year + + \l_@@_circa_uncertainty_year) } + } + { + \pgfmathsetmacro{\l_@@_start_definite_x} + { yeartox(\l_@@_startmax_year) } + } + \bool_if:NTF\l_@@_finishmin_circa_bool + { + \pgfmathsetmacro{\l_@@_finish_definite_x} + { yeartox(\l_@@_finishmin_year + - \l_@@_circa_uncertainty_year) } + } + { + \pgfmathsetmacro{\l_@@_finish_definite_x} + { yeartox(\l_@@_finishmin_year) } + } +% \end{macrocode} +% It is possible that circa indicators have made \cs{l_@@_start_definite_x} greater than \cs{l_@@_finish_definite_x}. +% Check for this; if so, set them both to their average. +% \begin{macrocode} + \@@_pgfmathsetbool:nn{\l_@@_tmpa_bool} + { + \l_@@_start_definite_x > \l_@@_finish_definite_x + } + \bool_if:NT\l_@@_tmpa_bool + { + \pgfmathsetmacro{\l_@@_start_definite_x} + { + .5*( + \l_@@_start_definite_x + + \l_@@_finish_definite_x + ) + } + \pgfmathsetmacro{\l_@@_finish_definite_x} + { \l_@@_start_definite_x } + } +% \end{macrocode} +% Calculate whether it is necessary to make an adjustment to ensure that the minimum width requirement is satisfied, +% and store in \cs{l_@@_tmpa_bool}. +% \begin{macrocode} + \@@_pgfmathsetbool:nn{\l_@@_tmpa_bool} + { + (\l_@@_finish_extreme_x-\l_@@_start_extreme_x) + < \l_@@_minimum_width_pgf + } +% \end{macrocode} +% Calculate whether it is necessary to make an adjustment to prevent a rendering glitch when there are two fadings and +% a zero-width (or very small) definite part, and store in \cs{l_@@_tmpb_bool}. Note the quick test for both ranges +% being fadings. +% \begin{macrocode} + \int_if_zero:nTF + { + \l_@@_start_range_type_int + + \l_@@_finish_range_type_int + } + { + \@@_pgfmathsetbool:nn{\l_@@_tmpb_bool} + { + ( + \l_@@_finish_definite_x + - \l_@@_start_definite_x + ) + < \l_@@_minimum_width_pgf + } + } + { \bool_set_false:N\l_@@_tmpb_bool } +% \end{macrocode} +% Make the adjustment if necessary. +% \begin{macrocode} + \bool_if:nT{ + \l_@@_tmpa_bool || \l_@@_tmpb_bool + }{ + \pgfmathsetmacro{\l_@@_width_adjust} + { .5*\l_@@_minimum_width_pgf } + \pgfmathsetmacro{\l_@@_start_definite_x} + { \l_@@_start_definite_x-\l_@@_width_adjust } + \pgfmathsetmacro{\l_@@_finish_definite_x} + { \l_@@_finish_definite_x+\l_@@_width_adjust } + \pgfmathsetmacro{\l_@@_start_extreme_x} + { + min( + \l_@@_start_extreme_x, + \l_@@_start_definite_x + ) + } + \pgfmathsetmacro{\l_@@_finish_extreme_x} + { + max( + \l_@@_finish_extreme_x, + \l_@@_finish_definite_x + ) + } + } + \pgfmathsetmacro{\l_@@_start_solid_x} + { \l_@@_start_definite_x } + \pgfmathsetmacro{\l_@@_finish_solid_x} + { \l_@@_finish_definite_x } +% \end{macrocode} +% All the data needed to draw the interval are now ready. Shift to the correct vertical coordinate and open a scope for +% drawing. +% \begin{macrocode} + \pgftransformshift{ \pgfpoint{0}{\l_@@_current_y} } + \pgfscope +% \end{macrocode} +% First, do the necessary clipping if the interval extends beyond the specified tolerance from the chart, then process +% the ranges, then draw the solid part of the bar, then do the labelling, then define the node for the bar. Note that +% ranges wth the `slant' style function by adding a new clipping path and extending the solid part, so that there is +% only \emph{one} solid rectangle drawn. +% \begin{macrocode} + \@@_interval_beyond_clip: + \int_case:nn {\l_@@_start_range_type_int} + { + {0}{ \@@_interval_start_range_fade: } + {1}{ \@@_interval_start_range_slant: } + } + \int_case:nn {\l_@@_finish_range_type_int} + { + {0}{ \@@_interval_finish_range_fade: } + {1}{ \@@_interval_finish_range_slant: } + } + \@@_interval_draw_solid: + \@@_interval_mark: + \@@_interval_define_bar_node: + \endpgfscope + \@@_interval_label: + } +% \end{macrocode} +% \end{macro} +% +% +% +% \subsubsection{Clipping} +% +% \begin{macro}{\@@_interval_beyond_clip:} +% If the interval bar extends beyond the specified tolerance from the chart, clip it appropriately. To avoid repeated +% computation, use \cs{l_@@_tmpa_bool} and \cs{l_@@_tmpb_bool} to store whether it is necessary to clip on the left +% and right (respectively). If both are false, there is no need for any clipping. +% \begin{macrocode} +\cs_new:Npn\@@_interval_beyond_clip: + { + \@@_pgfmathsetbool:nn{\l_@@_tmpa_bool} + {\l_@@_start_extreme_x<=\l_@@_start_tolerance_x} + \@@_pgfmathsetbool:nn{\l_@@_tmpb_bool} + {\l_@@_finish_extreme_x>=\l_@@_finish_tolerance_x} + \bool_if:nT{ \l_@@_tmpa_bool || \l_@@_tmpb_bool } + { \@@_interval_beyond_clip_aux: } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_interval_beyond_clip_aux:} +% This macro does the actual clipping. The clipping path starts at the north-east and proceeds clockwise. +% \begin{macrocode} +\cs_new:Npn\@@_interval_beyond_clip_aux: + { + \pgfinterruptboundingbox + \bool_if:NTF\l_@@_tmpb_bool + { + \pgfpathmoveto{ + \pgfpoint + {\l_@@_finish_beyond_x} + {\l_@@_bar_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l_@@_finish_beyond_x} + {\l_@@_bar_half_thickness_pgf} + } + \pgfpatharc + {90} + {270} + {\l_@@_beyond_x_radius_pgf + ~and~\l_@@_bar_half_thickness_pgf} + \pgfpathlineto{ + \pgfpoint + {\l_@@_finish_beyond_x} + {-\l_@@_bar_thickness_pgf} + } + } + { + \pgfpathmoveto{ + \pgfpoint + {\l_@@_right_nonclip_x} + {\l_@@_bar_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l_@@_right_nonclip_x} + {-\l_@@_bar_thickness_pgf} + } + } + \bool_if:NTF\l_@@_tmpa_bool + { + \pgfpathlineto{ + \pgfpoint + {\l_@@_start_beyond_x} + {-\l_@@_bar_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l_@@_start_beyond_x} + {-\l_@@_bar_half_thickness_pgf} + } + \pgfpatharc + {-90} + {90} + {\l_@@_beyond_x_radius_pgf + ~and~\l_@@_bar_half_thickness_pgf} + \pgfpathlineto{ + \pgfpoint + {\l_@@_start_beyond_x} + {\l_@@_bar_thickness_pgf} + } + } + { + \pgfpathlineto{ + \pgfpoint + {\l_@@_left_nonclip_x} + {-\l_@@_bar_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l_@@_left_nonclip_x} + {\l_@@_bar_thickness_pgf} + } + } + \pgfpathclose + \pgfusepath{clip} + \endpgfinterruptboundingbox + } +% \end{macrocode} +% \end{macro} +% +% +% +% \subsubsection{Fading in/out} +% +% Set up the PGF `in' fading. +% \begin{macrocode} +\pgfdeclarehorizontalshading{@@_fade_in_shading}{4bp}{ + color(0bp)=(transparent!100); + color(1bp)=(transparent!100); + color(3bp)=(transparent!0); + color(4bp)=(transparent!0) +} +\pgfdeclarefading + {@@_fade_in} + {\pgfuseshading{@@_fade_in_shading}} +% \end{macrocode} +% +% \begin{macro}{\@@_interval_start_range_fade:} +% Draw fading to indicate a start range. Because some PDF renderers may otherwise leave a gap between the fade and +% solid part, compute \cs{l_@@_fade_extra_pgf} and draw this amount of overlap with the solid part. Because of the +% definition of the `in' fading, \cs{l_@@_fade_extra_pgf} must not exceed half of the fade width. And the overlap +% should not exceed the actual length of the solid part. +% \begin{macrocode} +\cs_new:Npn\@@_interval_start_range_fade: + { + \pgfmathsetlengthmacro{\l_@@_fade_width_pgf} + { \l_@@_start_solid_x-\l_@@_start_extreme_x } + \@@_pgfmathsetbool:nn{\l_@@_tmpa_bool} + { \l_@@_fade_width_pgf + > \pgfkeysvalueof{/timechart/fade~minimum~width} } + \bool_if:NT \l_@@_tmpa_bool + { + \@@_if_x_range_intersect_tolerance_bounds:nnT + {\l_@@_start_extreme_x}{\l_@@_start_solid_x} + { + \pgfmathsetmacro{\l_@@_fade_extra_pgf} + { + min( + .49*\l_@@_fade_width_pgf, + \l_@@_finish_solid_x-\l_@@_start_solid_x + ) + } + \pgfscope + \pgfpathrectanglecorners{ + \pgfpoint + {\l_@@_start_extreme_x} + {-\l_@@_bar_half_thickness_pgf} + }{ + \pgfpoint + {\l_@@_start_solid_x+\l_@@_fade_extra_pgf} + {\l_@@_bar_half_thickness_pgf} + } + \pgfgettransform{\@@_transform_current} + \pgfsetfading{@@_fade_in}{ + \pgfsettransform{\@@_transform_current} + \pgftransformshift{ + \pgfpoint + { + .5*\l_@@_start_extreme_x + +.5*\l_@@_start_solid_x + } + {0} + } + \pgftransformxscale{ + (\l_@@_start_solid_x-\l_@@_start_extreme_x) + /2bp + } + \pgftransformyscale{\l_@@_bar_thickness_pgf/4bp} + } + \pgfsetfillcolor{\l_@@_bar_color} + \pgfusepath{fill} + \endpgfscope + } + } + } +% \end{macrocode} +% \end{macro} +% +% Set up the PGF `out' fading. +% \begin{macrocode} +\pgfdeclarehorizontalshading{@@_fade_out_shading}{4bp}{ + color(0bp)=(transparent!0); + color(1bp)=(transparent!0); + color(3bp)=(transparent!100); + color(4bp)=(transparent!100) +} +\pgfdeclarefading + {@@_fade_out} + {\pgfuseshading{@@_fade_out_shading}} +% \end{macrocode} +% +% \begin{macro}{\@@_interval_finish_range_fade:} +% Draw fading to indicate a finish range. As in \cs{@@_interval_start_range_fade:}, a small overlap is computed. +% \begin{macrocode} +\cs_new:Npn\@@_interval_finish_range_fade: + { + \pgfmathsetlengthmacro{\l_@@_fade_width_pgf} + { \l_@@_finish_extreme_x-\l_@@_finish_solid_x } + \@@_pgfmathsetbool:nn{\l_@@_tmpa_bool} + { \l_@@_fade_width_pgf + > \pgfkeysvalueof{/timechart/fade~minimum~width} } + \bool_if:NT \l_@@_tmpa_bool + { + \@@_if_x_range_intersect_tolerance_bounds:nnT + {\l_@@_finish_solid_x}{\l_@@_finish_extreme_x} + { + \pgfmathsetmacro{\l_@@_fade_extra_pgf} + { + min( + .49*\l_@@_fade_width_pgf, + \l_@@_finish_solid_x-\l_@@_start_solid_x + ) + } + \pgfscope + \pgfpathrectanglecorners{ + \pgfpoint + {\l_@@_finish_solid_x-\l_@@_fade_extra_pgf} + {-\l_@@_bar_half_thickness_pgf} + }{ + \pgfpoint + {\l_@@_finish_extreme_x} + {\l_@@_bar_half_thickness_pgf} + } + \pgfgettransform{\@@_transform_current} + \pgfsetfading{@@_fade_out}{ + \pgfsettransform{\@@_transform_current} + \pgftransformshift{ + \pgfpoint{ + .5*\l_@@_finish_solid_x + +.5*\l_@@_finish_extreme_x + } + {0} + } + \pgftransformxscale{ + (\l_@@_finish_extreme_x-\l_@@_finish_solid_x) + /2bp + } + \pgftransformyscale{\l_@@_bar_thickness_pgf/4bp} + } + \pgfsetfillcolor{\l_@@_bar_color} + \pgfusepath{fill} + \endpgfscope + } + } + } +% \end{macrocode} +% \end{macro} +% +% +% +% \subsubsection{Slant in/out} +% +% \begin{macro}{\@@_interval_start_range_slant:} +% Clip and modify \cs{l_@@_start_solid_x} to draw a slanting shape to indicate the start range. +% \begin{macrocode} +\cs_new:Npn\@@_interval_start_range_slant: + { + \@@_if_equal:nnF + {\l_@@_start_extreme_x}{\l_@@_start_solid_x} + { + \@@_if_x_range_intersect_tolerance_bounds:nnT + {\l_@@_start_extreme_x}{\l_@@_start_solid_x} + { + \pgfinterruptboundingbox + \pgfpathmoveto{ + \pgfpoint + {\l_@@_start_extreme_x} + {-\l_@@_bar_half_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l_@@_start_solid_x} + {\l_@@_bar_half_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l_@@_start_solid_x} + {\l_@@_bar_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l_@@_right_nonclip_x} + {\l_@@_bar_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l_@@_right_nonclip_x} + {-\l_@@_bar_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l_@@_start_extreme_x} + {-\l_@@_bar_thickness_pgf} + } + \pgfpathclose + \pgfusepath{clip} + \endpgfinterruptboundingbox + \pgfmathsetmacro{\l_@@_start_solid_x} + {\l_@@_start_extreme_x} + } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_interval_finish_range_slant:} +% Clip and modify \cs{l_@@_finish_solid_x} to draw a slanting shape to indicate the finish range. +% \begin{macrocode} +\cs_new:Npn\@@_interval_finish_range_slant: + { + \@@_if_equal:nnF + {\l_@@_finish_solid_x}{\l_@@_finish_extreme_x} + { + \@@_if_x_range_intersect_tolerance_bounds:nnT + {\l_@@_finish_solid_x}{\l_@@_finish_extreme_x} + { + \pgfinterruptboundingbox + \pgfpathmoveto{ + \pgfpoint + {\l_@@_finish_solid_x} + {-\l_@@_bar_half_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l_@@_finish_extreme_x} + {\l_@@_bar_half_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l_@@_finish_extreme_x} + {\l_@@_bar_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l_@@_left_nonclip_x} + {\l_@@_bar_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l_@@_left_nonclip_x} + {-\l_@@_bar_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l_@@_finish_solid_x} + {-\l_@@_bar_thickness_pgf} + } + \pgfpathclose + \pgfusepath{clip} + \endpgfinterruptboundingbox + \pgfmathsetmacro{\l_@@_finish_solid_x} + {\l_@@_finish_extreme_x} + } + } + } +% \end{macrocode} +% \end{macro} +% +% +% +% \subsubsection{Solid bar} +% +% \begin{macro}{\@@_interval_draw_solid:} +% Draw the solid part of an interval. +% \begin{macrocode} +\cs_new:Npn\@@_interval_draw_solid: + { + \@@_if_x_range_intersect_tolerance_bounds:nnT + {\l_@@_start_solid_x} + {\l_@@_finish_solid_x} + { + \pgfpathrectanglecorners{ + \pgfpoint + {\l_@@_start_solid_x} + {-\l_@@_bar_half_thickness_pgf} + }{ + \pgfpoint + {\l_@@_finish_solid_x} + {\l_@@_bar_half_thickness_pgf} + } + \pgfsetfillcolor{\l_@@_bar_color} + \pgfusepath{fill} + } + } +% \end{macrocode} +% \end{macro} +% +% +% +% \subsubsection{Marks} +% +% \begin{macrocode} +\msg_new:nnn{timechart}{interval_mark_outside} + { Attempt~to~mark~outside~interval~at~date~#1 } +% \end{macrocode} +% +% \begin{macro}{\@@_interval_mark:} +% Draw marks on an interval. +% \begin{macrocode} +\cs_new:Npn\@@_interval_mark: + { + \pgfscope + \pgfsetstrokecolor{\l_@@_mark_color} + \foreach \year in \l_@@_mark_text + { + \pgfmathsetmacro{\l_@@_mark_x}{yeartox(\year)} + \@@_pgfmathsetbool:nn{\l_@@_tmpa_bool} + { + or ( + \l_@@_mark_x < \l_@@_start_extreme_x, + \l_@@_mark_x > \l_@@_finish_extreme_x + ) + } + \bool_if:NT\l_@@_tmpa_bool + { \msg_error:nne{timechart}{interval_mark_outside}{\year} } + \pgfpathmoveto{ + \pgfpoint + {\l_@@_mark_x} + {-\l_@@_bar_half_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l_@@_mark_x} + {\l_@@_bar_half_thickness_pgf} + } + \pgfusepath{draw} + } + \endpgfscope + } +% \end{macrocode} +% \end{macro} +% +% +% +% \subsubsection{Bar node} +% +% \begin{macro}{\@@_interval_define_bar_node:} +% Define a node with into which the bar fits exactly. +% \begin{macrocode} +\cs_new:Npn\@@_interval_define_bar_node: + { + \@@_make_rectangle_node:nnnn + { + \pgfpoint + {\l_@@_start_extreme_x} + {-\l_@@_bar_half_thickness_pgf} + }{ + \pgfpoint + {\l_@@_finish_extreme_x} + {\l_@@_bar_half_thickness_pgf} + } + {\pgfkeysvalueof{/timechart/interval~bar~node~name}} + {\c_false_bool} + } +% \end{macrocode} +% \end{macro} +% +% +% +% \subsubsection{Label} +% +% \begin{macro}{\@@_interval_label:} +% Place the label for the item. +% \begin{macrocode} +\cs_new:Npn\@@_interval_label: + { + \str_if_empty:NF \l_@@_label_text + { + \pgfinterruptboundingbox + \int_case:nn {\l_@@_label_pos_int} + { + {0}{ \@@_interval_label_left: } + {1}{ \@@_interval_label_center: } + {2}{ \@@_interval_label_right: } + } + \endpgfinterruptboundingbox + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_interval_label_left:} +% Place the label on the left of the interval. +% \begin{macrocode} +\cs_new:Npn\@@_interval_label_left: + { + \@@_if_x_in_bounds:nT{\l_@@_start_extreme_x} + { + \group_begin: + \pgftransformshift{ + \pgfpoint + {\l_@@_start_extreme_x} + {\l_@@_text_baseline_pgf} + } + \node[/timechart/interval~label,anchor=base~east] + (\l_@@_interval_label_node_name) + at (0,0) + { + \@@_make_ref:NN + \l_@@_ref_text + \l_@@_label_text + }; + \group_end: + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_interval_label_right:} +% Place the label on the right of the interval. +% \begin{macrocode} +\cs_new:Npn\@@_interval_label_right: + { + \@@_if_x_in_bounds:nT{\l_@@_finish_extreme_x} + { + \group_begin: + \pgftransformshift{ + \pgfpoint + {\l_@@_finish_extreme_x} + {\l_@@_text_baseline_pgf} + } + \node[/timechart/interval~label,anchor=base~west] + (\l_@@_interval_label_node_name) + at (0,0) + { + \@@_make_ref:NN + \l_@@_ref_text + \l_@@_label_text + }; + \group_end: + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_interval_label_center:} +% Place the label at the center of the interval. +% \begin{macrocode} +\cs_new:Npn\@@_interval_label_center: + { + \pgfmathsetlengthmacro{\l_@@_label_anchor_x} + { + .5*max(\l_@@_start_definite_x,\l_@@_start_x) + + .5*min(\l_@@_finish_definite_x,\l_@@_finish_x) + } +% \end{macrocode} +% First draw the `background label' on the layer below the bar. +% \begin{macrocode} + \group_begin: + \pgftransformshift{ + \pgfpoint + {\l_@@_label_anchor_x} + {\l_@@_text_baseline_pgf} + } + \pgfonlayer{labelbg} + \node[ + /timechart/interval~label~centered~background, + anchor=base + ] + at (0,0) + { \l_@@_label_text }; + \endpgfonlayer + \group_end: +% \end{macrocode} +% Then draw the label on top of the bar, clipping it to the bar outline. +% \begin{macrocode} + \pgfscope + \pgfpathrectanglecorners{ + \pgfpoint + {\l_@@_start_extreme_x} + {-\l_@@_bar_half_thickness_pgf} + }{ + \pgfpoint + {\l_@@_finish_extreme_x} + {\l_@@_bar_half_thickness_pgf} + } + \pgfusepath{clip} + \group_begin: + \pgftransformshift{ + \pgfpoint + {\l_@@_label_anchor_x} + {\l_@@_text_baseline_pgf} + } + \node[ + /timechart/interval~label~centered, + anchor=base + ] + (\l_@@_interval_label_node_name) + at (0,0) + { + \@@_make_ref:NN + \l_@@_ref_text + \l_@@_label_text + }; + \group_end: + \endpgfscope + } +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Text} +% +% \begin{macro}{\@@_text_user:Omm} +% Place text. This macro will be made available as \cs{timecharttext} inside the \env{timechart} environment. +% \begin{arguments} +% \item PGF keys under \key{/timechart/} to apply. +% \item Year at which to place text. +% \item Text. +% \end{arguments} +% \begin{macrocode} +\NewDocumentCommand{\@@_text_user:Omm}{ O{} m m } + { + \str_if_empty:nF{#3}{ + \group_begin: + \pgfmathsetmacro{\l_@@_text_x}{yeartox(#2)} + \@@_if_x_in_tolerance_bounds:nT{\l_@@_text_x} + { +% \end{macrocode} +% Process keys supplied locally and retrieve needed keys. +% \begin{macrocode} + \pgfqkeys{/timechart}{ + #1, + ref/.get=\l_@@_ref_text, + text~node~name/.get=\l_@@_node_name_text, + text~baseline/.get=\l_@@_text_baseline_pgf, + } +% \end{macrocode} +% Shift to the correct vertical coordinate and place the text. +% \begin{macrocode} + \pgftransformshift{ + \pgfpoint{0}{\l_@@_current_y} + } + \pgfinterruptboundingbox + \group_begin: + \pgftransformshift{ + \pgfpoint{\l_@@_text_x}{\l_@@_text_baseline_pgf} + } + \cs_set:Npn\l_@@_text{#3} + \int_case:nn {\l_@@_text_pos_int} + { + {0}{ \cs_set:Npn\l_@@_node_anchor_text{base~east} } + {1}{ \cs_set:Npn\l_@@_node_anchor_text{base} } + {2}{ \cs_set:Npn\l_@@_node_anchor_text{base~west} } + } + \node[/timechart/text,anchor=\l_@@_node_anchor_text] + (\l_@@_node_name_text) + at (0,0) + { + \@@_make_ref:NN + \l_@@_ref_text + \l_@@_text + }; + \group_end: + \endpgfinterruptboundingbox + } + \group_end: + } +% \end{macrocode} +% Since the text itself does not affect the bounding box, create a space (which will handle the automatic step). +% \begin{macrocode} + \@@_space_user:O[#1] + } +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Space} +% +% \begin{macro}{\@@_space_user:O} +% Create a vertical space as if there were an interval at the current coordinate. This macro will be made available as +% \cs{timechartspace} inside the \env{timechart} environment. +% \begin{macrocode} +\NewDocumentCommand{\@@_space_user:O}{ O{} } + { + \group_begin: +% \end{macrocode} +% Process keys supplied locally and retreive the one needed value. + +% \begin{macrocode} + \pgfqkeys{/timechart}{ + #1, + interval~bar~thickness/.get=\l_@@_bar_thickness_pgf, + } + \pgfmathsetlengthmacro{\l_@@_bar_half_thickness_pgf} + { .5*\l_@@_bar_thickness_pgf } +% \end{macrocode} +% Shift to the correct vertical coordinate and create the space. +% \begin{macrocode} + \pgftransformshift{ + \pgfpoint{0}{\l_@@_current_y} + } + \pgfpathmoveto{ + \pgfpoint{0}{-\l_@@_bar_half_thickness_pgf} + } + \pgfpathmoveto{ + \pgfpoint{0}{\l_@@_bar_half_thickness_pgf} + } + \pgfusepath{discard} + \group_end: + \bool_if:NT\l_@@_autostep_bool{ + \@@_step_y_user:O + } + } +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Legends} +% +% \begin{macro}{\timechartlegenditem} +% Draw a bar suitable for use in a legend, applying style in \param{1}. +% \begin{macrocode} +\NewDocumentCommand{\timechartlegenditem}{ O{} } + { + \@@_legend_aux:nn{#1}{ + \pgfmathsetlengthmacro{\l_@@_start_solid_x}{0} + \pgfmathsetlengthmacro{\l_@@_finish_solid_x} + {\l_@@_legenditem_width_pgf} + \@@_interval_draw_solid: + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\timechartlegendstartrange,\timechartlegendfinishrange} +% Draw a bar with start/finish range, suitable for use in a legend, applying style in \param{1}. +% \begin{macrocode} +\NewDocumentCommand{\timechartlegendstartrange}{ O{} } + { + \@@_legend_aux:nn{#1}{ + \pgfmathsetlengthmacro{\l_@@_start_extreme_x}{0} + \pgfmathsetlengthmacro{\l_@@_start_solid_x} + {\pgfkeysvalueof{/timechart/legend~item~range~width}} + \pgfmathsetlengthmacro{\l_@@_finish_solid_x} + {\l_@@_legenditem_width_pgf} + \int_case:nn {\l_@@_start_range_type_int} + { + {0}{ \@@_interval_start_range_fade: } + {1}{ \@@_interval_start_range_slant: } + } + \@@_interval_draw_solid: + } + } +\NewDocumentCommand{\timechartlegendfinishrange}{ O{} } + { + \@@_legend_aux:nn{#1}{ + \pgfmathsetlengthmacro{\l_@@_start_solid_x}{0} + \pgfmathsetlengthmacro{\l_@@_finish_solid_x} + { + \l_@@_legenditem_width_pgf + -\pgfkeysvalueof{/timechart/legend~item~range~width} + } + \pgfmathsetlengthmacro{\l_@@_finish_extreme_x} + {\l_@@_legenditem_width_pgf} + \int_case:nn {\l_@@_finish_range_type_int} + { + {0}{ \@@_interval_finish_range_fade: } + {1}{ \@@_interval_finish_range_slant: } + } + \@@_interval_draw_solid: + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_legend_aux:nn} +% Auxiliary command for legend items. Draw a \TikZ\ picture, applying PGF keys \param{1} under \key{/timechart/} +% and using \TikZ\ code \param{2}. +% \begin{macrocode} +\cs_new:Npn\@@_legend_aux:nn #1#2 + { + \tikzpicture +% \end{macrocode} +% Process supplied PGF keys and retrieve only those necessary. +% \begin{macrocode} + \pgfkeys{ + /timechart/.cd, + #1, + interval~bar~thickness/.get=\l_@@_bar_thickness_pgf, + interval~bar~color/.get=\l_@@_bar_color, + interval~minimum~width/.get=\l_@@_minimum_width_pgf, + beyond~length/.get=\l_@@_beyond_length_pgf, + legend~item~width/.get=\l_@@_legenditem_width_pgf, + } + \pgfmathsetlengthmacro{\l_@@_bar_half_thickness_pgf} + {.5*\l_@@_bar_thickness_pgf} + \pgfmathsetmacro{\l_@@_start_x}{0} + \pgfmathsetmacro{\l_@@_finish_x} + {\l_@@_legenditem_width_pgf} + \pgfmathsetlengthmacro{\l_@@_start_beyond_x} + {\l_@@_start_x-\l_@@_beyond_length_pgf} + \pgfmathsetlengthmacro{\l_@@_finish_beyond_x} + {\l_@@_finish_x+\l_@@_beyond_length_pgf} + \pgfmathsetmacro{\l_@@_current_y}{0} +% \end{macrocode} +% Ensure that the legend is `visible' from the perspective of the drawing macros. +% \begin{macrocode} + \pgfmathsetmacro{\l_@@_start_tolerance_x} + {\l_@@_start_x-10mm} + \pgfmathsetmacro{\l_@@_finish_tolerance_x} + {\l_@@_finish_x+10mm} + #2 +% \end{macrocode} +% Make sure that the legend has the required bounding box. +% \begin{macrocode} + \pgfresetboundingbox + \pgfmoveto{ + \pgfpoint{0}{-\l_@@_bar_half_thickness_pgf} + } + \pgfmoveto{ + \pgfpoint + {\l_@@_legenditem_width_pgf} + {\l_@@_bar_half_thickness_pgf} + } + \endtikzpicture% + } +% \end{macrocode} +% \end{macro} +% +% +% +% \begin{macrocode} +%</package> +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex diff --git a/Master/texmf-dist/source/latex/timechart/timechart.ins b/Master/texmf-dist/source/latex/timechart/timechart.ins new file mode 100644 index 00000000000..e4a9c32bed5 --- /dev/null +++ b/Master/texmf-dist/source/latex/timechart/timechart.ins @@ -0,0 +1,38 @@ +%% +%% Copyright (C) 2025 Alan J. Cain +%% +%% This file may be distributed and/or modified under the +%% conditions of the LaTeX Project Public Licence, either +%% version 1.3 of this licence or (at your option) any later +%% version. The latest version of this licence is in: +%% +%% http://www.latex-project.org/lppl.txt +%% +%% and version 1.3c or later is part of all distributions of +%% LaTeX version 2008-05-04 or later. +%% +\input l3docstrip.tex +\askforoverwritefalse +\nopostamble + +\preamble + +This is a generated file. + +Copyright (C) 2025 Alan J. Cain + +This file may be distributed and/or modified under the +conditions of the LaTeX Project Public Licence, either +version 1.3 of this licence or (at your option) any later +version. The latest version of this licence is in: + +http://www.latex-project.org/lppl.txt + +and version 1.3c or later is part of all distributions of +LaTeX version 2008-05-04 or later. + +\endpreamble + +\generate{\file{timechart.sty}{\from{timechart.dtx}{package}}} + +\endbatchfile diff --git a/Master/texmf-dist/tex/latex/timechart/timechart.sty b/Master/texmf-dist/tex/latex/timechart/timechart.sty new file mode 100644 index 00000000000..47ab006e982 --- /dev/null +++ b/Master/texmf-dist/tex/latex/timechart/timechart.sty @@ -0,0 +1,1615 @@ +%% +%% This is file `timechart.sty', +%% generated with the docstrip utility. +%% +%% The original source files were: +%% +%% timechart.dtx (with options: `package') +%% +%% This is a generated file. +%% +%% Copyright (C) 2025 Alan J. Cain +%% +%% This file may be distributed and/or modified under the +%% conditions of the LaTeX Project Public Licence, either +%% version 1.3 of this licence or (at your option) any later +%% version. The latest version of this licence is in: +%% +%% http://www.latex-project.org/lppl.txt +%% +%% and version 1.3c or later is part of all distributions of +%% LaTeX version 2008-05-04 or later. +%% +\NeedsTeXFormat{LaTeX2e}[2020-02-02] +\ProvidesExplPackage{timechart}{2025-02-15}{0.51} + {Typesetting chronological charts} +\RequirePackage{tikz} +\bool_new:N\l__timechart_tmpa_bool +\bool_new:N\l__timechart_tmpb_bool +\bool_new:N\l__timechart_tmpc_bool +\bool_new:N\l__timechart_tmpd_bool +\cs_set_eq:NN\l__timechart_tmpa_dim\l_tmpa_dim +\cs_set_eq:NN\l__timechart_tmpb_dim\l_tmpb_dim +\dim_new:N\l__timechart_tmpc_dim +\dim_new:N\l__timechart_tmpd_dim +\cs_new:Npn\__timechart_make_ref:NN #1#2 + { + \str_if_empty:NTF #1 + { #2 } + { \hyperref[#1]{#2} } + } +\cs_new:Npn\__timechart_pgfmathsetbool:nn #1#2 + { + \pgfmathsetmacro{#1}{ifthenelse(#2,"\c_true_bool","\c_false_bool")} + } +\cs_new:Npn\__timechart_if_equal:nnF #1#2#3 + { + \__timechart_pgfmathsetbool:nn{\l__timechart_tmpa_bool}{#1==#2} + \bool_if:NF\l__timechart_tmpa_bool{#3} + } +\cs_new:Npn\__timechart_pgfextractxy:nnn #1#2#3 + { + \pgf@process{#3} + #1=\pgf@x\relax + #2=\pgf@y\relax + } +\cs_new:Npn\__timechart_hsmash_pgfnode:nnnnn #1#2#3#4#5 + { + \pgfinterruptboundingbox + \pgfnode{#1}{#2}{#3}{#4}{#5} + \pgfcoordinate + {__timechart_tmpa_coord} + {\pgfpointanchor{current~bounding~box}{south}} + \pgfcoordinate + {__timechart_tmpb_coord} + {\pgfpointanchor{current~bounding~box}{north}} + \endpgfinterruptboundingbox + \pgfextractx + {\l__timechart_tmpa_dim} + {\pgfpointanchor{__timechart_tmpa_coord}{center}} + \pgfextractx + {\l__timechart_tmpb_dim} + {\pgfpointanchor{__timechart_tmpb_coord}{center}} + \pgfpathmoveto{\pgfpoint{\l__timechart_tmpa_dim}{0}} + \pgfpathmoveto{\pgfpoint{\l__timechart_tmpb_dim}{0}} + \pgfusepath{discard} + } +\cs_new:Npn\__timechart_make_rectangle_node:nnnn #1#2#3#4 + { + \group_begin: + \__timechart_pgfextractxy:nnn + {\l__timechart_tmpa_dim}{\l__timechart_tmpb_dim}{#1} + \__timechart_pgfextractxy:nnn + {\l__timechart_tmpc_dim}{\l__timechart_tmpd_dim}{#2} + \pgftransformshift{#1} + \pgfset{ + minimum~width=\l__timechart_tmpc_dim-\l__timechart_tmpa_dim, + minimum~height=\l__timechart_tmpd_dim-\l__timechart_tmpb_dim, + inner~sep=0, + outer~sep=0, + } + \bool_if:NTF #4 + { \pgfnode{rectangle}{south~west}{}{#3}{\pgfusepath{draw}} } + { \pgfnode{rectangle}{south~west}{}{#3}{\pgfusepath{discard}} } + \group_end: + } +\pgfmathsetmacro{\l__timechart_left_nonclip_x}{-16000pt} +\pgfmathsetmacro{\l__timechart_right_nonclip_x}{16000pt} +\cs_new:Npn\__timechart_set_style_line_width:nn #1#2 + { + \begingroup + \tikzset{#2} + \pgfmathsetlengthmacro{#1}{\pgflinewidth} + \pgfmathsmuggle #1 + \endgroup + } +\pgfkeys{ + /timechart/.cd, + width/.initial=\textwidth, + tolerance/.initial=5pt, + beyond~length/.initial=5pt, + beyond~x~radius/.initial=4pt, + ystep/.initial=-10pt, + minor~tick~interval/.initial=10, + major~tick~interval/.initial=50, + no~grid/.code = { \bool_set_false:N\l__timechart_grid_bool }, + grid~top~ysep/.initial={3pt}, + grid~bottom~ysep/.initial={3pt}, + grid/.style={}, + no~axis/.code = { \bool_set_false:N\l__timechart_axis_bool }, + axis~line/.style={ + line~cap=rect, + }, + axis~ysep/.initial=3pt, + minor~tick/.style={}, + minor~tick~length/.initial=1.5mm, + major~tick/.style={}, + major~tick~length/.initial=3mm, + major~tick~label/.style={ + inner~sep=0, + outer~sep=0, + anchor=mid~west, + rotate=90, + }, + no~autostep/.code = { \bool_set_false:Nz\l__timechart_autostep_bool }, + ref/.initial={}, + mark/.initial={}, + marks/.forward~to=/timechart/mark, + circa~uncertainty/.initial=3, + interval~minimum~width/.initial=1pt, + interval~bar~color/.initial=black, + interval~bar~thickness/.initial=8pt, + interval~bar~node~name/.initial = {bar~node}, + interval~mark~color/.initial=gray, + interval~label/.style={}, + interval~label~centered/.style={/timechart/interval~label,text=white}, + interval~label~centered~background/.style={/timechart/interval~label}, + interval~label~baseline/.initial=-3pt, + interval~label~pos/.is~choice, + interval~label~pos/left/.code + = { \int_set:Nn\l__timechart_label_pos_int{0} }, + interval~label~pos/center/.code + = { \int_set:Nn\l__timechart_label_pos_int{1} }, + interval~label~pos/right/.code + = { \int_set:Nn\l__timechart_label_pos_int{2} }, + interval~label~node~name/.initial = {label~node}, + start~range/.is~choice, + start~range/fade/.code + = { \int_set:Nn\l__timechart_start_range_type_int{0} }, + start~range/slant/.code + = { \int_set:Nn\l__timechart_start_range_type_int{1} }, + finish~range/.is~choice, + finish~range/fade/.code + = { \int_set:Nn\l__timechart_finish_range_type_int{0} }, + finish~range/slant/.code + = { \int_set:Nn\l__timechart_finish_range_type_int{1} }, + fade~minimum~width/.initial = 0pt, + text~node~name/.initial = {text~node}, + text/.style={}, + text~baseline/.initial=-3pt, + text~pos/.is~choice, + text~pos/left/.code = { \int_set:Nn\l__timechart_text_pos_int{0} }, + text~pos/center/.code = { \int_set:Nn\l__timechart_text_pos_int{1} }, + text~pos/right/.code = { \int_set:Nn\l__timechart_text_pos_int{2} }, + legend~item~width/.initial=9mm, + legend~item~range~width/.initial=3mm, + left/.code = { + \int_set:Nn\l__timechart_label_pos_int{0} + \int_set:Nn\l__timechart_text_pos_int{0} + }, + center/.code = { + \int_set:Nn\l__timechart_label_pos_int{1} + \int_set:Nn\l__timechart_text_pos_int{1} + }, + right/.code = { + \int_set:Nn\l__timechart_label_pos_int{2} + \int_set:Nn\l__timechart_text_pos_int{2} + }, +} +\bool_new:N\l__timechart_grid_bool +\bool_set_true:N\l__timechart_grid_bool +\bool_new:N\l__timechart_axis_bool +\bool_set_true:N\l__timechart_axis_bool +\bool_new:N\l__timechart_autostep_bool +\bool_set_true:N\l__timechart_autostep_bool +\int_new:N \l__timechart_label_pos_int +\int_new:N \l__timechart_text_pos_int +\int_new:N \l__timechart_start_range_type_int +\int_new:N \l__timechart_finish_range_type_int +\NewDocumentEnvironment{timechart}{ O{} m m } + { \__timechart_main_begin:nnn{#1}{#2}{#3} } + { \__timechart_main_end: } +\cs_new:Npn \__timechart_main_begin:nnn #1#2#3 + { + \pgfkeys{ + /timechart/.cd, + #1, + width/.get=\l__timechart_width_pgf, + tolerance/.get=\l__timechart_tolerance_pgf, + ystep/.get=\l__timechart_ystep_pgf, + grid~top~ysep/.get=\l__timechart_grid_top_ysep_pgf, + grid~bottom~ysep/.get=\l__timechart_grid_bottom_ysep_pgf, + beyond~length/.get=\l__timechart_beyond_length_pgf, + beyond~x~radius/.get=\l__timechart_beyond_x_radius_pgf, + minor~tick~interval/.get=\l__timechart_minor_tick_interval_year, + major~tick~interval/.get=\l__timechart_major_tick_interval_year, + } + \tikzpicture + \pgfdeclarelayer{grid} + \pgfdeclarelayer{labelbg} + \pgfsetlayers{grid,labelbg,main} + \bool_if:NTF\l__timechart_grid_bool + { + \__timechart_set_style_line_width:nn + {\l__timechart_grid_line_width} + {/timechart/grid} + } + { \pgfmathsetlengthmacro{\l__timechart_grid_line_width}{0} } + \bool_if:NTF\l__timechart_axis_bool + { + \__timechart_set_style_line_width:nn + {\l__timechart_axis_line_width} + {/timechart/axis~line} + \__timechart_set_style_line_width:nn + {\l__timechart_major_tick_line_width} + {/timechart/major~tick} + \__timechart_set_style_line_width:nn + {\l__timechart_minor_tick_line_width} + {/timechart/minor~tick} + } + { + \pgfmathsetlengthmacro{\l__timechart_axis_line_width}{0} + \pgfmathsetlengthmacro{\l__timechart_major_tick_line_width}{0} + \pgfmathsetlengthmacro{\l__timechart_minor_tick_line_width}{0} + } + \__timechart_parse_date:NNn\l_tmpa_bool\l__timechart_start_year{#2} + \__timechart_parse_date:NNn\l_tmpa_bool\l__timechart_finish_year{#3} + \pgfmathsetmacro{\l__timechart_start_year} + {floor(\l__timechart_start_year)} + \pgfmathsetmacro{\l__timechart_finish_year} + {floor(\l__timechart_finish_year)} + \pgfmathsetmacro{\l__timechart_x} + { + ( + \l__timechart_width_pgf + - max( + \l__timechart_grid_line_width, + \l__timechart_axis_line_width, + \l__timechart_major_tick_line_width, + \l__timechart_minor_tick_line_width + ) + )/(\l__timechart_finish_year-\l__timechart_start_year) + } + \pgfkeys{ + /pgf/declare~function={ + yeartox(\n)=\l__timechart_x*(\n-\l__timechart_start_year); + }, + } + \pgfmathsetmacro{\l__timechart_start_x} + {yeartox(\l__timechart_start_year)} + \pgfmathsetmacro{\l__timechart_finish_x} + {yeartox(\l__timechart_finish_year)} + \pgfmathsetmacro{\l__timechart_start_tolerance_x}{ + \l__timechart_start_x-(\l__timechart_tolerance_pgf) + } + \pgfmathsetmacro{\l__timechart_finish_tolerance_x}{ + \l__timechart_finish_x+(\l__timechart_tolerance_pgf) + } + \pgfmathsetmacro{\l__timechart_start_beyond_x}{ + \l__timechart_start_x-(\l__timechart_beyond_length_pgf) + } + \pgfmathsetmacro{\l__timechart_finish_beyond_x}{ + \l__timechart_finish_x+(\l__timechart_beyond_length_pgf) + } + \pgfmathsetmacro{\l__timechart_current_y}{0} + \pgfmathsetmacro{\l__timechart_saved_y}{0} + \pgfmathsetmacro{\l__timechart_auto_reset_minimum_y}{-16000pt} + \pgfmathsetmacro{\l__timechart_auto_reset_maximum_y}{16000pt} + \pgfmathsetmacro{\l__timechart_start_plus_year}{ + \l__timechart_start_year+\l__timechart_minor_tick_interval_year + } + \pgfmathsetmacro{\l__timechart_start_plusplus_year}{ + \l__timechart_start_year+(2*\l__timechart_minor_tick_interval_year) + } + \pgfmathsetmacro{\l__timechart_end_minus_year}{ + \l__timechart_finish_year-\l__timechart_minor_tick_interval_year + } + \group_begin: + \cs_set_eq:NN\timechartinterval\__timechart_interval_user:Ommm + \cs_set_eq:NN\timecharttext\__timechart_text_user:Omm + \cs_set_eq:NN\timechartspace\__timechart_space_user:O + \cs_set_eq:NN\timechartsety\__timechart_set_y_user:m + \cs_set_eq:NN\timechartsavey\__timechart_save_y_user: + \cs_set_eq:NN\timechartresety\__timechart_reset_y_user: + \cs_set_eq:NN\timechartsetyminimumautoreset + \__timechart_set_y_minimum_auto_reset_user:m + \cs_set_eq:NN\timechartsetymaximumautoreset + \__timechart_set_y_maximum_auto_reset_user:m + \cs_set_eq:NN\timechartstepy\__timechart_step_y_user:O + \cs_set_eq:NN\timechartfinish\__timechart_main_end_user: + } +\cs_new:Npn\__timechart_main_end: + { + \cs_if_eq:NNT\timechartfinish\__timechart_main_end_user: + { \__timechart_main_end_user: } + \endtikzpicture + } +\cs_new:Npn\__timechart_main_end_user: + { + \pgfextracty{\l__timechart_tmpa_dim} + { \pgfpointanchor{current~bounding~box}{south} } + \pgfextracty{\l__timechart_tmpb_dim} + { \pgfpointanchor{current~bounding~box}{north} } + \pgfresetboundingbox + \dim_compare:nNnTF{\l__timechart_tmpa_dim}>{\l__timechart_tmpb_dim} + { + \pgfmathsetmacro{\l__timechart_content_bottom_y}{0pt} + \pgfmathsetmacro{\l__timechart_content_top_y}{0pt} + } + { + \pgfmathsetmacro{\l__timechart_content_bottom_y} + {\l__timechart_tmpa_dim} + \pgfmathsetmacro{\l__timechart_content_top_y} + {\l__timechart_tmpb_dim} + } + \bool_if:NTF{\l__timechart_grid_bool} + { + \__timechart_grid_draw: + \pgfmathsetmacro{\l__timechart_axis_y} + { + \l__timechart_content_top_y + + \l__timechart_grid_top_ysep_pgf + + \pgfkeysvalueof{/timechart/axis~ysep} + } + } + { + \__timechart_nogrid_bounding_box_set: + \pgfmathsetmacro{\l__timechart_axis_y} + { + \l__timechart_content_top_y + +\pgfkeysvalueof{/timechart/axis~ysep} + } + } + \bool_if:NT{\l__timechart_axis_bool} + { \__timechart_axis_draw: } + \group_end: + } +\cs_new:Npn\__timechart_grid_draw: + { + \pgfmathsetmacro{\l__timechart_grid_bottom_y}{ + \l__timechart_content_bottom_y-\l__timechart_grid_bottom_ysep_pgf + } + \pgfmathsetmacro{\l__timechart_grid_top_y}{ + \l__timechart_content_top_y+\l__timechart_grid_top_ysep_pgf + } + \pgfonlayer{ grid } + \scope[/timechart/grid] + \foreach \year in { + \l__timechart_start_plus_year, + \l__timechart_start_plusplus_year, + ..., + \l__timechart_end_minus_year + } { + \group_begin: + \__timechart_pgfmathsetbool:nn{\l__timechart_tmpa_bool} + { Mod(\year,\l__timechart_major_tick_interval_year)==0 } + \bool_if:NT\l__timechart_tmpa_bool + { + \pgftransformshift{ \pgfpoint{yeartox(\year)}{0} } + \pgfpathmoveto{ \pgfpoint{0}{\l__timechart_grid_top_y} } + \pgfpathlineto{ \pgfpoint{0}{\l__timechart_grid_bottom_y} } + \pgfusepath{ draw } + } + \group_end: + } + \__timechart_make_rectangle_node:nnnn + { \pgfpoint{\l__timechart_start_x}{\l__timechart_grid_bottom_y} } + { \pgfpoint{\l__timechart_finish_x}{\l__timechart_grid_top_y} } + { grid } + { \c_true_bool } + \endscope + \endpgfonlayer + } +\cs_new:Npn\__timechart_axis_draw: + { + \group_begin: + \pgfkeys{ + /timechart/minor~tick~length/.get=\__timechart_minor_tick_length_pgf, + /timechart/major~tick~length/.get=\__timechart_major_tick_length_pgf, + } + \pgftransformshift{ \pgfpoint{0}{\l__timechart_axis_y} } + \pgfmathsetmacro{\__timechart_start_major_tick_year} + { + \l__timechart_start_year + -Mod( + \l__timechart_start_year, + \l__timechart_major_tick_interval_year + ) + } + \pgfmathsetmacro{\__timechart_start_major_tick_year} + { + ifthenelse( + \__timechart_start_major_tick_year<\l__timechart_start_year, + \__timechart_start_major_tick_year + +\l__timechart_major_tick_interval_year, + \__timechart_start_major_tick_year + ) + } + \pgfmathsetmacro{\__timechart_finish_major_tick_year} + { + \l__timechart_finish_year + -Mod( + \l__timechart_finish_year, + \l__timechart_major_tick_interval_year + ) + } + \foreach \year in { + \l__timechart_start_year, + \l__timechart_start_plus_year, + ..., + \l__timechart_finish_year + } { + \pgfmathsetmacro{\x}{yeartox(\year)} + \__timechart_pgfmathsetbool:nn + {\l__timechart_tmpa_bool} + { Mod(\year,\l__timechart_major_tick_interval_year)==0 } + \bool_if:NTF\l__timechart_tmpa_bool + { \__timechart_axis_draw_labelled_major_tick:NN\x\year } + { \__timechart_axis_draw_minor_tick:N\x } + } + \__timechart_draw_axis_line + \pgfextracty{\l__timechart_tmpa_dim} + { \pgfpointanchor{current~bounding~box}{north} } + \__timechart_make_rectangle_node:nnnn + { \pgfpoint{\l__timechart_start_x}{0} } + { \pgfpoint{\l__timechart_finish_x}{\l__timechart_tmpa_dim} } + { axis } + { \c_false_bool } + \group_end: + } +\cs_new:Npn\__timechart_axis_draw_minor_tick:N #1 + { + \scope[/timechart/minor~tick] + \pgfpathmoveto{ \pgfpoint{#1}{0} } + \pgfpathlineto{ \pgfpoint{#1}{\__timechart_minor_tick_length_pgf} } + \pgfusepath{draw} + \endscope + } +\cs_new:Npn\__timechart_axis_draw_labelled_major_tick:NN #1#2 + { + \__timechart_pgfmathsetbool:nn{\l__timechart_tmpa_bool}{#2==0} + \bool_if:NTF \l__timechart_tmpa_bool + { \__timechart_axis_draw_zero_tick:N #1 } + { + \__timechart_pgfmathsetbool:nn{\l__timechart_tmpb_bool} + { #2==\__timechart_start_major_tick_year } + \__timechart_pgfmathsetbool:nn{\l__timechart_tmpc_bool} + { #2==\__timechart_finish_major_tick_year } + \bool_set:Nn\l__timechart_tmpd_bool + { \l__timechart_tmpb_bool || \l__timechart_tmpc_bool } + \__timechart_axis_draw_major_tick:N #1 + \__timechart_axis_draw_year_label:nnnnnn + { #1 } + { #2 } + { \l__timechart_tmpd_bool } + { mid~west } + { 0 } + { \__timechart_major_tick_length_pgf+1mm } + } + } +\cs_new:Npn\__timechart_axis_draw_major_tick:N #1 + { + \scope[/timechart/major~tick] + \pgfpathmoveto{ \pgfpoint{#1}{0} } + \pgfpathlineto{ \pgfpoint{#1}{\__timechart_major_tick_length_pgf} } + \pgfusepath{draw} + \endscope + } +\cs_new:Npn\__timechart_axis_draw_zero_tick:N #1 + { + \group_begin: + \pgftransformshift{ \pgfpoint{#1}{0} } + \pgfmathsetmacro{\a}{5} + \pgfmathsetlengthmacro{\r}{1mm} + \pgfmathsetlengthmacro{\t}{\r*(cos(\a)-cos(90-\a))/(1-cos(90-\a))} + \scope[/timechart/major~tick,line~join=miter] + \pgfpathmoveto{\pgfpointorigin} + \pgfpathlineto{\pgfpoint{0}{\__timechart_major_tick_length_pgf}} + \pgfpatharc{0}{90-\a}{\t~and~\r} + \pgfpatharc{270+\a}{360-\a}{\r} + \pgfpatharc{180+\a}{270-\a}{\r} + \pgfpatharc{90+\a}{180-\a}{\t~and~\r} + \pgfpathlineto{\pgfpoint{0}{\__timechart_major_tick_length_pgf-1pt}} + \pgfusepath{draw} + \endscope + \__timechart_axis_draw_year_label:nnnnnn + { 0 } + { -1 } + { \c_true_bool } + { base~west } + { -.5mm } + { 5.5mm } + \__timechart_axis_draw_year_label:nnnnnn + { 0 } + { 1 } + { \c_true_bool } + { north~west } + { .5mm } + { 5.5mm } + \group_end: + } +\cs_new:Npn\__timechart_draw_axis_line + { + \scope[/timechart/axis~line] + \pgfpathmoveto{ \pgfpoint{\l__timechart_start_x}{0} } + \pgfpathlineto{ \pgfpoint{\l__timechart_finish_x}{0} } + \pgfusepath{draw} + \endscope + } +\cs_new:Npn\__timechart_axis_draw_year_label:nnnnnn #1#2#3#4#5#6 + { + \group_begin: + \pgftransformshift{ \pgfpoint{#1+#5}{#6} } + \pgfmathtruncatemacro{\absyear}{ abs(#2) } + \scope[/timechart/major~tick~label] + \bool_if:NTF #3 + { + \__timechart_pgfmathsetbool:nn{\l__timechart_tmpa_bool}{#2<0} + \bool_if:NTF\l__timechart_tmpa_bool + { \cs_set_eq:NN\__timechart_make_year:n\timechartmakebeforeyear } + { \cs_set_eq:NN\__timechart_make_year:n\timechartmakeafteryear } + \__timechart_hsmash_pgfnode:nnnnn + {rectangle} + {#4} + {\__timechart_make_year:n{\absyear}} + {} + {} + } + { \pgfnode{rectangle}{#4}{\absyear}{}{} } + \endscope + \group_end: + } +\cs_new:Npn\timechartmakebeforeyear #1 + { + #1\nobreakspace\textsc{bce} + } +\cs_new:Npn\timechartmakeafteryear #1 + { + #1\nobreakspace\textsc{ce} + } +\cs_new:Npn\__timechart_nogrid_bounding_box_set: + { + \pgfpathmoveto + { \pgfpoint{\l__timechart_start_x}{\l__timechart_content_bottom_y} } + \pgfpathmoveto + { \pgfpoint{\l__timechart_finish_x}{\l__timechart_content_top_y} } + \pgfusepath{discard} + } +\cs_new:Npn\__timechart_set_y_user:m #1 + { + \pgfmathsetmacro{\l__timechart_current_y}{#1} + } +\cs_new:Npn\__timechart_save_y_user: + { + \pgfmathsetmacro{\l__timechart_saved_y}{\l__timechart_current_y} + } +\cs_new:Npn\__timechart_reset_y_user: + { + \pgfmathsetmacro{\l__timechart_current_y}{\l__timechart_saved_y} + } +\cs_new:Npn\__timechart_set_y_minimum_auto_reset_user:m #1 + { + \pgfmathsetmacro{\l__timechart_auto_reset_minimum_y}{#1} + } +\cs_new:Npn\__timechart_set_y_maximum_auto_reset_user:m #1 + { + \pgfmathsetmacro{\l__timechart_auto_reset_maximum_y}{#1} + } +\NewDocumentCommand{\__timechart_step_y_user:O}{ O{1} } + { + \pgfmathsetmacro{\l__timechart_current_y} + {\l__timechart_current_y+#1*\l__timechart_ystep_pgf} + \__timechart_pgfmathsetbool:nn{\l__timechart_tmpa_bool} + { + or( + \l__timechart_current_y<\l__timechart_auto_reset_minimum_y, + \l__timechart_current_y>\l__timechart_auto_reset_maximum_y + ) + } + \bool_if:nT{\l__timechart_tmpa_bool} + { \pgfmathsetmacro{\l__timechart_current_y}{\l__timechart_saved_y)} } + } +\cs_new:Npn\__timechart_if_x_in_bounds:nT #1#2 + { + \__timechart_pgfmathsetbool:nn{\l__timechart_tmpa_bool}{ + and( + #1>=\l__timechart_start_x, + #1<=\l__timechart_finish_x + ) + } + \bool_if:NT\l__timechart_tmpa_bool + {#2} + } + +\cs_new:Npn\__timechart_if_x_in_tolerance_bounds:nT #1#2 + { + \__timechart_pgfmathsetbool:nn{\l__timechart_tmpa_bool}{ + and( + #1>=\l__timechart_start_tolerance_x, + #1<=\l__timechart_finish_tolerance_x + ) + } + \bool_if:NT\l__timechart_tmpa_bool{#2} + } +\cs_new:Npn\__timechart_if_x_range_intersect_tolerance_bounds:nnT #1#2#3 + { + \__timechart_pgfmathsetbool:nn{\l__timechart_tmpa_bool}{ + or( + or( + and( + #2>=\l__timechart_start_tolerance_x, + #2<=\l__timechart_finish_tolerance_x + ), + and( + #1>=\l__timechart_start_tolerance_x, + #1<=\l__timechart_finish_tolerance_x + ) + ), + and( + #1<\l__timechart_start_tolerance_x, + #2>\l__timechart_finish_tolerance_x + ) + ) + } + \bool_if:NT\l__timechart_tmpa_bool + {#3} + } +\cs_new:Npn\__timechart_parse_date_or_daterange:NNNNNn #1#2#3#4#5#6 +{ + \bool_set:Nn #1 {\__timechart_is_nondaterange_p:w #6/\q_stop} + \bool_set_inverse:N #1 + \bool_if:nTF #1 + { \__timechart_parse_range:w #2#3#4#5\q_mark #6\q_stop } + { + \__timechart_parse_date:NNn #2#3{#6} + \bool_set_eq:NN #4#2 + \pgfmathsetmacro{#5}{#3} + } +} +\cs_new:Npn\__timechart_is_nondaterange_p:w #1/#2\q_stop +{ + \tl_if_empty_p:n{#2} +} +\cs_new:Npn\__timechart_parse_range:w #1#2#3#4\q_mark #5/#6\q_stop +{ + \__timechart_parse_date:NNn #1#2{#5} + \__timechart_parse_date:NNn #3#4{#6} +} +\cs_new:Npn\__timechart_parse_date:NNn #1#2#3 +{ + \bool_set:Nn #1 { \__timechart_is_circa_p:w #3c\q_stop } + \bool_if:NTF #1 + { \__timechart_parse_circa_date:w #2\q_mark #3\q_stop } + { \__timechart_parse_noncirca_date:Nn #2{#3} } +} +\cs_new:Npn\__timechart_is_circa_p:w #1c#2\q_stop +{ + \tl_if_empty_p:n{#1} +} +\cs_new:Npn\__timechart_parse_circa_date:w #1\q_mark c#2\q_stop +{ + \__timechart_parse_noncirca_date:Nn #1{#2} +} +\cs_new:Npn\__timechart_parse_noncirca_date:Nn #1#2 +{ + \bool_if:nTF { \__timechart_is_before_p:w #2-\q_stop } + { \__timechart_parse_before_date:w #1\q_mark #2\q_stop } + { \__timechart_parse_signed_date:w #1\q_mark #2-0-0\q_stop } +} +\cs_new:Npn\__timechart_is_before_p:w #1-#2\q_stop +{ + \tl_if_empty_p:n{#1} +} +\cs_new:Npn\__timechart_parse_before_date:w #1\q_mark-#2\q_stop +{ + \__timechart_parse_signed_date:w #1-\q_mark #2-0-0\q_stop +} +\cs_new:cpn{c__timechart_year_days_pgf}{365} +\cs_new:cpn{c__timechart_month_days_1_pgf}{31} +\cs_new:cpn{c__timechart_month_days_2_pgf}{28} +\cs_new:cpn{c__timechart_month_days_3_pgf}{31} +\cs_new:cpn{c__timechart_month_days_4_pgf}{30} +\cs_new:cpn{c__timechart_month_days_5_pgf}{31} +\cs_new:cpn{c__timechart_month_days_6_pgf}{30} +\cs_new:cpn{c__timechart_month_days_7_pgf}{31} +\cs_new:cpn{c__timechart_month_days_8_pgf}{31} +\cs_new:cpn{c__timechart_month_days_9_pgf}{30} +\cs_new:cpn{c__timechart_month_days_10_pgf}{31} +\cs_new:cpn{c__timechart_month_days_11_pgf}{30} +\cs_new:cpn{c__timechart_month_days_12_pgf}{31} +\cs_new:cpn{c__timechart_cumulative_days_1_pgf}{0} +\cs_new:cpn{c__timechart_cumulative_days_2_pgf}{31} +\cs_new:cpn{c__timechart_cumulative_days_3_pgf}{59} +\cs_new:cpn{c__timechart_cumulative_days_4_pgf}{90} +\cs_new:cpn{c__timechart_cumulative_days_5_pgf}{120} +\cs_new:cpn{c__timechart_cumulative_days_6_pgf}{151} +\cs_new:cpn{c__timechart_cumulative_days_7_pgf}{181} +\cs_new:cpn{c__timechart_cumulative_days_8_pgf}{212} +\cs_new:cpn{c__timechart_cumulative_days_9_pgf}{243} +\cs_new:cpn{c__timechart_cumulative_days_10_pgf}{273} +\cs_new:cpn{c__timechart_cumulative_days_11_pgf}{304} +\cs_new:cpn{c__timechart_cumulative_days_12_pgf}{334} +\cs_new:Npn\__timechart_parse_signed_date:w #1#2\q_mark #3-#4-#5\q_stop +{ + \pgfmathtruncatemacro{\__timechart_parsed_year_pgf}{#2#3} + \pgfmathtruncatemacro{\__timechart_parsed_month_pgf}{#4} + \pgfmathtruncatemacro{\__timechart_parsed_day_pgf}{#5} + \__timechart_pgfmathsetbool:nn{\l_tmpa_bool}{ + or( + \__timechart_parsed_month_pgf < 1, + \__timechart_parsed_month_pgf > 12, + ) + } + \bool_if:NTF\l_tmpa_bool + { + \pgfmathsetmacro{#1}{#2#3} + } + { + \cs_set_eq:NN\l__timechart_year_days_pgf\c__timechart_year_days_pgf + \cs_set_eq:Nc\l__timechart_month_days_pgf + { c__timechart_month_days_\__timechart_parsed_month_pgf _pgf } + \cs_set_eq:Nc\l__timechart_cumulative_days_pgf + { c__timechart_cumulative_days_\__timechart_parsed_month_pgf _pgf } + \__timechart_pgfmathsetbool:nn{\l_tmpa_bool}{ + or( + Mod(\__timechart_parsed_year_pgf,400) == 0, + and( + Mod(\__timechart_parsed_year_pgf,4) == 0, + Mod(\__timechart_parsed_year_pgf,100) != 0 + ) + ) + } + \bool_if:NT\l_tmpa_bool + { + \pgfmathsetmacro{\l__timechart_year_days_pgf} + { \l__timechart_year_days_pgf+1 } + \__timechart_pgfmathsetbool:nn{\l_tmpb_bool} + { \__timechart_parsed_month == 1 } + \bool_if:NF\l_tmpb_bool + { + \__timechart_pgfmathsetbool:nn{\l_tmpb_bool} + { \__timechart_parsed_month == 2 } + \bool_if:NF\l_tmpb_bool + { + \pgfmathsetmacro{\l__timechart_month_days_pgf} + { \l__timechart_month_days_pgf + 1 } + } + { + \pgfmathsetmacro{\l__timechart_cumulative_days_pgf} + { \l__timechart_cumulative_days_pgf + 1 } + } + } + } + \__timechart_pgfmathsetbool:nn{\l_tmpa_bool}{ + or( + \__timechart_parsed_day_pgf < 1, + \__timechart_parsed_day_pgf > \l__timechart_month_days_pgf, + ) + } + \bool_if:NTF\l_tmpa_bool + { + \pgfmathsetmacro{#1} + { + #2#3 + + \l__timechart_cumulative_days_pgf/\l__timechart_year_days_pgf + } + } + { + \pgfmathsetmacro{#1} + { + #2#3 + + ( + \l__timechart_cumulative_days_pgf + + \__timechart_parsed_day_pgf + )/\l__timechart_year_days_pgf + } + } + } +} +\bool_new:N\l__timechart_start_is_range_bool +\bool_new:N\l__timechart_startmin_circa_bool +\bool_new:N\l__timechart_startmax_circa_bool +\bool_new:N\l__timechart_finish_is_range_bool +\bool_new:N\l__timechart_finishmin_circa_bool +\bool_new:N\l__timechart_finishmax_circa_bool +\msg_new:nnn{timechart}{interval_dates_invalid} + { Invalid~interval~dates:~#1~to~#2 } +\NewDocumentCommand{\__timechart_interval_user:Ommm}{ O{} m m m } + { + \group_begin: + \__timechart_parse_date_or_daterange:NNNNNn + \l__timechart_start_is_range_bool + \l__timechart_startmin_circa_bool\l__timechart_startmin_year + \l__timechart_startmax_circa_bool\l__timechart_startmax_year + {#2} + \__timechart_parse_date_or_daterange:NNNNNn + \l__timechart_finish_is_range_bool + \l__timechart_finishmin_circa_bool\l__timechart_finishmin_year + \l__timechart_finishmax_circa_bool\l__timechart_finishmax_year + {#3} + \__timechart_pgfmathsetbool:nn{\l_tmpa_bool}{ + and( + \l__timechart_startmin_year <= \l__timechart_startmax_year, + and( + \l__timechart_startmax_year <= \l__timechart_finishmin_year, + \l__timechart_finishmin_year <= \l__timechart_finishmax_year + ) + ) + } + \bool_if:NTF\l_tmpa_bool + { + \__timechart_interval_checked:nn{#1}{#4} + \group_end: + \bool_if:NT\l__timechart_autostep_bool + { \__timechart_step_y_user:O } + } + { + \msg_error:nnnn{timechart}{interval_dates_invalid}{#2}{#3} + \group_end: + } + } +\cs_new:Npn \__timechart_interval_checked:nn #1#2 + { + \pgfqkeys{/timechart}{ + #1, + circa~uncertainty/.get=\l__timechart_circa_uncertainty_year + } + \bool_if:NTF\l__timechart_startmin_circa_bool + { + \pgfmathsetmacro{\l__timechart_start_extreme_x} + { yeartox(\l__timechart_startmin_year + - \l__timechart_circa_uncertainty_year) } + } + { + \pgfmathsetmacro{\l__timechart_start_extreme_x} + { yeartox(\l__timechart_startmin_year) } + } + \bool_if:NTF\l__timechart_finishmax_circa_bool + { + \pgfmathsetmacro{\l__timechart_finish_extreme_x} + { yeartox(\l__timechart_finishmax_year + + \l__timechart_circa_uncertainty_year) } + } + { + \pgfmathsetmacro{\l__timechart_finish_extreme_x} + { yeartox(\l__timechart_finishmax_year) } + } + \__timechart_if_x_range_intersect_tolerance_bounds:nnT + {\l__timechart_start_extreme_x}{\l__timechart_finish_extreme_x} + { \__timechart_draw_visible_interval:nn{#1}{#2} } + } +\cs_new:Npn\__timechart_draw_visible_interval:nn #1#2 + { + \pgfqkeys{/timechart}{ + ref/.get=\l__timechart_ref_text, + mark/.get=\l__timechart_mark_text, + interval~minimum~width/.get=\l__timechart_minimum_width_pgf, + interval~bar~color/.get=\l__timechart_bar_color, + interval~bar~thickness/.get=\l__timechart_bar_thickness_pgf, + interval~mark~color/.get=\l__timechart_mark_color, + interval~label~baseline/.get=\l__timechart_text_baseline_pgf, + interval~label~node~name/.get=\l__timechart_interval_label_node_name, + } + \pgfmathsetlengthmacro{\l__timechart_bar_half_thickness_pgf} + {.5*\l__timechart_bar_thickness_pgf} + \cs_set:Npn\l__timechart_label_text{#2} + \bool_if:NTF\l__timechart_startmax_circa_bool + { + \pgfmathsetmacro{\l__timechart_start_definite_x} + { yeartox(\l__timechart_startmax_year + + \l__timechart_circa_uncertainty_year) } + } + { + \pgfmathsetmacro{\l__timechart_start_definite_x} + { yeartox(\l__timechart_startmax_year) } + } + \bool_if:NTF\l__timechart_finishmin_circa_bool + { + \pgfmathsetmacro{\l__timechart_finish_definite_x} + { yeartox(\l__timechart_finishmin_year + - \l__timechart_circa_uncertainty_year) } + } + { + \pgfmathsetmacro{\l__timechart_finish_definite_x} + { yeartox(\l__timechart_finishmin_year) } + } + \__timechart_pgfmathsetbool:nn{\l__timechart_tmpa_bool} + { + \l__timechart_start_definite_x > \l__timechart_finish_definite_x + } + \bool_if:NT\l__timechart_tmpa_bool + { + \pgfmathsetmacro{\l__timechart_start_definite_x} + { + .5*( + \l__timechart_start_definite_x + + \l__timechart_finish_definite_x + ) + } + \pgfmathsetmacro{\l__timechart_finish_definite_x} + { \l__timechart_start_definite_x } + } + \__timechart_pgfmathsetbool:nn{\l__timechart_tmpa_bool} + { + (\l__timechart_finish_extreme_x-\l__timechart_start_extreme_x) + < \l__timechart_minimum_width_pgf + } + \int_if_zero:nTF + { + \l__timechart_start_range_type_int + + \l__timechart_finish_range_type_int + } + { + \__timechart_pgfmathsetbool:nn{\l__timechart_tmpb_bool} + { + ( + \l__timechart_finish_definite_x + - \l__timechart_start_definite_x + ) + < \l__timechart_minimum_width_pgf + } + } + { \bool_set_false:N\l__timechart_tmpb_bool } + \bool_if:nT{ + \l__timechart_tmpa_bool || \l__timechart_tmpb_bool + }{ + \pgfmathsetmacro{\l__timechart_width_adjust} + { .5*\l__timechart_minimum_width_pgf } + \pgfmathsetmacro{\l__timechart_start_definite_x} + { \l__timechart_start_definite_x-\l__timechart_width_adjust } + \pgfmathsetmacro{\l__timechart_finish_definite_x} + { \l__timechart_finish_definite_x+\l__timechart_width_adjust } + \pgfmathsetmacro{\l__timechart_start_extreme_x} + { + min( + \l__timechart_start_extreme_x, + \l__timechart_start_definite_x + ) + } + \pgfmathsetmacro{\l__timechart_finish_extreme_x} + { + max( + \l__timechart_finish_extreme_x, + \l__timechart_finish_definite_x + ) + } + } + \pgfmathsetmacro{\l__timechart_start_solid_x} + { \l__timechart_start_definite_x } + \pgfmathsetmacro{\l__timechart_finish_solid_x} + { \l__timechart_finish_definite_x } + \pgftransformshift{ \pgfpoint{0}{\l__timechart_current_y} } + \pgfscope + \__timechart_interval_beyond_clip: + \int_case:nn {\l__timechart_start_range_type_int} + { + {0}{ \__timechart_interval_start_range_fade: } + {1}{ \__timechart_interval_start_range_slant: } + } + \int_case:nn {\l__timechart_finish_range_type_int} + { + {0}{ \__timechart_interval_finish_range_fade: } + {1}{ \__timechart_interval_finish_range_slant: } + } + \__timechart_interval_draw_solid: + \__timechart_interval_mark: + \__timechart_interval_define_bar_node: + \endpgfscope + \__timechart_interval_label: + } +\cs_new:Npn\__timechart_interval_beyond_clip: + { + \__timechart_pgfmathsetbool:nn{\l__timechart_tmpa_bool} + {\l__timechart_start_extreme_x<=\l__timechart_start_tolerance_x} + \__timechart_pgfmathsetbool:nn{\l__timechart_tmpb_bool} + {\l__timechart_finish_extreme_x>=\l__timechart_finish_tolerance_x} + \bool_if:nT{ \l__timechart_tmpa_bool || \l__timechart_tmpb_bool } + { \__timechart_interval_beyond_clip_aux: } + } +\cs_new:Npn\__timechart_interval_beyond_clip_aux: + { + \pgfinterruptboundingbox + \bool_if:NTF\l__timechart_tmpb_bool + { + \pgfpathmoveto{ + \pgfpoint + {\l__timechart_finish_beyond_x} + {\l__timechart_bar_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l__timechart_finish_beyond_x} + {\l__timechart_bar_half_thickness_pgf} + } + \pgfpatharc + {90} + {270} + {\l__timechart_beyond_x_radius_pgf + ~and~\l__timechart_bar_half_thickness_pgf} + \pgfpathlineto{ + \pgfpoint + {\l__timechart_finish_beyond_x} + {-\l__timechart_bar_thickness_pgf} + } + } + { + \pgfpathmoveto{ + \pgfpoint + {\l__timechart_right_nonclip_x} + {\l__timechart_bar_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l__timechart_right_nonclip_x} + {-\l__timechart_bar_thickness_pgf} + } + } + \bool_if:NTF\l__timechart_tmpa_bool + { + \pgfpathlineto{ + \pgfpoint + {\l__timechart_start_beyond_x} + {-\l__timechart_bar_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l__timechart_start_beyond_x} + {-\l__timechart_bar_half_thickness_pgf} + } + \pgfpatharc + {-90} + {90} + {\l__timechart_beyond_x_radius_pgf + ~and~\l__timechart_bar_half_thickness_pgf} + \pgfpathlineto{ + \pgfpoint + {\l__timechart_start_beyond_x} + {\l__timechart_bar_thickness_pgf} + } + } + { + \pgfpathlineto{ + \pgfpoint + {\l__timechart_left_nonclip_x} + {-\l__timechart_bar_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l__timechart_left_nonclip_x} + {\l__timechart_bar_thickness_pgf} + } + } + \pgfpathclose + \pgfusepath{clip} + \endpgfinterruptboundingbox + } +\pgfdeclarehorizontalshading{__timechart_fade_in_shading}{4bp}{ + color(0bp)=(transparent!100); + color(1bp)=(transparent!100); + color(3bp)=(transparent!0); + color(4bp)=(transparent!0) +} +\pgfdeclarefading + {__timechart_fade_in} + {\pgfuseshading{__timechart_fade_in_shading}} +\cs_new:Npn\__timechart_interval_start_range_fade: + { + \pgfmathsetlengthmacro{\l__timechart_fade_width_pgf} + { \l__timechart_start_solid_x-\l__timechart_start_extreme_x } + \__timechart_pgfmathsetbool:nn{\l__timechart_tmpa_bool} + { \l__timechart_fade_width_pgf + > \pgfkeysvalueof{/timechart/fade~minimum~width} } + \bool_if:NT \l__timechart_tmpa_bool + { + \__timechart_if_x_range_intersect_tolerance_bounds:nnT + {\l__timechart_start_extreme_x}{\l__timechart_start_solid_x} + { + \pgfmathsetmacro{\l__timechart_fade_extra_pgf} + { + min( + .49*\l__timechart_fade_width_pgf, + \l__timechart_finish_solid_x-\l__timechart_start_solid_x + ) + } + \pgfscope + \pgfpathrectanglecorners{ + \pgfpoint + {\l__timechart_start_extreme_x} + {-\l__timechart_bar_half_thickness_pgf} + }{ + \pgfpoint + {\l__timechart_start_solid_x+\l__timechart_fade_extra_pgf} + {\l__timechart_bar_half_thickness_pgf} + } + \pgfgettransform{\__timechart_transform_current} + \pgfsetfading{__timechart_fade_in}{ + \pgfsettransform{\__timechart_transform_current} + \pgftransformshift{ + \pgfpoint + { + .5*\l__timechart_start_extreme_x + +.5*\l__timechart_start_solid_x + } + {0} + } + \pgftransformxscale{ + (\l__timechart_start_solid_x-\l__timechart_start_extreme_x) + /2bp + } + \pgftransformyscale{\l__timechart_bar_thickness_pgf/4bp} + } + \pgfsetfillcolor{\l__timechart_bar_color} + \pgfusepath{fill} + \endpgfscope + } + } + } +\pgfdeclarehorizontalshading{__timechart_fade_out_shading}{4bp}{ + color(0bp)=(transparent!0); + color(1bp)=(transparent!0); + color(3bp)=(transparent!100); + color(4bp)=(transparent!100) +} +\pgfdeclarefading + {__timechart_fade_out} + {\pgfuseshading{__timechart_fade_out_shading}} +\cs_new:Npn\__timechart_interval_finish_range_fade: + { + \pgfmathsetlengthmacro{\l__timechart_fade_width_pgf} + { \l__timechart_finish_extreme_x-\l__timechart_finish_solid_x } + \__timechart_pgfmathsetbool:nn{\l__timechart_tmpa_bool} + { \l__timechart_fade_width_pgf + > \pgfkeysvalueof{/timechart/fade~minimum~width} } + \bool_if:NT \l__timechart_tmpa_bool + { + \__timechart_if_x_range_intersect_tolerance_bounds:nnT + {\l__timechart_finish_solid_x}{\l__timechart_finish_extreme_x} + { + \pgfmathsetmacro{\l__timechart_fade_extra_pgf} + { + min( + .49*\l__timechart_fade_width_pgf, + \l__timechart_finish_solid_x-\l__timechart_start_solid_x + ) + } + \pgfscope + \pgfpathrectanglecorners{ + \pgfpoint + {\l__timechart_finish_solid_x-\l__timechart_fade_extra_pgf} + {-\l__timechart_bar_half_thickness_pgf} + }{ + \pgfpoint + {\l__timechart_finish_extreme_x} + {\l__timechart_bar_half_thickness_pgf} + } + \pgfgettransform{\__timechart_transform_current} + \pgfsetfading{__timechart_fade_out}{ + \pgfsettransform{\__timechart_transform_current} + \pgftransformshift{ + \pgfpoint{ + .5*\l__timechart_finish_solid_x + +.5*\l__timechart_finish_extreme_x + } + {0} + } + \pgftransformxscale{ + (\l__timechart_finish_extreme_x-\l__timechart_finish_solid_x) + /2bp + } + \pgftransformyscale{\l__timechart_bar_thickness_pgf/4bp} + } + \pgfsetfillcolor{\l__timechart_bar_color} + \pgfusepath{fill} + \endpgfscope + } + } + } +\cs_new:Npn\__timechart_interval_start_range_slant: + { + \__timechart_if_equal:nnF + {\l__timechart_start_extreme_x}{\l__timechart_start_solid_x} + { + \__timechart_if_x_range_intersect_tolerance_bounds:nnT + {\l__timechart_start_extreme_x}{\l__timechart_start_solid_x} + { + \pgfinterruptboundingbox + \pgfpathmoveto{ + \pgfpoint + {\l__timechart_start_extreme_x} + {-\l__timechart_bar_half_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l__timechart_start_solid_x} + {\l__timechart_bar_half_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l__timechart_start_solid_x} + {\l__timechart_bar_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l__timechart_right_nonclip_x} + {\l__timechart_bar_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l__timechart_right_nonclip_x} + {-\l__timechart_bar_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l__timechart_start_extreme_x} + {-\l__timechart_bar_thickness_pgf} + } + \pgfpathclose + \pgfusepath{clip} + \endpgfinterruptboundingbox + \pgfmathsetmacro{\l__timechart_start_solid_x} + {\l__timechart_start_extreme_x} + } + } + } +\cs_new:Npn\__timechart_interval_finish_range_slant: + { + \__timechart_if_equal:nnF + {\l__timechart_finish_solid_x}{\l__timechart_finish_extreme_x} + { + \__timechart_if_x_range_intersect_tolerance_bounds:nnT + {\l__timechart_finish_solid_x}{\l__timechart_finish_extreme_x} + { + \pgfinterruptboundingbox + \pgfpathmoveto{ + \pgfpoint + {\l__timechart_finish_solid_x} + {-\l__timechart_bar_half_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l__timechart_finish_extreme_x} + {\l__timechart_bar_half_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l__timechart_finish_extreme_x} + {\l__timechart_bar_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l__timechart_left_nonclip_x} + {\l__timechart_bar_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l__timechart_left_nonclip_x} + {-\l__timechart_bar_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l__timechart_finish_solid_x} + {-\l__timechart_bar_thickness_pgf} + } + \pgfpathclose + \pgfusepath{clip} + \endpgfinterruptboundingbox + \pgfmathsetmacro{\l__timechart_finish_solid_x} + {\l__timechart_finish_extreme_x} + } + } + } +\cs_new:Npn\__timechart_interval_draw_solid: + { + \__timechart_if_x_range_intersect_tolerance_bounds:nnT + {\l__timechart_start_solid_x} + {\l__timechart_finish_solid_x} + { + \pgfpathrectanglecorners{ + \pgfpoint + {\l__timechart_start_solid_x} + {-\l__timechart_bar_half_thickness_pgf} + }{ + \pgfpoint + {\l__timechart_finish_solid_x} + {\l__timechart_bar_half_thickness_pgf} + } + \pgfsetfillcolor{\l__timechart_bar_color} + \pgfusepath{fill} + } + } +\msg_new:nnn{timechart}{interval_mark_outside} + { Attempt~to~mark~outside~interval~at~date~#1 } +\cs_new:Npn\__timechart_interval_mark: + { + \pgfscope + \pgfsetstrokecolor{\l__timechart_mark_color} + \foreach \year in \l__timechart_mark_text + { + \pgfmathsetmacro{\l__timechart_mark_x}{yeartox(\year)} + \__timechart_pgfmathsetbool:nn{\l__timechart_tmpa_bool} + { + or ( + \l__timechart_mark_x < \l__timechart_start_extreme_x, + \l__timechart_mark_x > \l__timechart_finish_extreme_x + ) + } + \bool_if:NT\l__timechart_tmpa_bool + { \msg_error:nne{timechart}{interval_mark_outside}{\year} } + \pgfpathmoveto{ + \pgfpoint + {\l__timechart_mark_x} + {-\l__timechart_bar_half_thickness_pgf} + } + \pgfpathlineto{ + \pgfpoint + {\l__timechart_mark_x} + {\l__timechart_bar_half_thickness_pgf} + } + \pgfusepath{draw} + } + \endpgfscope + } +\cs_new:Npn\__timechart_interval_define_bar_node: + { + \__timechart_make_rectangle_node:nnnn + { + \pgfpoint + {\l__timechart_start_extreme_x} + {-\l__timechart_bar_half_thickness_pgf} + }{ + \pgfpoint + {\l__timechart_finish_extreme_x} + {\l__timechart_bar_half_thickness_pgf} + } + {\pgfkeysvalueof{/timechart/interval~bar~node~name}} + {\c_false_bool} + } +\cs_new:Npn\__timechart_interval_label: + { + \str_if_empty:NF \l__timechart_label_text + { + \pgfinterruptboundingbox + \int_case:nn {\l__timechart_label_pos_int} + { + {0}{ \__timechart_interval_label_left: } + {1}{ \__timechart_interval_label_center: } + {2}{ \__timechart_interval_label_right: } + } + \endpgfinterruptboundingbox + } + } +\cs_new:Npn\__timechart_interval_label_left: + { + \__timechart_if_x_in_bounds:nT{\l__timechart_start_extreme_x} + { + \group_begin: + \pgftransformshift{ + \pgfpoint + {\l__timechart_start_extreme_x} + {\l__timechart_text_baseline_pgf} + } + \node[/timechart/interval~label,anchor=base~east] + (\l__timechart_interval_label_node_name) + at (0,0) + { + \__timechart_make_ref:NN + \l__timechart_ref_text + \l__timechart_label_text + }; + \group_end: + } + } +\cs_new:Npn\__timechart_interval_label_right: + { + \__timechart_if_x_in_bounds:nT{\l__timechart_finish_extreme_x} + { + \group_begin: + \pgftransformshift{ + \pgfpoint + {\l__timechart_finish_extreme_x} + {\l__timechart_text_baseline_pgf} + } + \node[/timechart/interval~label,anchor=base~west] + (\l__timechart_interval_label_node_name) + at (0,0) + { + \__timechart_make_ref:NN + \l__timechart_ref_text + \l__timechart_label_text + }; + \group_end: + } + } +\cs_new:Npn\__timechart_interval_label_center: + { + \pgfmathsetlengthmacro{\l__timechart_label_anchor_x} + { + .5*max(\l__timechart_start_definite_x,\l__timechart_start_x) + + .5*min(\l__timechart_finish_definite_x,\l__timechart_finish_x) + } + \group_begin: + \pgftransformshift{ + \pgfpoint + {\l__timechart_label_anchor_x} + {\l__timechart_text_baseline_pgf} + } + \pgfonlayer{labelbg} + \node[ + /timechart/interval~label~centered~background, + anchor=base + ] + at (0,0) + { \l__timechart_label_text }; + \endpgfonlayer + \group_end: + \pgfscope + \pgfpathrectanglecorners{ + \pgfpoint + {\l__timechart_start_extreme_x} + {-\l__timechart_bar_half_thickness_pgf} + }{ + \pgfpoint + {\l__timechart_finish_extreme_x} + {\l__timechart_bar_half_thickness_pgf} + } + \pgfusepath{clip} + \group_begin: + \pgftransformshift{ + \pgfpoint + {\l__timechart_label_anchor_x} + {\l__timechart_text_baseline_pgf} + } + \node[ + /timechart/interval~label~centered, + anchor=base + ] + (\l__timechart_interval_label_node_name) + at (0,0) + { + \__timechart_make_ref:NN + \l__timechart_ref_text + \l__timechart_label_text + }; + \group_end: + \endpgfscope + } +\NewDocumentCommand{\__timechart_text_user:Omm}{ O{} m m } + { + \str_if_empty:nF{#3}{ + \group_begin: + \pgfmathsetmacro{\l__timechart_text_x}{yeartox(#2)} + \__timechart_if_x_in_tolerance_bounds:nT{\l__timechart_text_x} + { + \pgfqkeys{/timechart}{ + #1, + ref/.get=\l__timechart_ref_text, + text~node~name/.get=\l__timechart_node_name_text, + text~baseline/.get=\l__timechart_text_baseline_pgf, + } + \pgftransformshift{ + \pgfpoint{0}{\l__timechart_current_y} + } + \pgfinterruptboundingbox + \group_begin: + \pgftransformshift{ + \pgfpoint{\l__timechart_text_x}{\l__timechart_text_baseline_pgf} + } + \cs_set:Npn\l__timechart_text{#3} + \int_case:nn {\l__timechart_text_pos_int} + { + {0}{ \cs_set:Npn\l__timechart_node_anchor_text{base~east} } + {1}{ \cs_set:Npn\l__timechart_node_anchor_text{base} } + {2}{ \cs_set:Npn\l__timechart_node_anchor_text{base~west} } + } + \node[/timechart/text,anchor=\l__timechart_node_anchor_text] + (\l__timechart_node_name_text) + at (0,0) + { + \__timechart_make_ref:NN + \l__timechart_ref_text + \l__timechart_text + }; + \group_end: + \endpgfinterruptboundingbox + } + \group_end: + } + \__timechart_space_user:O[#1] + } +\NewDocumentCommand{\__timechart_space_user:O}{ O{} } + { + \group_begin: + + \pgfqkeys{/timechart}{ + #1, + interval~bar~thickness/.get=\l__timechart_bar_thickness_pgf, + } + \pgfmathsetlengthmacro{\l__timechart_bar_half_thickness_pgf} + { .5*\l__timechart_bar_thickness_pgf } + \pgftransformshift{ + \pgfpoint{0}{\l__timechart_current_y} + } + \pgfpathmoveto{ + \pgfpoint{0}{-\l__timechart_bar_half_thickness_pgf} + } + \pgfpathmoveto{ + \pgfpoint{0}{\l__timechart_bar_half_thickness_pgf} + } + \pgfusepath{discard} + \group_end: + \bool_if:NT\l__timechart_autostep_bool{ + \__timechart_step_y_user:O + } + } +\NewDocumentCommand{\timechartlegenditem}{ O{} } + { + \__timechart_legend_aux:nn{#1}{ + \pgfmathsetlengthmacro{\l__timechart_start_solid_x}{0} + \pgfmathsetlengthmacro{\l__timechart_finish_solid_x} + {\l__timechart_legenditem_width_pgf} + \__timechart_interval_draw_solid: + } + } +\NewDocumentCommand{\timechartlegendstartrange}{ O{} } + { + \__timechart_legend_aux:nn{#1}{ + \pgfmathsetlengthmacro{\l__timechart_start_extreme_x}{0} + \pgfmathsetlengthmacro{\l__timechart_start_solid_x} + {\pgfkeysvalueof{/timechart/legend~item~range~width}} + \pgfmathsetlengthmacro{\l__timechart_finish_solid_x} + {\l__timechart_legenditem_width_pgf} + \int_case:nn {\l__timechart_start_range_type_int} + { + {0}{ \__timechart_interval_start_range_fade: } + {1}{ \__timechart_interval_start_range_slant: } + } + \__timechart_interval_draw_solid: + } + } +\NewDocumentCommand{\timechartlegendfinishrange}{ O{} } + { + \__timechart_legend_aux:nn{#1}{ + \pgfmathsetlengthmacro{\l__timechart_start_solid_x}{0} + \pgfmathsetlengthmacro{\l__timechart_finish_solid_x} + { + \l__timechart_legenditem_width_pgf + -\pgfkeysvalueof{/timechart/legend~item~range~width} + } + \pgfmathsetlengthmacro{\l__timechart_finish_extreme_x} + {\l__timechart_legenditem_width_pgf} + \int_case:nn {\l__timechart_finish_range_type_int} + { + {0}{ \__timechart_interval_finish_range_fade: } + {1}{ \__timechart_interval_finish_range_slant: } + } + \__timechart_interval_draw_solid: + } + } +\cs_new:Npn\__timechart_legend_aux:nn #1#2 + { + \tikzpicture + \pgfkeys{ + /timechart/.cd, + #1, + interval~bar~thickness/.get=\l__timechart_bar_thickness_pgf, + interval~bar~color/.get=\l__timechart_bar_color, + interval~minimum~width/.get=\l__timechart_minimum_width_pgf, + beyond~length/.get=\l__timechart_beyond_length_pgf, + legend~item~width/.get=\l__timechart_legenditem_width_pgf, + } + \pgfmathsetlengthmacro{\l__timechart_bar_half_thickness_pgf} + {.5*\l__timechart_bar_thickness_pgf} + \pgfmathsetmacro{\l__timechart_start_x}{0} + \pgfmathsetmacro{\l__timechart_finish_x} + {\l__timechart_legenditem_width_pgf} + \pgfmathsetlengthmacro{\l__timechart_start_beyond_x} + {\l__timechart_start_x-\l__timechart_beyond_length_pgf} + \pgfmathsetlengthmacro{\l__timechart_finish_beyond_x} + {\l__timechart_finish_x+\l__timechart_beyond_length_pgf} + \pgfmathsetmacro{\l__timechart_current_y}{0} + \pgfmathsetmacro{\l__timechart_start_tolerance_x} + {\l__timechart_start_x-10mm} + \pgfmathsetmacro{\l__timechart_finish_tolerance_x} + {\l__timechart_finish_x+10mm} + #2 + \pgfresetboundingbox + \pgfmoveto{ + \pgfpoint{0}{-\l__timechart_bar_half_thickness_pgf} + } + \pgfmoveto{ + \pgfpoint + {\l__timechart_legenditem_width_pgf} + {\l__timechart_bar_half_thickness_pgf} + } + \endtikzpicture% + } diff --git a/Master/tlpkg/bin/tlpkg-ctan-check b/Master/tlpkg/bin/tlpkg-ctan-check index 323561b7801..e6a15e38bea 100755 --- a/Master/tlpkg/bin/tlpkg-ctan-check +++ b/Master/tlpkg/bin/tlpkg-ctan-check @@ -878,7 +878,7 @@ my @TLP_working = qw( tikzpackets tikzpagenodes tikzpeople tikzpfeile tikzpingus tikzposter tikzquads tikzquests tikzscale tikzsymbols tikztosvg tikzviolinplots tile-graphic tilings - timbreicmc timeop times timetable timing-diagrams + timbreicmc timechart timeop times timetable timing-diagrams tinos tipa tipa-de tipauni tipfr tiscreen titlecaps titlefoot titlepages titlepic titleref titlesec titling tkz-base tkz-berge tkz-bernoulli tkz-doc tkz-elements tkz-euclide diff --git a/Master/tlpkg/tlpsrc/collection-pictures.tlpsrc b/Master/tlpkg/tlpsrc/collection-pictures.tlpsrc index 2d0e057812e..abe6c620ee4 100644 --- a/Master/tlpkg/tlpsrc/collection-pictures.tlpsrc +++ b/Master/tlpkg/tlpsrc/collection-pictures.tlpsrc @@ -240,6 +240,7 @@ depend tikztosvg depend tikzviolinplots depend tile-graphic depend tilings +depend timechart depend timing-diagrams depend tipfr depend tkz-base diff --git a/Master/tlpkg/tlpsrc/timechart.tlpsrc b/Master/tlpkg/tlpsrc/timechart.tlpsrc new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/Master/tlpkg/tlpsrc/timechart.tlpsrc |