% \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} % % \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 % % \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 % % \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} % % \end{macrocode} % % \end{implementation} % % \PrintIndex