3Implementation¶
3.1Packages¶
pgfgantt is modest in terms of dependencies: It only requires the TikZ and pgfcalendar packages.
\RequirePackage{tikz}
\usetikzlibrary{%
arrows, backgrounds, calc,%
patterns, positioning, shapes.geometric%
}
\RequirePackage{pgfcalendar}
3.2Macros for Key and Error Management¶
\@gtt@ifstar ¶
\@gtt@ifstar reimplements the LaTeX2ε kernel’s \@ifstar macro. This makes it robust to amsgen’s reimplementation of \@ifstar.
\def\@gtt@ifstar#1{\kernel@ifnextchar*{\@firstoftwo{#1}}}
\ganttset
\ganttset changes the current key path to /pgfgantt/ and then executes the keys in its mandatory argument.
\def\ganttset#1{\pgfqkeys{/pgfgantt}{#1}}
\@gtt@keydef ¶
The following auxiliary macros save us some code when we devise keys later on. \@gtt@keydef{⟨key⟩}{⟨initial value⟩} declares the key /pgfgantt/⟨key⟩ and stores its ⟨initial value⟩.
\def\@gtt@keydef#1#2{%
\pgfkeyssetvalue{/pgfgantt/#1}{#2}%
}
\ganttvalueof
\ganttvalueof{⟨key⟩} retrieves the value stored by a ⟨key⟩. Link type authors should be able to use this macro in their code; thus, it lacks any @s.
\def\ganttvalueof#1{%
\pgfkeysvalueof{/pgfgantt/#1}%
}
\@gtt@stylekeydef ¶
\@gtt@stylekeydef{⟨key⟩}{⟨initial style⟩} declares a style ⟨key⟩ with an ⟨initial style⟩.
\def\@gtt@stylekeydef#1#2{%
\pgfkeys{/pgfgantt/#1/.style={#2}}%
}
\@gtt@PackageError ¶
\@gtt@PackageWarning ¶
\@gtt@PackageError{⟨message⟩} and \@gtt@PackageWarning{⟨message⟩} issue a package error or warning ⟨message⟩, respectively.
\def\@gtt@PackageError#1{%
\PackageError{pgfgantt}{#1}{}%
}
\def\@gtt@PackageWarning#1{%
\PackageWarning{pgfgantt}{#1}%
}
3.3The Horizontal and Vertical Grid¶
\gtt @currentline ¶
\gtt @lasttitleline ¶
\gtt @currgrid ¶
\gtt @chartwidth ¶
The count register \gtt@currentline holds the current line; it starts from 0 and decreases. \gtt@lasttitleline equals the line of the title element drawn last. \gtt@currgrid is the index of the current grid line drawn. \gtt@chartwidth equals the number of time slots.
\newcount\gtt@currentline
\newcount\gtt@lasttitleline
\newcount\gtt@currgrid
\newcount\gtt@chartwidth
hgrid
hgrid style
\ifgtt @hgrid ¶
\gtt @hgridstyle ¶
hgrid checks whether its value is false and sets the boolean \ifgtt@hgrid accordingly. If the value is true or missing, horizontal grid lines appear dotted.
\@gtt@stylekeydef{hgrid style}{dotted}
\newif\ifgtt@hgrid
\ganttset{%
hgrid/.code={%
\def\@tempa{#1}%
\def\@tempb{false}%
\ifx\@tempa\@tempb%
\gtt@hgridfalse%
\else%
\gtt@hgridtrue%
\def\@tempb{true}%
\ifx\@tempa\@tempb%
\def\gtt@hgridstyle{dotted}%
\else%
\def\gtt@hgridstyle{#1}%
\fi%
\fi%
},%
hgrid/.default=dotted
}
\gtt @hgrid@do ¶
The \gtt@hgrid@do macro decomposes the style list for the horizontal grid into its comma-separated items. Each item is analyzed (see below) only if some grid lines are still left to draw. Note the “elegant” quadruple \expandafter construction, which enables tail recursion.
\def\gtt@hgrid@do#1,{%
\ifx\relax#1\else
\ifnum\gtt@currgrid<\gtt@currentline\else%
\gtt@hgrid@analyze#1\relax%
\expandafter\expandafter\expandafter\gtt@hgrid@do%
\expandafter\fi%
\fi%
}
\gtt @hgrid@analyze ¶
In the absence of a star as the first token in a style list item, \gtt@hgrid@analyze adds the multiplier 1 to the input stream.
\def\gtt@hgrid@analyze{%
\@gtt@ifstar{\gtt@hgrid@draw}{\gtt@hgrid@draw1}%
}
\gtt @hgrid@draw ¶
\gtt@hgrid@draw draws as many grid lines as required by the multiplier. It increases \gtt@currgrid after each line drawn and breaks the loop as soon as all grid rules have been drawn.
\def\gtt@hgrid@draw#1#2\relax{%
\foreach \i in {1,...,#1} {%
\pgfmathsetmacro\y@upper{%
\gtt@lasttitleline * \ganttvalueof{y unit title} +%
(\gtt@currgrid - \gtt@lasttitleline)%
* \ganttvalueof{y unit chart}%
}%
\draw [#2]
(0pt, \y@upper pt) --
(\gtt@chartwidth * \ganttvalueof{x unit}, \y@upper pt);%
\global\advance\gtt@currgrid by-1\relax%
\ifnum\gtt@currgrid<\gtt@currentline\breakforeach\fi%
}%
}
vgrid
\ifgtt @vgrid ¶
\gtt @vgridstyle ¶
\gtt @vgrid@do ¶
\gtt @vgrid@analyze ¶
\gtt @vgrid@draw ¶
Analogously, we declare options and macros for printing the vertical grid.
\newif\ifgtt@vgrid
\ganttset{%
vgrid/.code={%
\def\@tempa{#1}%
\def\@tempb{false}%
\ifx\@tempa\@tempb%
\gtt@vgridfalse%
\else%
\gtt@vgridtrue%
\def\@tempb{true}%
\ifx\@tempa\@tempb%
\def\gtt@vgridstyle{dotted}%
\else%
\def\gtt@vgridstyle{#1}%
\fi%
\fi%
},%
vgrid/.default=dotted
}
\def\gtt@vgrid@do#1,{%
\ifx\relax#1\else%
\ifnum\gtt@currgrid>\gtt@chartwidth\else%
\gtt@vgrid@analyze#1\relax%
\expandafter\expandafter\expandafter\gtt@vgrid@do%
\expandafter\fi%
\fi%
}
\def\gtt@vgrid@analyze{%
\@gtt@ifstar{\gtt@vgrid@draw}{\gtt@vgrid@draw1}%
}
\def\gtt@vgrid@draw#1#2\relax{%
\foreach \i in {1,...,#1} {%
\draw [#2]
(\gtt@currgrid * \ganttvalueof{x unit}, \y@upper pt) --%
(\gtt@currgrid * \ganttvalueof{x unit}, \y@lower pt);%
\global\advance\gtt@currgrid by1\relax%
\ifnum\gtt@currgrid>\gtt@chartwidth\breakforeach\fi%
}%
}
3.4Time Slot Formats¶
\gtt @smugglecount ¶
\gtt@smugglecount{⟨count⟩} smuggles the local value of a count register over the end of a TeX group.
\def\gtt@smugglecount#1\endgroup{%
\edef\@tempa{\the#1}%
\expandafter\endgroup\expandafter#1\expandafter=\@tempa%
}
\gtt @juliantotimeslot ¶
\gtt@juliantotimeslot{⟨count 1⟩}{⟨count 2⟩} converts the Julian date stored in ⟨count 1⟩ to a time slot and stores the latter in ⟨count 2⟩. This macro is called after the start of Gantt chart. Thus, \gtt@startyear, \gtt@startmonth and \gtt@startjulian (see section 3.5) have already been initialized. Depending on the value of time slot unit, one time slot corresponds to one day, one month or one year.
\newcommand\gtt@juliantotimeslot[2]{%
\begingroup%
\@tempcnta=#1\relax%
\ifgtt@timeslotunit@day%
\advance\@tempcnta by-\gtt@startjulian\relax%
\advance\@tempcnta by1\relax%
\fi%
\ifgtt@timeslotunit@month%
\pgfcalendarjuliantodate{\@tempcnta}{\@tempa}{\@tempb}{\@tempc}%
\@tempcnta=\@tempa\relax%
\advance\@tempcnta by-\gtt@startyear\relax%
\multiply\@tempcnta by12\relax%
\advance\@tempcnta by\@tempb\relax%
\advance\@tempcnta by-\gtt@startmonth\relax%
\advance\@tempcnta by1\relax%
\fi%
\ifgtt@timeslotunit@year%
\pgfcalendarjuliantodate{\@tempcnta}{\@tempa}{\@tempb}{\@tempc}%
\@tempcnta=\@tempa\relax%
\advance\@tempcnta by-\gtt@startyear\relax%
\advance\@tempcnta by1\relax%
\fi%
#2=\@tempcnta\relax%
\gtt@smugglecount#2%
\endgroup%
}
\newgantttimeslotformat
\newgantttimeslotformat{⟨name⟩}{⟨code⟩} defines the macro
\gtt@tsstojulian@⟨name⟩{⟨tss⟩}{⟨count⟩}. This macro executes ⟨code⟩ (within a group), which should convert ⟨tss⟩ to a Julian date and store the date in ⟨count⟩.
\newcommand\newgantttimeslotformat[2]{%
\expandafter\def\csname gtt@tsstojulian@#1\endcsname##1##2{%
\begingroup#2\gtt@smugglecount##2\endgroup%
}%
}
The predefined time slot formats simple, isodate, isodate-yearmonth and isodate-year are straight forward.
\newgantttimeslotformat{simple}{%
#2=#1\relax%
\advance#2 by\gtt@tsf@startjulian\relax%
\advance#2 by-1\relax%
}
\newgantttimeslotformat{isodate}{%
\pgfcalendardatetojulian{#1}{#2}%
}
\newgantttimeslotformat{isodate-yearmonth}{%
\pgfcalendardatetojulian{#1-01}{#2}%
}
\newgantttimeslotformat{isodate-year}{%
\pgfcalendardatetojulian{#1-01-01}{#2}%
}
\gtt @tsf@getdmy ¶
\gtt@tsf@getdmy{⟨date⟩} decomposes a ⟨date⟩ day[sep]month[sep]year (with [sep] representing a period, hyphen or slash) into day, month and year and stores these numbers in \local@day, \local@month and \local@year, respectively.
\newcommand\gtt@tsf@getdmy[1]{%
\edef\local@firstarg{#1}%
\def\local@decompose##1.##2.##3\relax{%
\def\local@day{##1}\def\local@month{##2}\def\local@year{##3}%
}%
\expandafter\local@decompose\local@firstarg..\relax%
\ifx\local@month\@empty%
\def\local@decompose##1/##2/##3\relax{%
\def\local@day{##1}\def\local@month{##2}\def\local@year{##3}%
}%
\expandafter\local@decompose\local@firstarg//\relax%
\ifx\local@month\@empty%
\def\local@decompose##1-##2-##3\relax{%
\def\local@day{##1}\def\local@month{##2}\def\local@year{##3}%
}%
\expandafter\local@decompose\local@firstarg--\relax%
\ifx\local@month\@empty%
\@gtt@PackageError{Illegal time slot specifier `#1'.}%
\else%
\def\local@decompose##1--{\def\local@year{##1}}%
\expandafter\local@decompose\local@year%
\fi%
\else%
\def\local@decompose##1//{\def\local@year{##1}}%
\expandafter\local@decompose\local@year%
\fi%
\else%
\def\local@decompose##1..{\def\local@year{##1}}%
\expandafter\local@decompose\local@year%
\fi%
}
Time slot formats little-endian, big-endian and middle-endian only differ in their call of \pgfcalendardatetojulian. If the year (stored in \local@year or \local@day) lacks a century (e. g., 13 instead of 2013), it is completed according to the value of time slot format/base century.
\newgantttimeslotformat{little-endian}{%
\gtt@tsf@getdmy{#1}%
\ifnum\local@year<100\relax%
\edef\local@year{\gtt@tsf@basecentury\local@year}%
\fi%
\pgfcalendardatetojulian{\local@year-\local@month-\local@day}{#2}%
}
\newgantttimeslotformat{big-endian}{%
\gtt@tsf@getdmy{#1}%
\ifnum\local@day<100\relax%
\edef\local@day{\gtt@tsf@basecentury\local@day}%
\fi%
\pgfcalendardatetojulian{\local@day-\local@month-\local@year}{#2}%
}
\newgantttimeslotformat{middle-endian}{%
\gtt@tsf@getdmy{#1}%
\ifnum\local@year<100\relax%
\edef\local@year{\gtt@tsf@basecentury\local@year}%
\fi%
\pgfcalendardatetojulian{\local@year-\local@day-\local@month}{#2}%
}
time slot format
\gtt @tsstojulian ¶
The key time slot format=⟨name⟩ checks whether the format ⟨name⟩
exists and then defines the macro \gtt@tsstojulian to be equivalent to
\gtt@tsstojulian@⟨name⟩.
\ganttset{%
time slot format/.code={%
\@ifundefined{gtt@tsstojulian@#1}{%
\@gtt@PackageError{%
Time slot format `#1' undefined.%
}%
}{}%
\expandafter\let\expandafter\gtt@tsstojulian%
\csname gtt@tsstojulian@#1\endcsname%
},%
time slot format=simple,%
time slot format/base century
\gtt @tsf@basecentury ¶
time slot format/base century=⟨year⟩ extracts the century from the four-digit ⟨year⟩ (e. g., 20 from 2000) and stores it in \gtt@tsf@basecentury.
time slot format/base century/.code={%
\begingroup%
\@tempcnta=#1\relax%
\divide\@tempcnta by100\relax%
\xdef\gtt@tsf@basecentury{\the\@tempcnta}%
\endgroup%
},%
time slot format/base century=2000,%
time slot format/start date
\gtt @tsf@startjulian ¶
time slot format/start date=⟨isodate⟩ stores the Julian date corresponding to ⟨isodate⟩ in \gtt@tsf@startjulian.
time slot format/start date/.code={%
\begingroup%
\pgfcalendardatetojulian{#1}{\@tempcnta}%
\xdef\gtt@tsf@startjulian{\the\@tempcnta}%
\endgroup%
},%
time slot format/start date=2000-01-01%
}
3.5The Main Environment¶
x unit
y unit title
y unit chart
Keys that store the basis vectors of the chart.
\@gtt@keydef{x unit}{.5cm}
\@gtt@keydef{y unit title}{1cm}
\@gtt@keydef{y unit chart}{1cm}
canvas
expand chart
today
today offset
today rule
today label
today label font
today label node
Keys related to the canvas and the today rule.
\@gtt@stylekeydef{canvas}{shape=rectangle, draw, fill=white}
\@gtt@keydef{expand chart}{none}
\@gtt@keydef{today}{none}
\@gtt@keydef{today offset}{1}
\@gtt@stylekeydef{today rule}{dashed, line width=1pt}
\@gtt@keydef{today label}{TODAY}
\@gtt@keydef{today label font}{\normalfont}
\@gtt@stylekeydef{today label node}{%
anchor=north, font=\ganttvalueof{today label font}%
}
\ifgtt @newlineshortcut ¶
newline shortcut
Boolean key that determines if \\ is equivalent to \ganttnewline.
\newif\ifgtt@newlineshortcut
\ganttset{%
newline shortcut/.is if=gtt@newlineshortcut,%
newline shortcut=true%
}
\ifgtt @tikzpicture ¶
\ifgtt @intitle ¶
\gtt @lasttitleslot ¶
\gtt @elementid ¶
\gtt @today@slot ¶
\gtt @startjulian ¶
\gtt @endjulian ¶
\gtt @chartid ¶
The boolean \ifgtt@tikzpicture is true if a Gantt chart appears within a TikZ picture. \ifgtt@intitle is true at the start of a ganttchart environment and set to false as soon as the first non-title element is encountered. \gtt@lasttitleslot corresponds to the \(x\)-coordinate of its right border. \gtt@elementid enumerates the automatic names of chart elements. \gtt@today@slot is the time slot of the today rule. \gtt@startjulian and \gtt@endjulian contain the Julian dates corresponding to the first and last time slot, respectively. \gtt@chartid assigns a consecutive number to each chart.
\newif\ifgtt@tikzpicture
\newif\ifgtt@intitle
\newcount\gtt@lasttitleslot
\newcount\gtt@elementid
\newcount\gtt@today@slot
\newcount\gtt@startjulian
\newcount\gtt@endjulian
\newcount\gtt@chartid
\gtt @chartextrasize ¶
Each ganttchart environment writes a \gtt@chartextrasize{⟨chart id⟩}{⟨extra size⟩} macro to the auxililary file. This macro stores its second argument in a macro of the form \@gtt@chart@⟨chart id⟩@extrasize. The ⟨extra size⟩ is the size of the chart’s bounding box less the size of the canvas, calculated as x unit times the number of time slots.
\def\gtt@chartextrasize#1#2{%
\global\@namedef{@gtt@chart@#1@extrasize}{#2}%
}
ganttchart
At the beginning of a ganttchart environment, the keys in its optional argument are executed. Initialize the macros and counts that contain start dates, end dates, the chart width, …
\newenvironment{ganttchart}[3][]{%
\ganttset{#1}%
\gtt@tsstojulian{#2}{\gtt@startjulian}%
\global\gtt@startjulian=\gtt@startjulian\relax%
\gtt@tsstojulian{#3}{\gtt@endjulian}%
\global\gtt@endjulian=\gtt@endjulian\relax%
\pgfcalendarjuliantodate{\gtt@startjulian}%
{\gtt@startyear}{\gtt@startmonth}{\@tempa}%
\xdef\gtt@startyear{\gtt@startyear}%
\xdef\gtt@startmonth{\gtt@startmonth}%
\gtt@juliantotimeslot{\gtt@endjulian}{\gtt@chartwidth}
\global\gtt@chartwidth=\gtt@chartwidth\relax%
… the time slot of the today rule, ….
\def\@tempa{none}%
\edef\@tempb{\ganttvalueof{today}}%
\ifx\@tempa\@tempb\else%
\gtt@tsstojulian{\ganttvalueof{today}}{\gtt@today@slot}
\gtt@juliantotimeslot{\gtt@today@slot}{\gtt@today@slot}%
\fi%
… the current element number, and information for drawing actions.
\global\gtt@elementid=0\relax%
\global\gtt@currentline=0\relax%
\global\gtt@lasttitleline=0\relax%
\global\gtt@lasttitleslot=0\relax%
\gtt @expanded@xunit ¶
If expand chart contains a value different from none, scale the chart so that its \(x\)-extent equals this value. To this end, use the information stored in the auxiliary file. \gtt@expanded@xunit will contain the new value for x unit.
\def\@tempa{none}%
\edef\@tempb{\ganttvalueof{expand chart}}%
\ifx\@tempa\@tempb\else%
\@ifundefined{@gtt@chart@\the\gtt@chartid @extrasize}{%
\@gtt@PackageWarning{Gantt chart expansion may have changed.
Rerun to get expansion right}%
}{%
\pgfmathparse{(\ganttvalueof{expand chart}%
- \@nameuse{@gtt@chart@\the\gtt@chartid @extrasize})%
/ \gtt@chartwidth}%
\edef\gtt@expanded@xunit{\pgfmathresult pt}%
\ganttset{x unit=\gtt@expanded@xunit}%
}%
\fi%
\\
\ganttalignnewline
If a ganttchart appears outside of a tikzpicture, we implicitly start this environment. “Within a tikzpicture” means that \useasboundingbox is defined. Since we expect a chart to start with at least one title element, \ifgtt@intitle is true. If newline shortcut is true, make the control symbol \\ equivalent to \ganttnewline. In any case, \ganttalignnewline is defined.
\@ifundefined{useasboundingbox}%
{\gtt@tikzpicturefalse\begin{tikzpicture}}%
{\gtt@tikzpicturetrue}%
\gtt@intitletrue%
\ifgtt@newlineshortcut%
\let\\\ganttnewline%
\fi%
\let\ganttalignnewline\tikz@align@newline%
}{
\y @upper ¶
\y @lower ¶
\y @mid ¶
\y @size ¶
\x @size ¶
After the contents of the environment have been drawn, we add the canvas to the background layer. pgfgantt saves \(x\)- and \(y\)-coordinates in local internal macros called \x@left, \x@right, \x@mid, \x@size, \y@upper, \y@lower, \y@mid and \y@size.
\begin{scope}[on background layer]%
\ifgtt@includetitle%
\def\y@upper{0}%
\else%
\pgfmathsetmacro\y@upper{%
\gtt@lasttitleline * \ganttvalueof{y unit title}%
}%
\fi%
\pgfmathsetmacro\y@lower{%
\gtt@lasttitleline * \ganttvalueof{y unit title}%
+ (\gtt@currentline - \gtt@lasttitleline - 1)%
* \ganttvalueof{y unit chart}%
}%
\pgfmathsetmacro\y@mid{%
(\y@upper + \y@lower) / 2%
}%
\pgfmathsetmacro\y@size{%
abs(\y@lower - \y@upper)%
}%
\pgfmathsetmacro\x@size{%
\gtt@chartwidth * \ganttvalueof{x unit}%
}%
\node [/pgfgantt/canvas, minimum width=\x@size pt,
minimum height=\y@size pt]
at (\x@size pt / 2, \y@mid pt) {};%
The contents of the vertical grid style list are evaluated at most \gtt@chartwidth-times, but the loop breaks as soon as all grid lines have been drawn.
\pgfmathsetmacro\y@upper{%
\gtt@lasttitleline * \ganttvalueof{y unit title}%
}%
\ifgtt@vgrid
\gtt@currgrid=1\relax%
\global\advance\gtt@chartwidth by-1\relax%
\foreach \x in {1,...,\gtt@chartwidth} {%
\expandafter\gtt@vgrid@do\gtt@vgridstyle,\relax,%
\ifnum\gtt@currgrid>\gtt@chartwidth\relax\breakforeach\fi%
}%
\global\advance\gtt@chartwidth by1\relax%
\fi%
Now, we draw the horizontal grid. If we exclude the title from the canvas, we omit the uppermost horizontal grid line since it would coincide with the canvas border.
\ifgtt@hgrid%
\gtt@currgrid=\gtt@lasttitleline\relax%
\ifgtt@includetitle\else%
\advance\gtt@currgrid by-1\relax
\fi%
\edef\@tempa{\the\gtt@currgrid}%
\foreach \t in {\@tempa,...,\gtt@currentline} {%
\expandafter\gtt@hgrid@do\gtt@hgridstyle,\relax,%
\ifnum\gtt@currgrid<\gtt@currentline\relax\breakforeach\fi%
}%
\fi%
\x @mid ¶
The last task of ganttchart is to apply the today key if its value differs from none.
\def\@tempa{none}%
\edef\@tempb{\ganttvalueof{today}}%
\ifx\@tempa\@tempb\else%
\pgfmathsetmacro\x@mid{%
(\gtt@today@slot - 1 + \ganttvalueof{today offset})%
* \ganttvalueof{x unit}%
}%
\draw [/pgfgantt/today rule]
(\x@mid pt, \y@upper pt) -- (\x@mid pt, \y@lower pt)
node [/pgfgantt/today label node] {\ganttvalueof{today label}};%
\fi%
\end{scope}%
Store the \(x\)-extent of the bounding box in \@tempdima. Calculate the size by which the bounding box exceeds the “raw” canvas size. Write this information to the auxiliary file.
\pgfextractx{\@tempdima}{%
\pgfpointdiff{\pgfpointanchor{current bounding box}{south west}}%
{\pgfpointanchor{current bounding box}{north east}}%
}%
\pgfmathparse{\@tempdima -\ganttvalueof{x unit} * \gtt@chartwidth}%
\protected@write\@auxout{}{%
\string\gtt@chartextrasize{\the\gtt@chartid}{\pgfmathresult pt}}%
Increase the chart counter.
\global\advance\gtt@chartid by1\relax%
At the end of a ganttchart, we also close the tikzpicture if we started it implicitly.
\ifgtt@tikzpicture\else\end{tikzpicture}\fi%
}
3.6Starting a New Line¶
\ganttnewline
\local @drawarg ¶
Unless the optional argument of \ganttnewline is empty, this macro adds a horizontal grid rule between the current and the new line. The style of this line, which is stored in \local@drawarg, is either hgrid style or the style specified in the optional argument. Anyway, \ganttnewline decreases \gtt@currentline and, if we are still in the title, \gtt@lasttitleline. Since the new line starts at time slot zero, \gtt@lasttitleslot is reset.
\newcommand\ganttnewline[1][]{%
\begingroup%
\def\local@drawarg{#1}%
\def\@tempa{grid}%
\ifx\local@drawarg\@empty\else%
\ifx\local@drawarg\@tempa%
\def\local@drawarg{/pgfgantt/hgrid style}%
\fi%
\pgfmathsetmacro\y@upper{%
\gtt@lasttitleline * \ganttvalueof{y unit title}%
+ (\gtt@currentline - \gtt@lasttitleline - 1)%
* \ganttvalueof{y unit chart}%
}
\expandafter\draw\expandafter[\local@drawarg]
(0pt, \y@upper pt) --
(\gtt@chartwidth * \ganttvalueof{x unit}, \y@upper pt);%
\fi%
\global\advance\gtt@currentline by-1\relax%
\ifgtt@intitle\global\advance\gtt@lasttitleline by-1\relax\fi%
\global\gtt@lasttitleslot=0\relax%
\endgroup%
}
3.7Vertical rules¶
vrule offset
vrule
vrule label font
vrule label node
Keys related to the vertical rules.
\@gtt@keydef{vrule offset}{1}
\@gtt@stylekeydef{vrule}{dashed, line width=1pt}
\@gtt@keydef{vrule label font}{\normalfont}
\@gtt@stylekeydef{vrule label node}{%
anchor=north, font=\ganttvalueof{vrule label font}%
}
\gtt @vrule@slot ¶
A count for storing the vrule time slot.
\newcount\gtt@vrule@slot
\ganttvrule
Calculate the coordinates for the vertical rule and draw it.
\newcommand\ganttvrule[3][]{%
\begingroup
\ganttset{#1}
\gtt@tsstojulian{#3}{\gtt@vrule@slot}%
\gtt@juliantotimeslot{\gtt@vrule@slot}{\gtt@vrule@slot}%
\pgfmathsetmacro\y@upper{%
\gtt@lasttitleline * \ganttvalueof{y unit title}%
}%
\pgfmathsetmacro\y@lower{%
\gtt@lasttitleline * \ganttvalueof{y unit title}%
+ (\gtt@currentline - \gtt@lasttitleline - 1)%
* \ganttvalueof{y unit chart}%
}%
\pgfmathsetmacro\x@mid{%
(\gtt@vrule@slot - 1 + \ganttvalueof{vrule offset})%
* \ganttvalueof{x unit}%
}%
\draw [/pgfgantt/vrule]
(\x@mid pt, \y@upper pt) -- (\x@mid pt, \y@lower pt)
node [/pgfgantt/vrule label node] {#2};%
\endgroup
}
3.8Titles¶
title
title label font
title label node
title label text
\gtt @titlelabeltext ¶
title list options
\gtt @titlelistoptions ¶
title left shift
title right shift
title top shift
title height
include title in canvas
\ifgtt @includetitle ¶
Keys that influence title elements. The parameter token #1 in the value of title label text is replaced by the argument of \gtt@titlelabeltext. Note that \@gtt@keydef cannot define title list options, since \@gtt@titlelistoptions is expanded after a \foreach statement, where \ganttvalueof will not work.
\@gtt@stylekeydef{title}{shape=rectangle, inner sep=0pt, draw, fill=white}
\@gtt@keydef{title label font}{\small}
\@gtt@stylekeydef{title label node}{%
anchor=center, font=\ganttvalueof{title label font}%
}
\ganttset{%
title label text/.code={%
\def\gtt@titlelabeltext##1{#1}%
},%
title label text=\strut#1,%
title list options/.code={%
\def\gtt@titlelistoptions{[#1]}%
},%
title list options={var=\x, evaluate=\x}%
}
\@gtt@keydef{title left shift}{0}
\@gtt@keydef{title right shift}{0}
\@gtt@keydef{title top shift}{0}
\@gtt@keydef{title height}{.6}
\newif\ifgtt@includetitle
\ganttset{%
include title in canvas/.is if=gtt@includetitle,%
include title in canvas
}
calendar week text
time slot unit
\ifgtt @timeslotunit@day ¶
\ifgtt @timeslotunit@month ¶
\ifgtt @timeslotunit@year ¶
Keys for title calendars.
\@gtt@keydef{calendar week text}{Week~\currentweek}
\newif\ifgtt@timeslotunit@day
\newif\ifgtt@timeslotunit@month
\newif\ifgtt@timeslotunit@year
\ganttset{%
time slot unit/.is choice,
time slot unit/day/.code={%
\gtt@timeslotunit@daytrue%
\gtt@timeslotunit@monthfalse%
\gtt@timeslotunit@yearfalse%
},%
time slot unit/month/.code={%
\gtt@timeslotunit@dayfalse%
\gtt@timeslotunit@monthtrue%
\gtt@timeslotunit@yearfalse%
},%
time slot unit/year/.code={%
\gtt@timeslotunit@dayfalse%
\gtt@timeslotunit@monthfalse%
\gtt@timeslotunit@yeartrue%
},%
time slot unit=day
}
\gantttitle
\x @left ¶
\x @right ¶
\gantttitle draws a title element (i. e., a rectangle with a single node at its center). For reasons that will become clear below, the element essentially starts at the \(x\)-coordinate stored in \gtt@lasttitleslot. This count is updated at the end of the macro.
\newcommand\gantttitle[3][]{%
\begingroup%
\ganttset{#1}%
\pgfmathsetmacro\x@left{%
(\gtt@lasttitleslot + \ganttvalueof{title left shift})%
* \ganttvalueof{x unit}%
}%
\pgfmathsetmacro\x@right{%
(\gtt@lasttitleslot + #3 + \ganttvalueof{title right shift})%
* \ganttvalueof{x unit}%
}%
\pgfmathsetmacro\x@mid{%
(\x@left + \x@right) / 2%
}%
\pgfmathsetmacro\x@size{%
\x@right - \x@left%
}%
\pgfmathsetmacro\y@upper{%
(\gtt@currentline - \ganttvalueof{title top shift})%
* \ganttvalueof{y unit title}%
}%
\pgfmathsetmacro\y@lower{%
(\gtt@currentline - \ganttvalueof{title top shift}%
- \ganttvalueof{title height}) * \ganttvalueof{y unit title}%
}%
\pgfmathsetmacro\y@mid{%
(\y@upper + \y@lower) / 2%
}%
\pgfmathsetmacro\y@size{%
\y@upper - \y@lower%
}%
\path (\x@mid pt, \y@mid pt)
node [/pgfgantt/title, minimum width=\x@size pt,
minimum height=\y@size pt] {}
node [/pgfgantt/title label node] {\gtt@titlelabeltext{#2}};%
\global\advance\gtt@lasttitleslot by#3\relax%
\endgroup%
}
\gantttitlelist
\gantttitlelist generates title elements by repeatedly calling \gantttitle. Since the latter always starts after the last time slot occupied by the previous element, \gantttitlelist does not have to calculate the respective \(x\)-coordinates explicitly.
\newcommand\gantttitlelist[3][]{%
\begingroup%
\ganttset{#1}%
\expandafter\foreach\gtt@titlelistoptions in {#2} {\gantttitle{\x}{#3}}%
\endgroup%
}
\ifgtt @titlecalendarstar ¶
\gantttitlecalendar
\gantttitlecalendar *
\gantttitlecalendar checks whether it is invoked in the starred or nonstarred form, sets \ifgtt@titlecalendarstar accordingly and then starts a command relaying chain.
\newif\ifgtt@titlecalendarstar
\newcommand\gantttitlecalendar{%
\@gtt@ifstar%
{\gtt@titlecalendarstartrue\@gantttitlecalendar}%
{\gtt@titlecalendarstarfalse\@gantttitlecalendar}%
}
\@gantttitlecalendar ¶
The first command in the relaying chain, \@gantttitlecalendar[⟨options⟩], processes the ⟨options⟩. If it was executed by the starred form of \gantttitlecalendar, it calls the second command in the chain. Otherwise, it directly calls the third command in the chain.
\newcommand\@gantttitlecalendar[1][]{
\begingroup%
\ganttset{#1}%
\ifgtt@titlecalendarstar%
\expandafter\@@gantttitlecalendar%
\else%
\expandafter\@@@gantttitlecalendar\expandafter%
{\expandafter\gtt@startjulian\expandafter}\expandafter%
{\expandafter\gtt@endjulian\expandafter}%
\fi%
}
\@@gantttitlecalendar ¶
The second command in the relaying chain,
\@@gantttitlecalendar{⟨start tss⟩}{⟨end tss⟩},
reads two mandatory arguments from the input stream and converts them to Julian dates. Finally, it calls the third command in the chain.
\newcommand\@@gantttitlecalendar[2]{
\gtt@tsstojulian{#1}{\@tempcnta}%
\gtt@tsstojulian{#2}{\@tempcntb}%
\@@@gantttitlecalendar{\@tempcnta}{\@tempcntb}%
}
\@@@gantttitlecalendar ¶
\gtt @calendar@startdate ¶
\gtt @calendar@enddate ¶
The third and last command in the relaying chain, \@@@gantttitlecalendar{⟨start Julian⟩}{⟨end Julian⟩}{⟨calendar lines⟩}, stores the start and end ISO-standard dates of the calendar in \gtt@calendar@startdate and \gtt@calendar@enddate, respectively. Then, it executes the keys in ⟨calendar lines⟩, which reside in path /pgfgantt/calendar.
\newcommand\@@@gantttitlecalendar[3]{%
\pgfcalendarjuliantodate{#1}{\@tempa}{\@tempb}{\@tempc}%
\edef\gtt@calendar@startdate{\@tempa-\@tempb-\@tempc}%
\pgfcalendarjuliantodate{#2}{\@tempa}{\@tempb}{\@tempc}%
\edef\gtt@calendar@enddate{\@tempa-\@tempb-\@tempc}%
\gtt@calendar@eolfalse%
\pgfqkeys{/pgfgantt/calendar}{#3}%
\endgroup%
}
\ifgtt @calendar@eol ¶
\gtt @calendar@slots ¶
\gtt @calendar@weeknumber ¶
\gtt @calendar@startofweek ¶
Booleans and counts for drawing title calendars: \ifgtt@calendar@eol is true if \ganttcalendar should start a new calendar line. \gtt@calendar@slots is the number of time slots a calendar element will cover. \gtt@calendar@weeknumber is the current week number in a calendar line of type week. \gtt@calendar@startofweek is the Julian date of the Monday in the current week.
\newif\ifgtt@calendar@eol
\newcount\gtt@calendar@slots
\newcount\gtt@calendar@weeknumber
\newcount\gtt@calendar@startofweek
\@gtt@getfourthdigit ¶
\@gtt@getdecade ¶
\@gtt@getfourthdigit returns the fourth digit of a year, while \@gtt@getdecade returns the first three digits of a year.
\def\@gtt@getfourthdigit#1#2#3#4{#4}
\def\@gtt@getdecade#1#2#3#4{#1#2#3}
We define a new check for \pgfcalendarifdate as described in the pgfcalendar manual: end of decade=⟨date⟩ returns true if a date marks the end of a decade as defined by ⟨date⟩. For instance, if ⟨date⟩ is 2009-12-31, then the conditional will be true for the dates 1999-12-31, 2009-12-31, 2019-12-31 and so on.
\pgfkeys{%
/pgf/calendar/end of decade/.code={
\begingroup
\pgfcalendardatetojulian{#1}{\@tempcnta}
\pgfcalendarjuliantodate{\@tempcnta}{\@tempa}{\@tempb}{\@tempc}
\edef\endofdecade{\expandafter\@gtt@getfourthdigit\@tempa}
\edef\querydecade{\expandafter\@gtt@getfourthdigit\pgfcalendarifdateyear}
\ifnum\endofdecade=\querydecade\relax%
\ifnum\pgfcalendarifdatemonth=\@tempb\relax%
\ifnum\pgfcalendarifdateday=\@tempc\relax%
\global\pgfcalendarmatchestrue%
\fi%
\fi%
\fi%
\endgroup
}%
}
For each ⟨line type⟩, we define a corresponding key /pgfgantt/calendar/⟨line type⟩. This key performs the necessary calculations and draws one or several \gantttitles. Line type decade draws decades.
\ganttset{%
calendar/decade/.code={%
\ifgtt@calendar@eol\ganttnewline\fi%
\begingroup%
\gtt@calendar@slots=1\relax%
\ifgtt@timeslotunit@year%
\pgfcalendar{}{\gtt@calendar@startdate}{\gtt@calendar@enddate}{%
\ifdate{end of decade=2009-12-31}{%
\gantttitle{%
\expandafter\@gtt@getdecade\pgfcalendarcurrentyear%
0s%
}{\the\gtt@calendar@slots}%
\gtt@calendar@slots=0\relax%
}{
\ifdate{equals=01-01}{%
\ifnum\pgfcalendarcurrentjulian>\pgfcalendarbeginjulian\relax%
\advance\gtt@calendar@slots by1\relax%
\fi
}{}%
}%
\ifdate{equals=\pgfcalendarendiso}{%
\ifnum\gtt@calendar@slots=0\relax\else%
\gantttitle{%
\expandafter\@gtt@getdecade\pgfcalendarcurrentyear%
0s%
}{\the\gtt@calendar@slots}%
\fi%
}{}%
}%
\fi%
\endgroup%
\gtt@calendar@eoltrue%
},%
Line type year draws years.
calendar/year/.code={%
\ifgtt@calendar@eol\ganttnewline\fi%
\begingroup%
\gtt@calendar@slots=1\relax%
\ifgtt@timeslotunit@year%
\pgfcalendar{}{\gtt@calendar@startdate}{\gtt@calendar@enddate}{%
\ifdate{equals=12-31}{%
\gantttitle{\pgfcalendarcurrentyear}{1}%
\gtt@calendar@slots=1\relax%
}{
\advance\gtt@calendar@slots by1\relax%
}%
\ifdate{equals=\pgfcalendarendiso}{%
\ifnum\gtt@calendar@slots=1\relax\else%
\gantttitle{\pgfcalendarcurrentyear}{1}%
\fi%
}{}%
}%
\fi%
\ifgtt@timeslotunit@month%
\pgfcalendar{}{\gtt@calendar@startdate}{\gtt@calendar@enddate}{%
\ifdate{equals=12-31}{%
\gantttitle{\pgfcalendarcurrentyear}{\the\gtt@calendar@slots}%
\gtt@calendar@slots=1\relax%
}{%
\ifdate{end of month=1}{%
\advance\gtt@calendar@slots by1\relax%
}{}%
}%
\ifdate{equals=\pgfcalendarendiso}{%
\ifdate{end of month=1}{%
\advance\gtt@calendar@slots by-1\relax%
}{}%
\ifdate{equals=12-31}{}{%
\gantttitle{\pgfcalendarcurrentyear}{\the\gtt@calendar@slots}%
}%
}{}%
}%
\fi%
\ifgtt@timeslotunit@day%
\pgfcalendar{}{\gtt@calendar@startdate}{\gtt@calendar@enddate}{%
\ifdate{equals=12-31}{%
\gantttitle{\pgfcalendarcurrentyear}{\the\gtt@calendar@slots}%
\gtt@calendar@slots=1\relax%
}{%
\advance\gtt@calendar@slots by1\relax%
}%
\ifdate{equals=\pgfcalendarendiso}{%
\ifnum\gtt@calendar@slots=1\relax\else%
\advance\gtt@calendar@slots by-1\relax%
\gantttitle{\pgfcalendarcurrentyear}{\the\gtt@calendar@slots}%
\fi%
}{}%
}%
\fi%
\endgroup%
\gtt@calendar@eoltrue%
},%
Line type month=⟨format⟩ draws months. Internally, a month is represented by a number between 1 (January) and 12 (December). However, when the title element is drawn, this number is fed to the macro \pgfcalendarmonth⟨format⟩ and possibly converted.
calendar/month/.code={%
\ifgtt@calendar@eol\ganttnewline\fi%
\begingroup%
\gtt@calendar@slots=1\relax%
\pgfcalendar{}{\gtt@calendar@startdate}{\gtt@calendar@enddate}{%
\ifdate{end of month=1}{%
\gantttitle{%
\csname pgfcalendarmonth#1\endcsname{\pgfcalendarcurrentmonth}%
}{%
\ifgtt@timeslotunit@month1\fi%
\ifgtt@timeslotunit@day\the\gtt@calendar@slots\fi%
}%
\gtt@calendar@slots=1\relax%
}{%
\advance\gtt@calendar@slots by1\relax%
}%
\ifdate{equals=\pgfcalendarendiso}{%
\ifnum\gtt@calendar@slots=1\relax\else%
\advance\gtt@calendar@slots by-1\relax%
\gantttitle{%
\csname pgfcalendarmonth#1\endcsname{\pgfcalendarcurrentmonth}%
}{%
\ifgtt@timeslotunit@month1\fi%
\ifgtt@timeslotunit@day\the\gtt@calendar@slots\fi%
}%
\fi%
}{}%
}%
\endgroup%
\gtt@calendar@eoltrue%
},%
\startyear
\startmonth
\startday
\currentweek
Line type week=⟨number⟩ draws weeks. The first week receives ⟨number⟩, which is also saved in \currentweek. This key also defines the macros \startyear, \startmonth and \startday, which store the year, month and day of the current week’s Monday. These four macros can be used in the value of calendar week text.
calendar/week/.code={%
\ifgtt@calendar@eol\ganttnewline\fi%
\begingroup%
\gtt@calendar@slots=1\relax%
\gtt@calendar@weeknumber=#1\relax%
\pgfcalendar{}{\gtt@calendar@startdate}{\gtt@calendar@enddate}{%
\ifdate{Sunday}{%
\gtt@calendar@startofweek=\pgfcalendarcurrentjulian\relax%
\advance\gtt@calendar@startofweek by1\relax%
\advance\gtt@calendar@startofweek by-\gtt@calendar@slots\relax%
\pgfcalendarjuliantodate{\gtt@calendar@startofweek}%
{\startyear}{\startmonth}{\startday}%
\def\currentweek{\the\gtt@calendar@weeknumber}%
\gantttitle{%
\ganttvalueof{calendar week text}%
}{%
\the\gtt@calendar@slots%
}%
\gtt@calendar@slots=1\relax%
\advance\gtt@calendar@weeknumber by1\relax%
}{%
\advance\gtt@calendar@slots by1%
}%
\ifdate{equals=\pgfcalendarendiso}{%
\ifnum\gtt@calendar@slots=1\relax\else%
\advance\gtt@calendar@slots by-1\relax%
\gtt@calendar@startofweek=\pgfcalendarcurrentjulian\relax%
\advance\gtt@calendar@startofweek by1\relax%
\advance\gtt@calendar@startofweek by-\gtt@calendar@slots\relax%
\pgfcalendarjuliantodate{\gtt@calendar@startofweek}%
{\startyear}{\startmonth}{\startday}%
\def\currentweek{\the\gtt@calendar@weeknumber}%
\gantttitle{%
\ganttvalueof{calendar week text}%
}{%
\the\gtt@calendar@slots%
}%
\fi%
}{}%
}%
\endgroup%
\gtt@calendar@eoltrue%
},%
calendar/week/.default=1,%
Line type weekday=⟨format⟩ draws weekdays. Internally, a weekday is represented by a number between 0 (Monday) and 6 (Sunday). However, when the title element is drawn, this number is fed to the macro \pgfcalendarweekday⟨format⟩ and possibly converted.
calendar/weekday/.code={%
\ifgtt@calendar@eol\ganttnewline\fi%
\begingroup%
\pgfcalendar{}{\gtt@calendar@startdate}{\gtt@calendar@enddate}{%
\gantttitle{%
\csname pgfcalendarweekday#1\endcsname{\pgfcalendarcurrentweekday}%
}{1}%
}%
\endgroup%
\gtt@calendar@eoltrue%
},%
Line type day=⟨format⟩ draws days of the month.
calendar/day/.code={%
\ifgtt@calendar@eol\ganttnewline\fi%
\begingroup%
\pgfcalendar{}{\gtt@calendar@startdate}{\gtt@calendar@enddate}{%
\gantttitle{\pgfcalendarcurrentday}{1}%
}%
\endgroup%
\gtt@calendar@eoltrue%
}%
}
3.9Chart Elements¶
progress
\gtt @progress ¶
progress label text
\gtt @progresslabeltext ¶
name
chart element start border
\ifgtt @ce@startatleftborder ¶
inline
\ifgtt @inline ¶
Keys that apply to all chart elements. The parameter token #1 in the value of progress label text is replaced by the argument of \gtt@progresslabeltext.
\ganttset{%
progress/.code={%
\def\gtt@progress{#1}%
},%
progress=none,%
progress label text/.code={%
\def\gtt@progresslabeltext##1{#1}%
},%
progress label text={%
\pgfmathprintnumber[precision=0, verbatim]{#1}\% complete%
}%
}
\@gtt@keydef{name}{}
\newif\ifgtt@ce@startatleftborder
\ganttset{%
chart element start border/.is choice,%
chart element start border/left/.code=\gtt@ce@startatleftbordertrue,%,
chart element start border/right/.code=\gtt@ce@startatleftborderfalse,%
chart element start border=left%
}
\newif\ifgtt@inline
\ganttset{%
inline/.is if=gtt@inline,%
inline=false%
}
\gtt @lastelement ¶
\gtt @currentelement ¶
\ifgtt @draw@complete ¶
\ifgtt @draw@incomplete ¶
\ifgtt @draw@clip ¶
\gtt @left@slot ¶
\gtt @right@slot ¶
The macros \gtt@lastelement and \gtt@currentelement save the name of the current and last chart element drawn. Thereby, the \ganttlinked... macros can add a link connecting them. \ifgtt@draw@complete, \ifgtt@draw@incomplete and \ifgtt@draw@clip decide whether to draw the complete and incomplete part of a chart element and if these parts are clipped. \gtt@left@slot and \gtt@right@slot store a chart element’s start and end time slot, respectively.
\def\gtt@lastelement{}
\def\gtt@currentelement{}
\newif\ifgtt@draw@complete
\newif\ifgtt@draw@incomplete
\newif\ifgtt@draw@clip
\newcount\gtt@left@slot
\newcount\gtt@right@slot
\gtt @chartelement ¶
\local @timeslotmodifier ¶
\gtt @name ¶
\gtt@chartelement[⟨options⟩]{⟨label⟩}{⟨start tss⟩}{⟨end tss⟩}{⟨type⟩}
is the generic command for drawing chart elements of a certain ⟨type⟩. First, \gtt@chartelement converts ⟨start
tss⟩ to \gtt@left@slot and ⟨end tss⟩ to \gtt@right@slot. Then it calculates the
usual coordinates. \gtt@name stores the name of the chart element.
\newcommand\gtt@chartelement[5][]{%
\begingroup%
\ganttset{#1}%
\gtt@tsstojulian{#3}{\gtt@left@slot}%
\gtt@juliantotimeslot{\gtt@left@slot}{\gtt@left@slot}%
\gtt@tsstojulian{#4}{\gtt@right@slot}%
\gtt@juliantotimeslot{\gtt@right@slot}{\gtt@right@slot}%
\def\local@timeslotmodifier{-1}%
\ifgtt@ce@startatleftborder\else%
\ifnum\gtt@left@slot=\gtt@right@slot\relax\else%
\def\local@timeslotmodifier{0}%
\fi%
\fi%
\pgfmathsetmacro\x@left{%
(\gtt@left@slot + \local@timeslotmodifier%
+ \ganttvalueof{#5 left shift})%
* \ganttvalueof{x unit}%
}%
\pgfmathsetmacro\x@right{%
(\gtt@right@slot + \ganttvalueof{#5 right shift})%
* \ganttvalueof{x unit}%
}%
\pgfmathsetmacro\x@mid{%
(\x@left + \x@right) / 2%
}%
\pgfmathsetmacro\x@size{%
\x@right - \x@left%
}%
\pgfmathsetmacro\y@upper{%
\gtt@lasttitleline * \ganttvalueof{y unit title}
+ (\gtt@currentline - \gtt@lasttitleline
- \ganttvalueof{#5 top shift}) * \ganttvalueof{y unit chart}%
}%
\pgfmathsetmacro\y@lower{%
\y@upper - \ganttvalueof{#5 height} * \ganttvalueof{y unit chart}%
}%
\pgfmathsetmacro\y@mid{%
(\y@upper + \y@lower) / 2%
}%
\pgfmathsetmacro\y@size{%
\y@upper - \y@lower%
}%
\edef\gtt@name{\ganttvalueof{name}}%
\ifx\gtt@name\@empty\edef\gtt@name{elem\the\gtt@elementid}\fi%
\local @none ¶
Depending on the values of progress and today, we determine the correct value for \gtt@progress. A value between 0 and 100 corresponds to a percentage of completeness. A value of 999 indicates that the chart element has no associated progress.
\def\local@none{none}%
\ifx\gtt@progress\local@none%
\def\gtt@progress{999}%
\else%
\def\@tempa{today}%
\ifx\gtt@progress\@tempa%
\edef\@tempa{\ganttvalueof{today}}%
\ifx\@tempa\local@none%
\@gtt@PackageWarning{%
Value of today is `none'. Ignoring `progress=today'%
}%
\def\gtt@progress{999}%
\else\ifnum\gtt@today@slot>\gtt@right@slot\relax%
\def\gtt@progress{100}%
\else\ifnum\gtt@today@slot<\gtt@left@slot\relax%
\def\gtt@progress{0}%
\else%
\pgfmathsetmacro\gtt@progress{%
(\gtt@today@slot - \gtt@left@slot - \local@timeslotmodifier)%
/ (\gtt@right@slot - \gtt@left@slot - \local@timeslotmodifier)%
* 100%
}%
\fi\fi\fi%
\fi%
\fi%
\x @clip@size ¶
Now we determine whether only the complete part of the chart element, only its incomplete one or both are drawn. In the former two cases, we refrain from clipping the (in)complete part.
\gtt@draw@completetrue%
\gtt@draw@incompletetrue%
\gtt@draw@cliptrue%
\ifdim\gtt@progress pt<0.001pt\relax%
\gtt@draw@completefalse%
\gtt@draw@clipfalse%
\else\ifdim\gtt@progress pt>99.999pt\relax%
\gtt@draw@incompletefalse%
\gtt@draw@clipfalse%
\fi\fi%
\ifgtt@draw@clip%
\pgfmathsetmacro\x@clip@size{%
(\gtt@right@slot - \gtt@left@slot - \local@timeslotmodifier)%
* \gtt@progress / 100%
}
\pgfmathsetmacro\x@clip{%
(\gtt@left@slot + \local@timeslotmodifier + \x@clip@size%
+ \ganttvalueof{today offset} - 1) * \ganttvalueof{x unit}%
}%
\fi%
We draw the chart element within a pgfinterruptboundingbox, since we clip a large area of the canvas in order to avoid removing parts of the chart element border.
\begin{pgfinterruptboundingbox}%
\begin{scope}%
\ifgtt@draw@clip%
\clip (\x@left pt - 10cm, \y@upper pt + 10cm) rectangle
(\x@clip pt, \y@lower pt - 10cm);%
\fi%
\ifgtt@draw@complete%
\node [/pgfgantt/#5, minimum width=\x@size pt,
minimum height=\y@size pt]
(\gtt@name) at (\x@mid pt, \y@mid pt) {};%
\fi%
\end{scope}%
\begin{scope}%
\ifgtt@draw@clip%
\clip (\x@clip pt, \y@upper pt + 10cm) rectangle
(\x@right pt + 10cm, \y@lower pt - 10cm);%
\fi%
\ifgtt@draw@incomplete%
\node [/pgfgantt/#5 incomplete, minimum width=\x@size pt,
minimum height=\y@size pt]
(\gtt@name) at (\x@mid pt, \y@mid pt) {};%
\fi%
\end{scope}%
\end{pgfinterruptboundingbox}%
If progress differs from none and progress label text differs from \relax, the progress label is drawn.
\ifdim\gtt@progress pt=999pt\relax\else%
\expandafter\ifx\gtt@progresslabeltext\relax\relax\else%
\node at (\gtt@name.\ganttvalueof{#5 progress label anchor})
[/pgfgantt/#5 progress label node]
{\gtt@progresslabeltext{\gtt@progress}};%
\fi%
\fi%
If ⟨label⟩ is not empty, a label is printed. Its anchor is either at the ⟨type⟩ inline label anchor of the chart element (inline=true) or at the left canvas border halfway between the upper and lower \(y\)-coordinate of the chart element (inline=false).
\def\@tempa{#2}%
\ifx\@tempa\@empty\else%
\ifgtt@inline%
\node at (\gtt@name.\ganttvalueof{#5 inline label anchor})
[/pgfgantt/#5 inline label node]
{\csname gtt@#5labeltext\endcsname{#2}};%
\else%
\node at (0, \y@mid pt)
[/pgfgantt/#5 label node]
{\csname gtt@#5labeltext\endcsname{#2}};%
\fi%
\fi%
Since the first bar clearly appears after the last line containing a title element, we set the boolean \ifgtt@intitle to false.
\xdef\gtt@lastelement{\gtt@currentelement}%
\xdef\gtt@currentelement{\gtt@name}%
\global\advance\gtt@elementid by1\relax%
\global\gtt@intitlefalse%
\endgroup%
}
\newganttchartelement
\newganttchartelement *
\newganttchartelement checks whether it was invoked in the starred or nonstarred form and executes \@newganttchartelement@one or \@newganttchartelement@two, respectively.
\def\newganttchartelement{%
\@gtt@ifstar\@newganttchartelement@one\@newganttchartelement@two%
}
\@newganttchartelement@one ¶
\@newganttchartelement@two ¶
Both \@newganttchartelement@one{⟨type⟩} and ...two{⟨type⟩} define two macros \gantt⟨type⟩ and \ganttlinked⟨type⟩, which draw a singular chart element or one that is linked to its predecessor. However, the newly defined macros will take three or four mandatory arguments (cf. \ganttmilestone vs. \ganttbar). At the end, we execute \@newganttchartelement@definekeys to process the second mandatory argument of \newganttchartelement.
\newcommand\@newganttchartelement@one[1]{%
\expandafter\newcommand\csname gantt#1\endcsname[3][]{%
\gtt@chartelement[##1]{##2}{##3}{##3}{#1}%
}%
\expandafter\newcommand\csname ganttlinked#1\endcsname[3][]{%
\begingroup%
\ganttset{##1}%
\gtt@chartelement{##2}{##3}{##3}{#1}%
\ganttlink{\gtt@lastelement}{\gtt@currentelement}%
\endgroup%
}%
\@newganttchartelement@definekeys{#1}%
}
\newcommand\@newganttchartelement@two[1]{%
\expandafter\newcommand\csname gantt#1\endcsname[4][]{%
\gtt@chartelement[##1]{##2}{##3}{##4}{#1}%
}%
\expandafter\newcommand\csname ganttlinked#1\endcsname[4][]{%
\begingroup%
\ganttset{##1}%
\gtt@chartelement{##2}{##3}{##4}{#1}%
\ganttlink{\gtt@lastelement}{\gtt@currentelement}%
\endgroup%
}%
\@newganttchartelement@definekeys{#1}%
}
\@newganttchartelement@definekeys ¶
\@newganttchartelement@definekeys{⟨type⟩}{⟨key-value list⟩} introduces 14 keys for the newly generated chart element ⟨type⟩.
\newcommand\@newganttchartelement@definekeys[2]{%
\@gtt@stylekeydef{#1}{shape=rectangle, inner sep=0pt, draw, fill=white}%
\@gtt@stylekeydef{#1 incomplete}{/pgfgantt/#1, fill=black!25}%
\@gtt@keydef{#1 label font}{\normalsize}%
\@gtt@stylekeydef{#1 label node}{%
anchor=east, font=\ganttvalueof{#1 label font}%
}%
\@gtt@keydef{#1 inline label anchor}{center}%
\@gtt@stylekeydef{#1 inline label node}{%
anchor=center, font=\ganttvalueof{#1 label font}%
}%
\@gtt@keydef{#1 progress label anchor}{east}%
\@gtt@keydef{#1 progress label font}{\scriptsize}%
\@gtt@stylekeydef{#1 progress label node}{%
anchor=west, font=\ganttvalueof{#1 progress label font}%
}%
\@gtt@keydef{#1 left shift}{0}%
\@gtt@keydef{#1 right shift}{0}%
\@gtt@keydef{#1 top shift}{.3}%
\@gtt@keydef{#1 height}{.4}%
\ganttset{%
#1 label text/.code={%
\expandafter\def\csname gtt@#1labeltext\endcsname####1{##1}%
},%
#1 label text=\strut##1,%
#2%
}%
}
\ganttbar
\ganttlinkedbar
bar
bar incomplete
bar label text
bar label font
bar label node
bar inline label anchor
bar inline label node
bar progress label anchor
bar progress label font
bar progress label node
bar left shift
bar right shift
bar top shift
bar height
Code for the predefined chart element type bar.
\newganttchartelement{bar}{%
bar/.style={shape=ganttbar, inner sep=0pt, draw, fill=white},%
bar incomplete/.style={/pgfgantt/bar, fill=black!25},%
bar label text=\strut#1,%
bar label font=\normalsize,%
bar label node/.style={%
anchor=east, font=\ganttvalueof{bar label font}%
},%
bar inline label anchor=center,%
bar inline label node/.style={%
anchor=center, font=\ganttvalueof{bar label font}%
},%
bar progress label anchor=east,%
bar progress label font=\scriptsize,%
bar progress label node/.style={%
anchor=west, font=\ganttvalueof{bar progress label font}%
},%
bar left shift=0,%
bar right shift=0,%
bar top shift=.3,%
bar height=.4%
}
\ganttgroup
\ganttlinkedgroup
group
group incomplete
group label text
group label font
group label node
group inline label anchor
group inline label node
group progress label anchor
group progress label font
group progress label node
group left shift
group right shift
group top shift
group height
Code for the predefined chart element type group.
\newganttchartelement{group}{%
group/.style={shape=ganttgroup, inner sep=0pt, fill=black},%
group incomplete/.style={/pgfgantt/group, fill=black!25},%
group label text=\strut#1,%
group label font=\bfseries,%
group label node/.style={%
anchor=east, font=\ganttvalueof{group label font}%
},%
group inline label anchor=center,%
group inline label node/.style={%
anchor=south, font=\ganttvalueof{group label font}%
},%
group progress label anchor=east,%
group progress label font=\scriptsize,%
group progress label node/.style={%
anchor=west, font=\ganttvalueof{group progress label font}%
},%
group left shift=-.1,%
group right shift=.1,%
group top shift=.4,%
group height=.2%
}
group right peak tip position
group right peak width
group right peak height
group left peak tip position
group left peak width
group left peak height
group peaks tip position
group peaks width
group peaks height
More keys for the appearance of groups.
\@gtt@keydef{group right peak tip position}{.5}
\@gtt@keydef{group right peak width}{.4}
\@gtt@keydef{group right peak height}{.1}
\@gtt@keydef{group left peak tip position}{.5}
\@gtt@keydef{group left peak width}{.4}
\@gtt@keydef{group left peak height}{.1}
\ganttset{%
group peaks tip position/.code={%
\ganttset{%
group left peak tip position=#1,%
group right peak tip position=#1%
}%
},%
group peaks width/.code={%
\ganttset{%
group left peak width=#1,%
group right peak width=#1%
}%
},%
group peaks height/.code={%
\ganttset{%
group left peak height=#1,%
group right peak height=#1%
}%
}%
}
\ganttmilestone
\ganttlinkedmilestone
milestone
milestone incomplete
milestone label text
milestone label font
milestone label node
milestone inline label anchor
milestone inline label node
milestone progress label anchor
milestone progress label font
milestone progress label node
milestone left shift
milestone right shift
milestone top shift
milestone height
Code for the predefined chart element type milestone.
\newganttchartelement*{milestone}{%
milestone/.style={%
shape=ganttmilestone, inner sep=0pt, draw, fill=black%
},%
milestone incomplete/.style={/pgfgantt/milestone, fill=black!25},%
milestone label text=\strut#1,%
milestone label font=\itshape,%
milestone label node/.style={%
anchor=east, font=\ganttvalueof{milestone label font}%
},%
milestone inline label anchor=center,%
milestone inline label node/.style={%
anchor=south, font=\ganttvalueof{milestone label font}%
},%
milestone progress label anchor=center,%
milestone progress label font=\scriptsize,%
milestone progress label node/.style={%
anchor=west, font=\ganttvalueof{milestone progress label font}%
},%
milestone left shift=.6,%
milestone right shift=.4,%
milestone top shift=.3,%
milestone height=.4%
}
3.10Node Shapes¶
on top fraction
on bottom fraction
on left fraction
on right fraction
Keys for configuring the additional anchors of the new node shapes.
\@gtt@keydef{on top fraction}{.5}
\@gtt@keydef{on bottom fraction}{.5}
\@gtt@keydef{on left fraction}{.5}
\@gtt@keydef{on right fraction}{.5}
Code for node shape ganttbar. Anchors and background path derive from node shape rectangle. The four additional anchors on top, on bottom, on left and on right are defined.
\pgfdeclareshape{ganttbar}{
\inheritsavedanchors[from=rectangle]
\inheritanchor[from=rectangle]{center}
\inheritanchor[from=rectangle]{mid}
\inheritanchor[from=rectangle]{base}
\inheritanchor[from=rectangle]{north}
\inheritanchor[from=rectangle]{south}
\inheritanchor[from=rectangle]{west}
\inheritanchor[from=rectangle]{mid west}
\inheritanchor[from=rectangle]{base west}
\inheritanchor[from=rectangle]{north west}
\inheritanchor[from=rectangle]{south west}
\inheritanchor[from=rectangle]{east}
\inheritanchor[from=rectangle]{mid east}
\inheritanchor[from=rectangle]{base east}
\inheritanchor[from=rectangle]{north east}
\inheritanchor[from=rectangle]{south east}
\inheritanchorborder[from=rectangle]
\anchor{on top}{
\southwest
\pgf@xa=\pgf@x
\northeast
\pgf@xb=\pgf@x
\advance\pgf@xb by-\pgf@xa
\pgf@xb=\ganttvalueof{on top fraction}\pgf@xb
\advance\pgf@xa by\pgf@xb
\pgf@x=\pgf@xa
}
\anchor{on bottom}{
\northeast
\pgf@xb=\pgf@x
\southwest
\pgf@xa=\pgf@x
\advance\pgf@xb by-\pgf@xa
\pgf@xb=\ganttvalueof{on bottom fraction}\pgf@xb
\advance\pgf@xa by\pgf@xb
\pgf@x=\pgf@xa
}
\anchor{on left}{
\northeast
\pgf@ya=\pgf@y
\southwest
\pgf@yb=\pgf@y
\advance\pgf@yb by-\pgf@ya
\pgf@yb=\ganttvalueof{on left fraction}\pgf@yb
\advance\pgf@ya by\pgf@yb
\pgf@y=\pgf@ya
}
\anchor{on right}{
\southwest
\pgf@yb=\pgf@y
\northeast
\pgf@ya=\pgf@y
\advance\pgf@yb by-\pgf@ya
\pgf@yb=\ganttvalueof{on right fraction}\pgf@yb
\advance\pgf@ya by\pgf@yb
\pgf@y=\pgf@ya
}
\inheritbackgroundpath[from=rectangle]
}
Code for node shape ganttgroup. Anchors derive from node shape ganttbar. The two additional anchors left peak and right peak are defined.
\pgfdeclareshape{ganttgroup}{
\inheritsavedanchors[from=rectangle]
\inheritanchor[from=rectangle]{center}
\inheritanchor[from=rectangle]{mid}
\inheritanchor[from=rectangle]{base}
\inheritanchor[from=rectangle]{north}
\inheritanchor[from=rectangle]{south}
\inheritanchor[from=rectangle]{west}
\inheritanchor[from=rectangle]{mid west}
\inheritanchor[from=rectangle]{base west}
\inheritanchor[from=rectangle]{north west}
\inheritanchor[from=rectangle]{south west}
\inheritanchor[from=rectangle]{east}
\inheritanchor[from=rectangle]{mid east}
\inheritanchor[from=rectangle]{base east}
\inheritanchor[from=rectangle]{north east}
\inheritanchor[from=rectangle]{south east}
\inheritanchorborder[from=rectangle]
\inheritanchor[from=ganttbar]{on top}
\inheritanchor[from=ganttbar]{on bottom}
\inheritanchor[from=ganttbar]{on left}
\inheritanchor[from=ganttbar]{on right}
\anchor{left peak}{
\pgf@process{
\pgfpointadd{
\southwest
}{
\pgfpoint%
{\pgfkeysvalueof{/pgf/outer xsep}}%
{\pgfkeysvalueof{/pgf/outer ysep}}
}
}
\pgfmathsetlength\pgf@x{
\pgf@x + \ganttvalueof{group left peak tip position}
* \ganttvalueof{group left peak width} * \ganttvalueof{x unit}
}
\pgfmathsetlength\pgf@y{
\pgf@y - \ganttvalueof{group left peak height}
* \ganttvalueof{y unit chart}
}
}
\anchor{right peak}{
\pgf@process{
\pgfpointadd{
\northeast
}{
\pgfpointscale{-1}{
\pgfpoint%
{\pgfkeysvalueof{/pgf/outer xsep}}%
{\pgfkeysvalueof{/pgf/outer ysep}}
}
}
}
\pgf@xa=\pgf@x
\pgf@process{
\pgfpointadd{
\southwest
}{
\pgfpoint%
{\pgfkeysvalueof{/pgf/outer xsep}}%
{\pgfkeysvalueof{/pgf/outer ysep}}
}
}
\pgfmathsetlength\pgf@x{
\pgf@xa - \ganttvalueof{group right peak tip position}
* \ganttvalueof{group right peak width} * \ganttvalueof{x unit}
}
\pgfmathsetlength\pgf@y{
\pgf@y - \ganttvalueof{group right peak height}
* \ganttvalueof{y unit chart}
}
}
\backgroundpath{
\pgf@process{
\pgfpointadd{
\northeast
}{
\pgfpointscale{-1}{
\pgfpoint%
{\pgfkeysvalueof{/pgf/outer xsep}}%
{\pgfkeysvalueof{/pgf/outer ysep}}
}
}
}
\pgf@xb=\pgf@x
\pgf@ya=\pgf@y
\pgf@process{
\pgfpointadd{
\southwest
}{
\pgfpoint%
{\pgfkeysvalueof{/pgf/outer xsep}}%
{\pgfkeysvalueof{/pgf/outer ysep}}
}
}
\pgf@xa=\pgf@x
\pgf@yb=\pgf@y
\pgfpathmoveto{\pgfpoint{\pgf@xa}{\pgf@ya}}
\pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@ya}}
\pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@yb}}
\pgfmathsetlength\pgf@xc{
\pgf@xb - \ganttvalueof{group right peak tip position}
* \ganttvalueof{group right peak width} * \ganttvalueof{x unit}
}
\pgfmathsetlength\pgf@yc{
\pgf@yb - \ganttvalueof{group right peak height}
* \ganttvalueof{y unit chart}
}
\pgfpathlineto{\pgfpoint{\pgf@xc}{\pgf@yc}}
\pgfmathsetlength\pgf@xc{
\pgf@xb - \ganttvalueof{group right peak width}
* \ganttvalueof{x unit}
}
\pgfpathlineto{\pgfpoint{\pgf@xc}{\pgf@yb}}
\pgfmathsetlength\pgf@xc{
\pgf@xa + \ganttvalueof{group left peak width}
* \ganttvalueof{x unit}
}
\pgfpathlineto{\pgfpoint{\pgf@xc}{\pgf@yb}}
\pgfmathsetlength\pgf@xc{
\pgf@xa + \ganttvalueof{group left peak tip position}
* \ganttvalueof{group left peak width} * \ganttvalueof{x unit}
}
\pgfmathsetlength\pgf@yc{
\pgf@yb - \ganttvalueof{group left peak height}
* \ganttvalueof{y unit chart}
}
\pgfpathlineto{\pgfpoint{\pgf@xc}{\pgf@yc}}
\pgfpathlineto{\pgfpoint{\pgf@xa}{\pgf@yb}}
\pgfpathclose
}
}
Code for node shape ganttmilestone. Anchors and background path derive from node shape diamond. The four additional anchors on top, on bottom, on left and on right are defined.
\pgfdeclareshape{ganttmilestone}{
\inheritsavedanchors[from=diamond]
\inheritanchor[from=diamond]{text}
\inheritanchor[from=diamond]{center}
\inheritanchor[from=diamond]{mid}
\inheritanchor[from=diamond]{base}
\inheritanchor[from=diamond]{north}
\inheritanchor[from=diamond]{south}
\inheritanchor[from=diamond]{west}
\inheritanchor[from=diamond]{north west}
\inheritanchor[from=diamond]{south west}
\inheritanchor[from=diamond]{east}
\inheritanchor[from=diamond]{north east}
\inheritanchor[from=diamond]{south east}
\inheritanchorborder[from=diamond]
\inheritbackgroundpath[from=diamond]
\anchor{on top}{
\pgf@process{\outernortheast}
\pgf@xa=2\pgf@x
\pgf@x=-\pgf@x
\advance\pgf@x by\ganttvalueof{on top fraction}\pgf@xa
\pgf@ya=2\pgf@y
\pgf@y=0pt
\pgfmathparse{
\ganttvalueof{on top fraction} < 0.5
? \ganttvalueof{on top fraction}
: 1 - \ganttvalueof{on top fraction}
}
\advance\pgf@y by\pgfmathresult\pgf@ya
}
\anchor{on bottom}{
\pgf@process{\outernortheast}
\pgf@xa=2\pgf@x
\pgf@x=-\pgf@x
\advance\pgf@x by\ganttvalueof{on bottom fraction}\pgf@xa
\pgf@ya=-2\pgf@y
\pgf@y=0pt
\pgfmathparse{
\ganttvalueof{on bottom fraction} < 0.5
? \ganttvalueof{on bottom fraction}
: 1 - \ganttvalueof{on bottom fraction}
}
\advance\pgf@y by\pgfmathresult\pgf@ya
}
\anchor{on right}{
\pgf@process{\outernortheast}
\pgf@ya=-2\pgf@y
\advance\pgf@y by\ganttvalueof{on right fraction}\pgf@ya
\pgf@xa=2\pgf@x
\pgf@x=0pt
\pgfmathparse{
\ganttvalueof{on right fraction} < 0.5
? \ganttvalueof{on right fraction}
: 1 - \ganttvalueof{on right fraction}
}
\advance\pgf@x by\pgfmathresult\pgf@xa
}
\anchor{on left}{
\pgf@process{\outernortheast}
\pgf@ya=-2\pgf@y
\advance\pgf@y by\ganttvalueof{on left fraction}\pgf@ya
\pgf@xa=-2\pgf@x
\pgf@x=0pt
\pgfmathparse{
\ganttvalueof{on left fraction} < 0.5
? \ganttvalueof{on left fraction}
: 1 - \ganttvalueof{on left fraction}
}
\advance\pgf@x by\pgfmathresult\pgf@xa
}
}
3.11Links¶
link
link type
link label
link label font
link label node
Keys for configuring links.
\@gtt@stylekeydef{link}{-latex, rounded corners=1pt}
\@gtt@keydef{link type}{auto}
\@gtt@keydef{link label}{}
\@gtt@keydef{link label font}{\scriptsize\itshape}
\@gtt@stylekeydef{link label node}{%
anchor=west, font=\ganttvalueof{link label font}%
}
\newganttlinktype
\newganttlinktype{⟨type⟩}{⟨code⟩} stores ⟨code⟩ in an internal macro
\@gtt@linktype@⟨type⟩, which is later called by \gtt@drawlink.
\newcommand\newganttlinktype[2]{%
\expandafter\def\csname @gtt@linktype@#1\endcsname{#2}%
}
\setganttlinklabel
\setganttlinklabel{⟨type⟩}{⟨label⟩} stores a given ⟨label⟩ in an internal macro \@gtt@linktype@⟨type⟩@label, which is later used by \gtt@drawlink.
\newcommand\setganttlinklabel[2]{%
\expandafter\def\csname @gtt@linktype@#1@label\endcsname{#2}%
}
\newganttlinktypealias
\newganttlinktypealias{⟨new link type⟩}{⟨existing link type⟩} copies both the link code and label of an ⟨existing link type⟩ into the internal macros associated with a ⟨new link type⟩.
\newcommand\newganttlinktypealias[2]{%
\expandafter\def\csname @gtt@linktype@#1\endcsname{%
\csname @gtt@linktype@#2\endcsname%
}%
\expandafter\def\csname @gtt@linktype@#1@label\endcsname{%
\csname @gtt@linktype@#2@label\endcsname%
}%
}
link mid
link bulge
link tolerance
We will define three link subtypes for the type auto, which require the following keys:
\@gtt@keydef{link mid}{.5}
\@gtt@keydef{link bulge}{.4}
\@gtt@keydef{link tolerance}{.6}
(1) r (short for “right”) draws a straight arrow. Note that r and default are alias types.
\newganttlinktype{r}{%
\draw [/pgfgantt/link]
(\xLeft, \yUpper) --
(\xRight, \yLower)
node [pos=.5, /pgfgantt/link label node] {\ganttlinklabel};
}
\newganttlinktypealias{default}{r}
(2) rdr (“right-down-right”) is an unlabeled three-part arrow. The value of link mid sets the position of the middle segment.
\newganttlinktype{rdr}{%
\draw [/pgfgantt/link]
(\xLeft, \yUpper) --
($(\xLeft, \yUpper)!\ganttvalueof{link mid}!
(\xRight, \yUpper)$) --
($(\xLeft, \yLower)!\ganttvalueof{link mid}!
(\xRight, \yLower)$) --
(\xRight, \yLower);%
}
(3) rdldr (“right-down-left-down-right”) is an unlabeled five-part arrow, which considers the values of link bulge and link mid.
\newganttlinktype{rdldr}{%
\draw [/pgfgantt/link]
(\xLeft, \yUpper) --
(\xLeft + \ganttvalueof{link bulge} * \ganttvalueof{x unit},
\yUpper) --
($(\xLeft + \ganttvalueof{link bulge} * \ganttvalueof{x unit},
\yUpper)!%
\ganttvalueof{link mid}!%
(\xLeft + \ganttvalueof{link bulge} * \ganttvalueof{x unit},
\yLower)$) --
($(\xRight - \ganttvalueof{link bulge} * \ganttvalueof{x unit},
\yUpper)!%
\ganttvalueof{link mid}!%
(\xRight - \ganttvalueof{link bulge} * \ganttvalueof{x unit},
\yLower)$) --
(\xRight - \ganttvalueof{link bulge} * \ganttvalueof{x unit},
\yLower) --
(\xRight, \yLower);%
}
Now we may define linke type auto: The first and last coordinate of the link should touch the preceding or following element at the center of its right or left border, respectively. We check if the connected elements lie in the same row or not (i. e., their \(y\)-coordinates differ at most 1 pt). In the latter case, \pgfmathparse yields 0.
\newganttlinktype{auto}{%
\pgfmathparse{abs(\yUpper - \yLower) <= 1}%
\ifcase\pgfmathresult%
Once again, two possibilities arise: Either the elements to be connected are at least separated by link tolerance time slots, in which case we draw a three-part arrow (i. e., link type rdr). Alternatively, the elements lie in adjacent time slots or even overlap, in which case we draw a five-part arrow (i. e., link type rdldr).
\pgfmathparse{
(\xRight - \xLeft)
>= \ganttvalueof{link tolerance} * \ganttvalueof{x unit}
}%
\ifcase\pgfmathresult%
\gtt@drawlink{rdldr}%
\else%
\gtt@drawlink{rdr}%
\fi%
For elements that lie in the same row, we draw a simple arrow (i. e., link type r).
\else%
\gtt@drawlink{r}%
\fi%
}
The dr type is explained in section 2.9.
\newganttlinktype{dr}{%
\ganttsetstartanchor{south}%
\ganttsetendanchor{west}%
\draw [/pgfgantt/link]
(\xLeft, \yUpper) --
(\xLeft, \yLower)
node [pos=.5, /pgfgantt/link label node] {\ganttlinklabel} --
(\xRight, \yLower);%
}
Here is the definition of the four straight link types and their labels.
\newganttlinktype{s-s}{%
\ganttsetstartanchor{south west}%
\ganttsetendanchor{north west}%
\draw [/pgfgantt/link]
(\xLeft, \yUpper) --
(\xRight, \yLower)
node [pos=.5, /pgfgantt/link label node] {\ganttlinklabel};
}
\setganttlinklabel{s-s}{start-to-start}
\newganttlinktype{s-f}{%
\ganttsetstartanchor{on bottom=0}%
\ganttsetendanchor{on top=1}%
\draw [/pgfgantt/link]
(\xLeft, \yUpper) --
(\xRight, \yLower)
node [pos=.5, /pgfgantt/link label node] {\ganttlinklabel};
}
\setganttlinklabel{s-f}{start-to-finish}
\newganttlinktype{f-s}{%
\ganttsetstartanchor{south east}%
\ganttsetendanchor{north west}%
\draw [/pgfgantt/link]
(\xLeft, \yUpper) --
(\xRight, \yLower)
node [pos=.5, /pgfgantt/link label node] {\ganttlinklabel};
}
\setganttlinklabel{f-s}{finish-to-start}
\newganttlinktype{f-f}{%
\ganttsetstartanchor{south east}%
\ganttsetendanchor{north east}%
\draw [/pgfgantt/link]
(\xLeft, \yUpper) --
(\xRight, \yLower)
node [pos=.5, /pgfgantt/link label node] {\ganttlinklabel};
}
\setganttlinklabel{f-f}{finish-to-finish}
\gtt @drawlink ¶
\@gtt@currlinktype ¶
\gtt@drawlink{⟨link type⟩} first checks if the ⟨link type⟩ is defined, falling back to type default if it is unknown. \@gtt@currlinktype stores the link type for future reference.
\newcommand\gtt@drawlink[1]{%
\@ifundefined{@gtt@linktype@#1}{%
\@gtt@PackageWarning{Link type `#1' unknown, using `default'.}%
\def\@gtt@currlinktype{default}%
}{%
\def\@gtt@currlinktype{#1}%
}%
\@gtt@currlabel ¶
\ganttlinklabel
If the link label key contains any value, it locally overrides the label set by \setganttlinklabel. \ganttlinklabel is defined accordingly.
\edef\@gtt@currlabel{\ganttvalueof{link label}}%
\ifx\@gtt@currlabel\@empty%
\def\ganttlinklabel{%
\csname @gtt@linktype@\@gtt@currlinktype @label\endcsname%
}%
\else%
\edef\ganttlinklabel{%
\ganttvalueof{link label}%
}%
\fi%
Finally, we call the internal macro that stores the code for the desired link type.
\csname @gtt@linktype@\@gtt@currlinktype\endcsname%
}
\@gtt@link@anchor ¶
We need the following keys for setting the start and end anchor of a link: Whenever a key /pgfgantt/link anchor/⟨anchor⟩ is undefined, pgfgantt stores ⟨anchor⟩ in \@gtt@link@anchor.
\ganttset{%
link anchor/.unknown/.code={%
\edef\@gtt@link@anchor{\pgfkeyscurrentname}%
},%
}
\@gtt@linkanchordef ¶
\@gtt@linkanchordef{⟨anchor⟩} deals with the anchors on top etc.: It creates a code key /pgfgantt/link anchor/⟨anchor⟩, which stores its own name in \@gtt@link@anchor and sets the appropriate …fraction key.
\def\@gtt@linkanchordef#1{%
\ganttset{%
link anchor/#1/.code={%
\edef\@gtt@link@anchor{#1}%
\ganttset{#1 fraction=##1}%
},%
link anchor/#1/.default=.5%
}%
}
\@gtt@linkanchordef{on top}
\@gtt@linkanchordef{on bottom}
\@gtt@linkanchordef{on left}
\@gtt@linkanchordef{on right}
\@gtt@setstartanchor ¶
\xLeft
\yUpper
\@gtt@setstartanchor{⟨anchor⟩} recalls the coordinates of the anchor stored in \@gtt@link@anchor from chart element \@gtt@link@startelement. It stores these coordinates in the auxiliary macros \xLeft and \yUpper.
\newcommand\@gtt@setstartanchor[1]{%
\pgfqkeys{/pgfgantt/link anchor}{#1}%
\pgfpointanchor{\@gtt@link@startelement}{\@gtt@link@anchor}%
\edef\xLeft{\the\pgf@x}%
\edef\yUpper{\the\pgf@y}%
}
\@gtt@setendanchor ¶
\xRight
\yLower
\@gtt@setendanchor{⟨anchor⟩} is similar to the command above. However, it stores the anchor coordinates in the auxiliary macros \xRight and \yLower.
\newcommand\@gtt@setendanchor[1]{%
\pgfqkeys{/pgfgantt/link anchor}{#1}%
\pgfpointanchor{\@gtt@link@endelement}{\@gtt@link@anchor}%
\edef\xRight{\the\pgf@x}%
\edef\yLower{\the\pgf@y}%
}
\ganttlink
\@gtt@link@startelement ¶
\@gtt@link@endelement ¶
\ganttlink[⟨options⟩]{⟨E1⟩}{⟨E2⟩} executes the ⟨options⟩ and stores the names of the connected elements ⟨E1⟩ and ⟨E2⟩ in \@gtt@link@startelement and \@gtt@link@endelement.
\newcommand\ganttlink[3][]{%
\begingroup%
\ganttset{#1}%
\def\@gtt@link@startelement{#2}%
\def\@gtt@link@endelement{#3}%
\ganttsetstartanchor
\ganttsetendanchor
\ganttsetstartanchor and \ganttsetendanchor are only valid in the second argument of \newganttlinktype. Since you may wish to omit one of those commands, we set default anchors for the link.
\let\ganttsetstartanchor\@gtt@setstartanchor%
\let\ganttsetendanchor\@gtt@setendanchor%
\ganttsetstartanchor{east}%
\ganttsetendanchor{west}%
We call \gtt@drawlink with the value of link type.
\gtt@drawlink{\ganttvalueof{link type}}%
\endgroup%
}