├── l3charts-doc.pdf ├── README.md ├── l3charts-doc.ist ├── .gitignore ├── l3charts-doc.cls ├── LICENSE ├── l3charts.sty └── l3charts-doc.tex /l3charts-doc.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eburghar/l3charts/HEAD/l3charts-doc.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # l3charts LaTeX package 2 | 3 | This package defines a few simple TikZ charts that can be drawn using LaTeX 4 | environments. 5 | 6 | See examples in the [included documentation](l3charts-doc.pdf). 7 | 8 | # Motivation 9 | 10 | This package has been developed mainly to typeset a fancy résumé, but perhaps it could be 11 | used in other contexts too. I didn't want to write TikZ charts directly in the document 12 | as it would have turned a simple typesetting file into an unreadable document, and I would have 13 | forgotten every detail after just a few months. 14 | 15 | I wouldn't have the patience to develop this with LaTeX or TeX either, but I was curious enough 16 | about `expl3` to try an implementation. You should probably take this package as a rough 17 | tutorial on how to develop with `expl3` because it uses nearly all the types defined in the 18 | reference documentation (expansion control, `seq`, `prop`, `keys`, `int`, `bool`, `fp`, `dim`, 19 | `msg`, ...) in straightforward ways. 20 | 21 | TeX will always be that dusty tech you can't ignore because there are so many (unmatched) 22 | packages coming from academic circles. `expl3` gives a touch of modernity and facilitates a 23 | lot package development by allowing to easily bridge LaTeX packages together (here LaTeX and TikZ). 24 | 25 | # Installation 26 | 27 | It is not yet published on [CTAN](https://www.ctan.org), but you can clone the 28 | repository and do a `l3build install`. 29 | 30 | ```sh 31 | git clone https://github.com/eburghar/l3charts 32 | cd l3charts 33 | l3build install 34 | ``` 35 | -------------------------------------------------------------------------------- /l3charts-doc.ist: -------------------------------------------------------------------------------- 1 | %% 2 | %% This is file `microtype-gind.ist', 3 | %% generated with the docstrip utility. 4 | %% 5 | %% The original source files were: 6 | %% 7 | %% microtype.dtx (with options: `docist') 8 | %% 9 | %% ------------------------------------------------------------------------ 10 | %% 11 | %% The `microtype' package 12 | %% Subliminal refinements towards typographical perfection 13 | %% Copyright (c) 2004--2022 R Schlicht 14 | %% 15 | %% This work may be distributed and/or modified under the conditions of the 16 | %% LaTeX Project Public License, either version 1.3c of this license or (at 17 | %% your option) any later version. The latest version of this license is in: 18 | %% https://www.latex-project.org/lppl.txt, and version 1.3c or later is part 19 | %% of all distributions of LaTeX version 2005/12/01 or later. 20 | %% 21 | %% This work has the LPPL maintenance status `maintained'. 22 | %% 23 | %% This work consists of the files microtype.dtx, microtype-utf.dtx and 24 | %% microtype.ins and the derived files microtype.sty, microtype-pdftex.def, 25 | %% microtype-luatex.def, microtype-xetex.def, microtype.lua, letterspace.sty 26 | %% and microtype-show.sty. 27 | %% 28 | %% ------------------------------------------------------------------------ 29 | %% 30 | actual '=' 31 | quote '!' 32 | level '>' 33 | preamble "\n \\begin{theindex} \n \\makeatletter\\scan@allowedfalse\n\\indexspace" 34 | postamble "\n\n \\end{theindex}\n" 35 | item_x1 "\\efill \n \\subitem " 36 | delim_0 "\\pfill " 37 | delim_1 "\\pfill " 38 | heading_prefix "{\\bfseries\\hfil " 39 | heading_suffix "\\hfil}\\nopagebreak\n" 40 | headings_flag 1 41 | symhead_positive "Options" 42 | numhead_positive "Commands" 43 | %% 44 | %% 45 | %% End of file `microtype-gind.ist'. 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Created by https://www.gitignore.io 4 | ### LaTeX ### 5 | ## Core latex/pdflatex auxiliary files: 6 | *.aux 7 | *.lof 8 | *.log 9 | *.lot 10 | *.fls 11 | *.out 12 | *.toc 13 | *.fmt 14 | *.fot 15 | *.cb 16 | *.cb2 17 | .*.lb 18 | 19 | ## Intermediate documents: 20 | *.dvi 21 | *.xdv 22 | *-converted-to.* 23 | # these rules might exclude image files for figures etc. 24 | # *.ps 25 | # *.eps 26 | # *.pdf 27 | 28 | ## Generated if empty string is given at "Please type another file name for output:" 29 | .pdf 30 | 31 | ## Bibliography auxiliary files (bibtex/biblatex/biber): 32 | *.bbl 33 | *.bcf 34 | *.blg 35 | *-blx.aux 36 | *-blx.bib 37 | *.run.xml 38 | 39 | ## Build tool auxiliary files: 40 | *.fdb_latexmk 41 | *.synctex 42 | *.synctex(busy) 43 | *.synctex.gz 44 | *.synctex.gz(busy) 45 | *.pdfsync 46 | 47 | ## Build tool directories for auxiliary files 48 | # latexrun 49 | latex.out/ 50 | 51 | ## Auxiliary and intermediate files from other packages: 52 | # algorithms 53 | *.alg 54 | *.loa 55 | 56 | # achemso 57 | acs-*.bib 58 | 59 | # amsthm 60 | *.thm 61 | 62 | # beamer 63 | *.nav 64 | *.pre 65 | *.snm 66 | *.vrb 67 | 68 | # changes 69 | *.soc 70 | 71 | # comment 72 | *.cut 73 | 74 | # cprotect 75 | *.cpt 76 | 77 | # elsarticle (documentclass of Elsevier journals) 78 | *.spl 79 | 80 | # endnotes 81 | *.ent 82 | 83 | # fixme 84 | *.lox 85 | 86 | # feynmf/feynmp 87 | *.mf 88 | *.mp 89 | *.t[1-9] 90 | *.t[1-9][0-9] 91 | *.tfm 92 | 93 | #(r)(e)ledmac/(r)(e)ledpar 94 | *.end 95 | *.?end 96 | *.[1-9] 97 | *.[1-9][0-9] 98 | *.[1-9][0-9][0-9] 99 | *.[1-9]R 100 | *.[1-9][0-9]R 101 | *.[1-9][0-9][0-9]R 102 | *.eledsec[1-9] 103 | *.eledsec[1-9]R 104 | *.eledsec[1-9][0-9] 105 | *.eledsec[1-9][0-9]R 106 | *.eledsec[1-9][0-9][0-9] 107 | *.eledsec[1-9][0-9][0-9]R 108 | 109 | # glossaries 110 | *.acn 111 | *.acr 112 | *.glg 113 | *.glo 114 | *.gls 115 | *.glsdefs 116 | *.lzo 117 | *.lzs 118 | 119 | # uncomment this for glossaries-extra (will ignore makeindex's style files!) 120 | # *.ist 121 | 122 | # gnuplottex 123 | *-gnuplottex-* 124 | 125 | # gregoriotex 126 | *.gaux 127 | *.glog 128 | *.gtex 129 | 130 | # htlatex 131 | *.4ct 132 | *.4tc 133 | *.idv 134 | *.lg 135 | *.trc 136 | *.xref 137 | 138 | # hyperref 139 | *.brf 140 | 141 | # knitr 142 | *-concordance.tex 143 | # TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files 144 | # *.tikz 145 | *-tikzDictionary 146 | 147 | # listings 148 | *.lol 149 | 150 | # luatexja-ruby 151 | *.ltjruby 152 | 153 | # makeidx 154 | *.idx 155 | *.ilg 156 | *.ind 157 | 158 | # minitoc 159 | *.maf 160 | *.mlf 161 | *.mlt 162 | *.mtc[0-9]* 163 | *.slf[0-9]* 164 | *.slt[0-9]* 165 | *.stc[0-9]* 166 | 167 | # minted 168 | _minted* 169 | *.pyg 170 | 171 | # morewrites 172 | *.mw 173 | 174 | # newpax 175 | *.newpax 176 | 177 | # nomencl 178 | *.nlg 179 | *.nlo 180 | *.nls 181 | 182 | # pax 183 | *.pax 184 | 185 | # pdfpcnotes 186 | *.pdfpc 187 | 188 | # sagetex 189 | *.sagetex.sage 190 | *.sagetex.py 191 | *.sagetex.scmd 192 | 193 | # scrwfile 194 | *.wrt 195 | 196 | # sympy 197 | *.sout 198 | *.sympy 199 | sympy-plots-for-*.tex/ 200 | 201 | # pdfcomment 202 | *.upa 203 | *.upb 204 | 205 | # pythontex 206 | *.pytxcode 207 | pythontex-files-*/ 208 | 209 | # tcolorbox 210 | *.listing 211 | 212 | # thmtools 213 | *.loe 214 | 215 | # TikZ & PGF 216 | *.dpth 217 | *.md5 218 | *.auxlock 219 | 220 | # todonotes 221 | *.tdo 222 | 223 | # vhistory 224 | *.hst 225 | *.ver 226 | 227 | # easy-todo 228 | *.lod 229 | 230 | # xcolor 231 | *.xcp 232 | 233 | # xmpincl 234 | *.xmpi 235 | 236 | # xindy 237 | *.xdy 238 | 239 | # xypic precompiled matrices and outlines 240 | *.xyc 241 | *.xyd 242 | 243 | # endfloat 244 | *.ttt 245 | *.fff 246 | 247 | # Latexian 248 | TSWLatexianTemp* 249 | 250 | ## Editors: 251 | # WinEdt 252 | *.bak 253 | *.sav 254 | 255 | # Texpad 256 | .texpadtmp 257 | 258 | # LyX 259 | *.lyx~ 260 | 261 | # Kile 262 | *.backup 263 | 264 | # gummi 265 | .*.swp 266 | 267 | # KBibTeX 268 | *~[0-9]* 269 | 270 | # TeXnicCenter 271 | *.tps 272 | 273 | # auto folder when using emacs and auctex 274 | ./auto/* 275 | *.el 276 | 277 | # expex forward references with \gathertags 278 | *-tags.tex 279 | 280 | # standalone packages 281 | *.sta 282 | 283 | # Makeindex log files 284 | *.lpz 285 | 286 | # xwatermark package 287 | *.xwm 288 | 289 | # REVTeX puts footnotes in the bibliography by default, unless the nofootinbib 290 | # option is specified. Footnotes are the stored in a file with suffix Notes.bib. 291 | # Uncomment the next line to have this generated file ignored. 292 | #*Notes.bib 293 | 294 | ### LaTeX Patch ### 295 | # LIPIcs / OASIcs 296 | *.vtc 297 | 298 | # glossaries 299 | *.glstex 300 | 301 | build/ 302 | .latexmkrc 303 | -------------------------------------------------------------------------------- /l3charts-doc.cls: -------------------------------------------------------------------------------- 1 | \NeedsTeXFormat{LaTeX2e} 2 | \ProvidesClass{l3charts-doc}[2022/07/02 Customized LaTeX documentation class] 3 | 4 | \DeclareOption*{\PassOptionsToClass{\CurrentOption}{ltxdoc}} 5 | \ProcessOptions\relax 6 | \LoadClass{ltxdoc} 7 | 8 | \usepackage{geometry} 9 | % bigger left margin for doc pages 10 | \geometry{ 11 | paper=a4paper, 12 | vmargin={2cm,2cm}, 13 | hmargin={4.7cm,1cm}, 14 | % showframe % Uncomment to show how the type block is set on the page 15 | } 16 | 17 | % for ifpdf 18 | \usepackage{iftex} 19 | \usepackage{fontspec} 20 | \usepackage[default]{raleway} 21 | % you need a license for this (https://www.monolisa.dev) 22 | \setmonofont{MonoLisa}[ 23 | ItalicFeatures={StylisticSet=2}, 24 | BoldItalicFeatures={StylisticSet=2}, 25 | Scale=MatchLowercase] 26 | 27 | \def\MacroFont{\ttfamily\small} 28 | \def\PackageFont{\ttfamily} 29 | 30 | % space between paragraphs. Remove first line indent 31 | \usepackage[indent=0pt]{parskip} 32 | 33 | \usepackage{xcolor} 34 | \definecolor{thered} {rgb} {0.65,0.04,0.07} 35 | \definecolor{thegreen} {rgb} {0.06,0.44,0.08} 36 | \definecolor{theblue} {rgb} {0.02,0.04,0.48} 37 | \definecolor{sectioning}{gray}{0.44} 38 | \definecolor{thegrey} {gray}{0.5} 39 | \definecolor{theframe} {gray}{0.75} 40 | \definecolor{theshade} {gray}{0.94} 41 | 42 | \usepackage[bookmarks,pdfdisplaydoctitle, 43 | colorlinks,linkcolor=theblue,citecolor=theblue,urlcolor=thered, 44 | hyperindex=false,hyperfootnotes=false]{hyperref} 45 | \usepackage{hyperxmp}% more attributes in hypersetup 46 | 47 | % Define an example environment. Borrowed from skdoc.cls 48 | \usepackage{moreverb} 49 | \usepackage{minted} 50 | \setminted{breaklines=true} 51 | \setminted{breakanywhere=true} 52 | \setminted{fontsize=\small} 53 | % \setminted{bgcolor=theshade} 54 | \usepackage{needspace} 55 | 56 | \ExplSyntaxOn 57 | \bool_new:N\g__skdoc_use_minted_bool 58 | \bool_gset_true:N\g__skdoc_use_minted_bool 59 | \bool_new:N\g__skdoc_no_index_bool 60 | \bool_gset_false:N\g__skdoc_no_index_bool 61 | \bool_new:N\g__skdoc_in_example_bool 62 | \bool_gset_true:N\g__skdoc_in_example_bool 63 | \bool_new:N\g__skdoc_with_implementation_bool 64 | \bool_gset_true:N\g__skdoc_with_implementation_bool 65 | \bool_new:N\g__skdoc_in_implementation_bool 66 | \bool_gset_false:N\g__skdoc_in_implementation_bool 67 | \bool_new:N\g__skdoc_negative_space_bool 68 | \bool_gset_false:N\g__skdoc_negative_space_bool 69 | \prg_new_conditional:Nnn\__skdoc_if_print_code:{p,T,F,TF}{ 70 | \bool_if:nTF{ 71 | \g__skdoc_in_implementation_bool && 72 | !\g__skdoc_with_implementation_bool 73 | }{ 74 | \prg_return_false: 75 | }{ 76 | \prg_return_true: 77 | } 78 | } 79 | \tl_new:N\skdoc@temptl 80 | \ior_new:N\skdoc@input 81 | \iow_new:N\skdoc@output 82 | 83 | \clist_new:N\l__skdoc_keys 84 | \DeclareDocumentEnvironment{skdoc@verbatim}{m}{% 85 | \clist_set:Nn\l__skdoc_keys{#1} 86 | \clist_map_inline:Nn\l__skdoc_keys{ 87 | \int_if_exist:cTF{skdoc@output@##1@line}{}{ 88 | \msg_critical:nnn{skdoc}{key-nexists}{##1} 89 | }% 90 | \int_compare:nNnT{\int_use:c{skdoc@output@##1@line}}=\c_zero_int% 91 | {\int_gincr:c{skdoc@output@##1@line}}% 92 | } 93 | \__skdoc_if_print_code:T{ 94 | \bool_if:NTF\g__skdoc_use_minted_bool{ 95 | \bool_if:NF\g__skdoc_in_example_bool{ 96 | \setkeys{minted@opt@g}{linenos} 97 | }% 98 | \exp_args:Nnx\setkeys{FV}{ 99 | firstnumber=\int_use:c{skdoc@output@#1@line} 100 | }% 101 | \iow_open:Nn\minted@code{\jobname.pyg}% 102 | \Needspace*{2\baselineskip}% 103 | }{ 104 | \bool_if:NF\g__skdoc_in_example_bool{\@bsphack}% 105 | } 106 | \bool_if:NF\g__skdoc_in_example_bool{ 107 | \marginnote{ 108 | \leavevmode 109 | \llap{ 110 | \scriptsize\color{gray} 111 | $\langle$#1$\rangle$ 112 | \makebox[2ex]{\strut} 113 | } 114 | } 115 | } 116 | } 117 | \def\verbatim@processline{% 118 | \clist_map_inline:Nn\l__skdoc_keys{ 119 | \tl_gput_right:cx{skdoc@output@####1}{\the\verbatim@line\iow_newline:}% 120 | } 121 | \__skdoc_if_print_code:T{ 122 | \bool_if:NTF\g__skdoc_use_minted_bool{ 123 | \iow_now:Nx\minted@code{\the\verbatim@line}% 124 | }{ 125 | \noindent\leavevmode% 126 | \bool_if:NF\g__skdoc_in_example_bool{\hspace*{-5ex}} 127 | \begin{minipage}[c][1ex]{\textwidth} 128 | \bool_if:nF{ 129 | \g__skdoc_in_example_bool && 130 | !\int_compare_p:nNn{\clist_count:N\l__skdoc_keys}>\c_one_int 131 | }{ 132 | \makebox[4ex]{% 133 | \leavevmode 134 | \sffamily\tiny\color{lightgray}\hfill% 135 | \clist_map_inline:Nn\l__skdoc_keys{ 136 | \oldstylenums{\int_use:c{skdoc@output@####1@line}}% 137 | } 138 | }% 139 | \hspace*{1ex}% 140 | } 141 | { 142 | \verbatim@font 143 | \the\verbatim@line 144 | } 145 | \end{minipage} 146 | \par 147 | } 148 | } 149 | \clist_map_inline:Nn\l__skdoc_keys{ 150 | \int_gincr:c{skdoc@output@####1@line}% 151 | } 152 | }% 153 | \group_begin: 154 | \let\do\@makeother\dospecials\catcode`\^^M\active% 155 | \bool_if:nT{ 156 | \__skdoc_if_print_code_p: && 157 | !\g__skdoc_use_minted_bool 158 | }{ 159 | \frenchspacing\@vobeyspaces 160 | } 161 | \verbatim@start% 162 | }{% 163 | \group_end: 164 | \__skdoc_if_print_code:T{ 165 | \bool_if:NTF\g__skdoc_use_minted_bool{ 166 | \iow_close:N\minted@code% 167 | \bool_if:NF\g__skdoc_in_example_bool{ 168 | \vspace*{-\topsep} 169 | \vspace*{-\partopsep} 170 | \vspace*{-\parskip} 171 | } 172 | \small % surely not the best place for this 173 | \minted@pygmentize{latex}% 174 | \DeleteFile{\jobname.pyg}% 175 | \vspace*{-\topsep} 176 | \vspace*{-\partopsep} 177 | }{ 178 | \bool_if:NF\g__skdoc_in_example_bool{\@esphack}% 179 | } 180 | }% 181 | } 182 | 183 | \dim_const:Nn\c__skdoc_example_margin_dim{0.5\baselineskip} 184 | \dim_const:Nn\c__skdoc_example_linewidth_dim{1pt} 185 | \coffin_new:N\l__skdoc_example_code_coffin 186 | \coffin_new:N\l__skdoc_example_divider_coffin 187 | \coffin_new:N\l__skdoc_example_result_coffin 188 | \DeclareDocumentEnvironment{example}{}{ 189 | \bool_gset_true:N\g__skdoc_in_example_bool% 190 | %\minisec{Example:}% 191 | \int_zero_new:c{skdoc@output@skdoc@private@example@line}% 192 | \tl_if_exist:cTF{skdoc@output@skdoc@private@example}{ 193 | \tl_clear:c{skdoc@output@skdoc@private@example} 194 | }{ 195 | \tl_new:c{skdoc@output@skdoc@private@example} 196 | } 197 | \dim_set:Nn\l_tmpa_dim{ \textwidth/2 198 | -\c__skdoc_example_margin_dim 199 | -\c__skdoc_example_linewidth_dim/2} 200 | \coffin_clear:N\l__skdoc_example_code_coffin 201 | \vcoffin_set:Nnw\l__skdoc_example_code_coffin{\l_tmpa_dim} 202 | \skdoc@verbatim{skdoc@private@example} 203 | }{ 204 | \endskdoc@verbatim 205 | \vcoffin_set_end: 206 | \coffin_clear:N\l__skdoc_example_result_coffin 207 | \vcoffin_set:Nnw\l__skdoc_example_result_coffin{\l_tmpa_dim} 208 | \iow_open:Nn\skdoc@output{\jobname.skdoc.tmp} 209 | \iow_now:Nx\skdoc@output{\tl_to_str:c{skdoc@output@skdoc@private@example}} 210 | \iow_close:N\skdoc@output 211 | \input{\jobname.skdoc.tmp} 212 | \vcoffin_set_end: 213 | \coffin_clear:N\l__skdoc_example_divider_coffin 214 | \dim_set:Nn\l_tmpa_dim{ 215 | \dim_max:nn{\coffin_ht:N\l__skdoc_example_code_coffin}% 216 | {\coffin_ht:N\l__skdoc_example_result_coffin} 217 | + 2\c__skdoc_example_margin_dim} 218 | \hcoffin_set:Nn\l__skdoc_example_divider_coffin{ 219 | \color{lightgray} 220 | \hspace*{\c__skdoc_example_margin_dim} 221 | \rule{\c__skdoc_example_linewidth_dim}{\l_tmpa_dim} 222 | \hspace*{\c__skdoc_example_margin_dim} 223 | } 224 | \coffin_clear:N\l_tmpa_coffin 225 | \coffin_set_eq:NN\l_tmpa_coffin\l__skdoc_example_divider_coffin 226 | \coffin_join:NnnNnnnn\l_tmpa_coffin{l}{vc}% 227 | \l__skdoc_example_result_coffin{r}{vc}% 228 | {0pt}{0pt} 229 | \coffin_join:NnnNnnnn\l_tmpa_coffin{r}{vc}% 230 | \l__skdoc_example_code_coffin{l}{vc}% 231 | {0pt}{0pt} 232 | \coffin_typeset:Nnnnn\l_tmpa_coffin{T}{l}{0pt}{0pt} 233 | \bool_gset_false:N\g__skdoc_in_example_bool% 234 | \vspace*{\c__skdoc_example_margin_dim}\par 235 | } 236 | \ExplSyntaxOff 237 | 238 | 239 | % Macros and environment 240 | %% format property list for options 241 | \NewDocumentEnvironment{props}{o}{%% 242 | \NewDocumentCommand\prop{mom}{\texttt{##1} & \IfValueT{##2}{\texttt{##2}} & ##3\\ \addlinespace} 243 | #1 is comma separated list of the following keywords : 244 | 245 | \begin{tabular}{lL{0.3\linewidth}L{0.4\linewidth}} \toprule 246 | Key & Default value & Description \\ 247 | \midrule 248 | } { 249 | \bottomrule 250 | \end{tabular} 251 | } 252 | 253 | \NewDocumentCommand\TikZ{}{Ti\textit{k}Z}% typeset TikZ 254 | 255 | % title 256 | \def\@maketitle{% 257 | % \newpage\null 258 | \begin{center}\sffamily 259 | {\huge \@title\par\vskip 1em}% 260 | {\large\parbox{.33\textwidth}{\hfil\@author\hfil}% 261 | \parbox{.33\textwidth}{\hfil\@date\hfil}\par\vskip 1em 262 | \url{\githuburl}}% 263 | \vskip 2em\rule{\textwidth}{.4pt}% 264 | \end{center}\par\vskip 2em} 265 | 266 | % headings 267 | \def\ps@MTheadings{% 268 | \def\@oddhead{% 269 | \hbox to\textwidth{\vbox{\hbox to\textwidth{% 270 | \footnotesize\sffamily{\leftmark\rightmark\strut}\hfill\thepage\strut}% 271 | \hrule height 0.4pt width\textwidth \vskip-0.4pt 272 | }}\hss} 273 | \let\@oddfoot\@empty 274 | \let\@mkboth\markboth 275 | \def\sectionmark##1{\markboth{\MakeUppercase{##1}}{}} 276 | \def\subsectionmark##1{\markright{\,: ##1}}} 277 | \pagestyle{MTheadings} 278 | 279 | % lists 280 | \setlength\leftmargini{15pt} 281 | \setlength\leftmarginii{12.5pt} 282 | \setlength\leftmarginiii{10pt} 283 | \def\@listi{\leftmargin \leftmargini 284 | \parsep 4.5pt plus 1pt minus 1pt 285 | \topsep 4.5pt plus 1pt minus 1pt 286 | \itemsep 0pt} 287 | \let\@listI\@listi 288 | \def\descriptionlabel#1{\hspace\labelsep\normalfont#1:} 289 | % 290 | \renewenvironment{itemize}{% 291 | \ifnum \@itemdepth >\thr@@\@toodeep\else 292 | \advance\@itemdepth\@ne 293 | \edef\@itemitem{labelitem\romannumeral\the\@itemdepth}% 294 | \expandafter\list 295 | \csname\@itemitem\endcsname 296 | {\ifnum\@itemdepth=\@ne\leftmargin 0pt\fi 297 | \def\makelabel##1{\hss\llap{\color{sectioning}##1}}}% 298 | \fi 299 | }{\endlist} 300 | % 301 | \newenvironment{enum}[1][0]{% 302 | \list\labelenumi 303 | {\usecounter{enumi}\setcounter{enumi}{#1}\addtocounter{enumi}{-1}% 304 | \renewcommand\labelenumi{\texttt{\theenumi}:}% 305 | \leftmargin 30pt 306 | \itemindent-15pt 307 | \labelwidth 15pt 308 | \labelsep 0pt 309 | \def\makelabel##1{##1\hss}} 310 | }{\endlist} 311 | % 312 | \newenvironment{options}{% 313 | \list{} 314 | {\leftmargin 0pt 315 | \labelwidth 0pt 316 | \labelsep 1em 317 | \itemindent \labelsep 318 | \lstset{belowskip=0pt}} 319 | }{\endlist} 320 | 321 | % Sections numbers colored and in the margin 322 | \def\@seccntformat#1{\llap{\csname the#1\endcsname\hskip\marginparsep}} 323 | \def\MTsectionfont{\bfseries\sffamily\ralewaythin\color{sectioning}} 324 | \patchcmd\section {\bfseries}{\MTsectionfont}\relax\relax 325 | \patchcmd\subsection {\bfseries}{\MTsectionfont}\relax\relax 326 | \patchcmd\subsubsection{\bfseries}{\MTsectionfont}\relax\relax 327 | \def\paragraph{\@startsection{paragraph}{4}% 328 | {0pt}{8pt plus 2pt minus 1pt}{-1em}% 329 | {\normalfont\normalsize\itshape}} 330 | 331 | % Change how Env and Macro appears in the margin by overriding doc package macros 332 | \def\PrintDescribeEnv#1{\strut\MacroFont\bslash begin\{{\color{thegreen}#1}\}% 333 | \\*[.25\baselineskip]\strut\bslash end\{{\color{thegreen}#1}\}} 334 | \def\PrintDescribeMacro#1{\strut\MacroFont\color{thegreen}\textbackslash\string #1} 335 | \def\DescribeOption{\leavevmode\@bsphack 336 | \begingroup\MakePrivateLetters\Describe@Option} 337 | \def\Describe@Option#1{\endgroup 338 | \marginpar{\raggedleft\PrintDescribeOption{#1}}% 339 | \SpecialOptionIndex{#1}\@esphack\ignorespaces} 340 | \def\PrintDescribeOption#1{\strut\MacroFont\color{thered}#1} 341 | 342 | % Colored index letters in the margin. 2 columns index 343 | \setcounter{IndexColumns}{2} 344 | \def\IndexMin{12\baselineskip} 345 | \g@addto@macro\IndexParms{% 346 | \small 347 | \def\indexspace#1{% 348 | \end{multicols} 349 | \vspace{-20pt}% 350 | \begin{multicols}{2} 351 | \ifpdf{\let\bfseries\empty\let\hfil\empty\phantomsection\pdfbookmark[2]{#1}{#1}}\fi 352 | \setbox0\hbox{\sffamily\hss#1}% 353 | \ifdim\wd0<1em \setbox0\hbox to 1em{\sffamily\hss#1\hss}\fi 354 | \llap{\color{thegrey}\box0\hskip\marginparsep}% 355 | \vspace*{-\baselineskip}% 356 | \IndexParms %\rightskip 15pt 357 | \let\item\@idxitem 358 | \raggedcolumns}} 359 | \IndexPrologue{\section{Index}% 360 | Numbers in upright shape refer to the \textit{page} where the corresponding entry 361 | is described (bold face) resp. occurs. 362 | } 363 | \DeclareRobustCommand\textoractual[2]{\ifpdf 364 | \pdfliteral direct{/Span<>BDC}#1\pdfliteral direct{EMC}% 365 | \else #1\fi} 366 | % additional bells ... 367 | \def\Describe#1#2#3{\noindent\csname Describe#1\endcsname{#2}% 368 | \DescribeValues{#1}{#3}} 369 | \def\DescribeOption{\leavevmode\@bsphack 370 | \begingroup\MakePrivateLetters\Describe@Option} 371 | \def\Describe@Option#1{\endgroup 372 | \marginpar{\raggedleft\PrintDescribeOption{#1}}% 373 | \SpecialOptionIndex{#1}\@esphack\ignorespaces} 374 | \def\DescribePackage{\leavevmode\@bsphack 375 | \begingroup\MakePrivateLetters\Describe@Package} 376 | \def\Describe@Package#1{\endgroup 377 | \marginpar{\raggedleft\PrintDescribeOption{#1.sty}}% 378 | \CatMainIndex{#1}{package}\@esphack\ignorespaces} 379 | \def\DescribeValues#1#2{% 380 | \let\@tempa\@empty \let\Option@default\@empty 381 | \@for\@tempb:=#2\do{% 382 | \csname Special#1Value\expandafter\endcsname\@tempb\@nil 383 | \expandafter\g@addto@macro\expandafter\@tempa 384 | \expandafter{\csname #1Sep\endcsname}% 385 | \expandafter\g@addto@macro\expandafter\@tempa 386 | \expandafter{\@tempb}}% 387 | \@ifnextchar[\PrintValues{\PrintValues[\Option@default]}} 388 | \def\SpecialOptionValue#1#2\@nil{% 389 | \if#1:\def\@tempb{\Variable{#2}}\else % : = variable 390 | \if#1!\def\@tempb{#2}\def\Option@default{#2}\else % ! = default 391 | \if#1*\def\@tempb{#2}\def\Option@default{\MaybeDefault{#2}}% * = default (maybe) 392 | \fi\fi\fi} 393 | \def\SpecialMacroValue#1#2\@nil{% 394 | \if#1?\def\@tempb{\normalsize[\Variable{#2}]}% % ? = optional 395 | \else\def\@tempb{\normalsize\{\Variable{#1#2}\}}\fi} 396 | \let\SpecialEnvValue\SpecialMacroValue 397 | \DeclareRobustCommand\langlechar{<} % for makeindex 398 | \DeclareRobustCommand\ranglechar{>} 399 | \def\Variable#1{% 400 | \textoractual{$\langle$}{\langlechar}% 401 | {\rmfamily\itshape#1}% 402 | \textoractual{$\rangle$}{\ranglechar}} 403 | \let\m@a\meta \def\meta#1{\textoractual{\m@a{#1}}{\langlechar#1\ranglechar}} 404 | \def\MaybeDefault#1{\textrm{*}\,#1} 405 | \def\OptionSep{{\rmfamily, }} \def\MacroSep{\,} \def\EnvironmentSep{\,} 406 | \def\PrintValues[#1]{{\MacroFont\expandafter\@gobble\@tempa\hfill #1}\\*[.25\baselineskip]} 407 | \def\CatIndex#1#2{\index{#1\actualchar{\protect\ttfamily #1} (#2)\encapchar hyperpage}} 408 | \def\CatIndeX#1#2#3{\index{#2\actualchar#1\ (#3)\encapchar hyperpage}} 409 | \def\CatMainIndex#1#2{\index{#1\actualchar{\protect\ttfamily #1} (#2)\encapchar docmain}} 410 | \def\SpecialOptionIndex#1{\@bsphack 411 | \index{\quotechar/#1% sort options as `Symbols' 412 | \actualchar{\protect\ttfamily#1}\encapchar docmain}% 413 | \CatMainIndex{#1}{option}\@esphack} 414 | \def\SpecialUsageIndex#1{\@bsphack 415 | \index{\quotechar1% sort commands as `Numbers' 416 | \actualchar\string\verb 417 | \quotechar*\verbatimchar\string#1\verbatimchar\encapchar docmain}% 418 | {\let\special@index\index\SpecialIndex@{#1}{\encapchar docmain}}\@esphack} 419 | \def\SpecialEnvIndex#1{\CatMainIndex{#1}{environment}} 420 | \def\cmd#1{\orig@cs{\expandafter\cmd@to@cs\string#1}} 421 | \DeclareRobustCommand\orig@cs[1]{\texttt{\char`\\#1}} 422 | \DeclareRobustCommand\cs[1]{\texttt{\char`\\#1}% 423 | {\let\special@index\index 424 | \expandafter\SpecialIndex@\expandafter{\csname#1\endcsname}{\encapchar hyperpage}}} 425 | \DeclareRobustCommand\key[1]{\textcolor{thered}{\ttfamily#1}} 426 | \DeclareRobustCommand\pkg[1]{{\PackageFont#1}\@bsphack\CatIndex{#1}{package}\@esphack} 427 | \DeclareRobustCommand\cls[1]{{\PackageFont#1}\@bsphack\CatIndex{#1}{class}\@esphack} 428 | \DeclareRobustCommand\opt[1]{{\ttfamily#1}\@bsphack\CatIndex{#1}{option}\@esphack} 429 | \DeclareRobustCommand\file[1]{{\ttfamily#1}} 430 | \DeclareRobustCommand\env[1]{{\ttfamily#1} (\emph{env})\@bsphack\CatIndex{#1}{environment}\@esphack} 431 | \DeclareRobustCommand\type[1]{\Variable{#1}} 432 | 433 | \def\MTrmn#1{\ifrmnum{#1}{\rmntonum{#1}}{#1}} 434 | % write references to the User manual intermediarily in roman numerals 435 | % in order to separate them from those in the Implementation part 436 | \def\@wrindex#1{\protected@write\@indexfile{\let\@roman\relax}{\string\indexentry{#1}{\@roman\thepage}}\endgroup\@esphack} 437 | \def\HyInd@pagelink#1{\begingroup\toks@={}\edef\x{\MTrmn{#1} }\expandafter\HyInd@removespaces\x\@nil\endgroup} 438 | \def\docmain#1{\textbf{\hyperpage{#1}}} 439 | \let\doc@hyperpage\hyperpage 440 | 441 | % History 442 | \newenvironment{History}{% 443 | \newcommand\Version[2]{ 444 | \vskip\topsep 445 | \pagebreak[2] 446 | \item[\textsf{\bfseries\color{sectioning}##1}]\textsf{\bfseries\color{sectioning}(##2)} 447 | \vskip\topsep 448 | \nopagebreak} 449 | \list\labelitemi 450 | {\leftmargin 0pt 451 | \parsep 0pt 452 | \def\refsection##1{[##1]} 453 | \def\makelabel##1{\hss\llap{\color{sectioning}##1}}} 454 | }{\endlist} 455 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The LaTeX Project Public License 2 | =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 3 | 4 | LPPL Version 1.3c 2008-05-04 5 | 6 | Copyright 1999 2002-2008 LaTeX3 Project 7 | Everyone is allowed to distribute verbatim copies of this 8 | license document, but modification of it is not allowed. 9 | 10 | 11 | PREAMBLE 12 | ======== 13 | 14 | The LaTeX Project Public License (LPPL) is the primary license under 15 | which the LaTeX kernel and the base LaTeX packages are distributed. 16 | 17 | You may use this license for any work of which you hold the copyright 18 | and which you wish to distribute. This license may be particularly 19 | suitable if your work is TeX-related (such as a LaTeX package), but 20 | it is written in such a way that you can use it even if your work is 21 | unrelated to TeX. 22 | 23 | The section `WHETHER AND HOW TO DISTRIBUTE WORKS UNDER THIS LICENSE', 24 | below, gives instructions, examples, and recommendations for authors 25 | who are considering distributing their works under this license. 26 | 27 | This license gives conditions under which a work may be distributed 28 | and modified, as well as conditions under which modified versions of 29 | that work may be distributed. 30 | 31 | We, the LaTeX3 Project, believe that the conditions below give you 32 | the freedom to make and distribute modified versions of your work 33 | that conform with whatever technical specifications you wish while 34 | maintaining the availability, integrity, and reliability of 35 | that work. If you do not see how to achieve your goal while 36 | meeting these conditions, then read the document `cfgguide.tex' 37 | and `modguide.tex' in the base LaTeX distribution for suggestions. 38 | 39 | 40 | DEFINITIONS 41 | =========== 42 | 43 | In this license document the following terms are used: 44 | 45 | `Work' 46 | Any work being distributed under this License. 47 | 48 | `Derived Work' 49 | Any work that under any applicable law is derived from the Work. 50 | 51 | `Modification' 52 | Any procedure that produces a Derived Work under any applicable 53 | law -- for example, the production of a file containing an 54 | original file associated with the Work or a significant portion of 55 | such a file, either verbatim or with modifications and/or 56 | translated into another language. 57 | 58 | `Modify' 59 | To apply any procedure that produces a Derived Work under any 60 | applicable law. 61 | 62 | `Distribution' 63 | Making copies of the Work available from one person to another, in 64 | whole or in part. Distribution includes (but is not limited to) 65 | making any electronic components of the Work accessible by 66 | file transfer protocols such as FTP or HTTP or by shared file 67 | systems such as Sun's Network File System (NFS). 68 | 69 | `Compiled Work' 70 | A version of the Work that has been processed into a form where it 71 | is directly usable on a computer system. This processing may 72 | include using installation facilities provided by the Work, 73 | transformations of the Work, copying of components of the Work, or 74 | other activities. Note that modification of any installation 75 | facilities provided by the Work constitutes modification of the Work. 76 | 77 | `Current Maintainer' 78 | A person or persons nominated as such within the Work. If there is 79 | no such explicit nomination then it is the `Copyright Holder' under 80 | any applicable law. 81 | 82 | `Base Interpreter' 83 | A program or process that is normally needed for running or 84 | interpreting a part or the whole of the Work. 85 | 86 | A Base Interpreter may depend on external components but these 87 | are not considered part of the Base Interpreter provided that each 88 | external component clearly identifies itself whenever it is used 89 | interactively. Unless explicitly specified when applying the 90 | license to the Work, the only applicable Base Interpreter is a 91 | `LaTeX-Format' or in the case of files belonging to the 92 | `LaTeX-format' a program implementing the `TeX language'. 93 | 94 | 95 | 96 | CONDITIONS ON DISTRIBUTION AND MODIFICATION 97 | =========================================== 98 | 99 | 1. Activities other than distribution and/or modification of the Work 100 | are not covered by this license; they are outside its scope. In 101 | particular, the act of running the Work is not restricted and no 102 | requirements are made concerning any offers of support for the Work. 103 | 104 | 2. You may distribute a complete, unmodified copy of the Work as you 105 | received it. Distribution of only part of the Work is considered 106 | modification of the Work, and no right to distribute such a Derived 107 | Work may be assumed under the terms of this clause. 108 | 109 | 3. You may distribute a Compiled Work that has been generated from a 110 | complete, unmodified copy of the Work as distributed under Clause 2 111 | above, as long as that Compiled Work is distributed in such a way that 112 | the recipients may install the Compiled Work on their system exactly 113 | as it would have been installed if they generated a Compiled Work 114 | directly from the Work. 115 | 116 | 4. If you are the Current Maintainer of the Work, you may, without 117 | restriction, modify the Work, thus creating a Derived Work. You may 118 | also distribute the Derived Work without restriction, including 119 | Compiled Works generated from the Derived Work. Derived Works 120 | distributed in this manner by the Current Maintainer are considered to 121 | be updated versions of the Work. 122 | 123 | 5. If you are not the Current Maintainer of the Work, you may modify 124 | your copy of the Work, thus creating a Derived Work based on the Work, 125 | and compile this Derived Work, thus creating a Compiled Work based on 126 | the Derived Work. 127 | 128 | 6. If you are not the Current Maintainer of the Work, you may 129 | distribute a Derived Work provided the following conditions are met 130 | for every component of the Work unless that component clearly states 131 | in the copyright notice that it is exempt from that condition. Only 132 | the Current Maintainer is allowed to add such statements of exemption 133 | to a component of the Work. 134 | 135 | a. If a component of this Derived Work can be a direct replacement 136 | for a component of the Work when that component is used with the 137 | Base Interpreter, then, wherever this component of the Work 138 | identifies itself to the user when used interactively with that 139 | Base Interpreter, the replacement component of this Derived Work 140 | clearly and unambiguously identifies itself as a modified version 141 | of this component to the user when used interactively with that 142 | Base Interpreter. 143 | 144 | b. Every component of the Derived Work contains prominent notices 145 | detailing the nature of the changes to that component, or a 146 | prominent reference to another file that is distributed as part 147 | of the Derived Work and that contains a complete and accurate log 148 | of the changes. 149 | 150 | c. No information in the Derived Work implies that any persons, 151 | including (but not limited to) the authors of the original version 152 | of the Work, provide any support, including (but not limited to) 153 | the reporting and handling of errors, to recipients of the 154 | Derived Work unless those persons have stated explicitly that 155 | they do provide such support for the Derived Work. 156 | 157 | d. You distribute at least one of the following with the Derived Work: 158 | 159 | 1. A complete, unmodified copy of the Work; 160 | if your distribution of a modified component is made by 161 | offering access to copy the modified component from a 162 | designated place, then offering equivalent access to copy 163 | the Work from the same or some similar place meets this 164 | condition, even though third parties are not compelled to 165 | copy the Work along with the modified component; 166 | 167 | 2. Information that is sufficient to obtain a complete, 168 | unmodified copy of the Work. 169 | 170 | 7. If you are not the Current Maintainer of the Work, you may 171 | distribute a Compiled Work generated from a Derived Work, as long as 172 | the Derived Work is distributed to all recipients of the Compiled 173 | Work, and as long as the conditions of Clause 6, above, are met with 174 | regard to the Derived Work. 175 | 176 | 8. The conditions above are not intended to prohibit, and hence do not 177 | apply to, the modification, by any method, of any component so that it 178 | becomes identical to an updated version of that component of the Work as 179 | it is distributed by the Current Maintainer under Clause 4, above. 180 | 181 | 9. Distribution of the Work or any Derived Work in an alternative 182 | format, where the Work or that Derived Work (in whole or in part) is 183 | then produced by applying some process to that format, does not relax or 184 | nullify any sections of this license as they pertain to the results of 185 | applying that process. 186 | 187 | 10. a. A Derived Work may be distributed under a different license 188 | provided that license itself honors the conditions listed in 189 | Clause 6 above, in regard to the Work, though it does not have 190 | to honor the rest of the conditions in this license. 191 | 192 | b. If a Derived Work is distributed under a different license, that 193 | Derived Work must provide sufficient documentation as part of 194 | itself to allow each recipient of that Derived Work to honor the 195 | restrictions in Clause 6 above, concerning changes from the Work. 196 | 197 | 11. This license places no restrictions on works that are unrelated to 198 | the Work, nor does this license place any restrictions on aggregating 199 | such works with the Work by any means. 200 | 201 | 12. Nothing in this license is intended to, or may be used to, prevent 202 | complete compliance by all parties with all applicable laws. 203 | 204 | 205 | NO WARRANTY 206 | =========== 207 | 208 | There is no warranty for the Work. Except when otherwise stated in 209 | writing, the Copyright Holder provides the Work `as is', without 210 | warranty of any kind, either expressed or implied, including, but not 211 | limited to, the implied warranties of merchantability and fitness for a 212 | particular purpose. The entire risk as to the quality and performance 213 | of the Work is with you. Should the Work prove defective, you assume 214 | the cost of all necessary servicing, repair, or correction. 215 | 216 | In no event unless required by applicable law or agreed to in writing 217 | will The Copyright Holder, or any author named in the components of the 218 | Work, or any other party who may distribute and/or modify the Work as 219 | permitted above, be liable to you for damages, including any general, 220 | special, incidental or consequential damages arising out of any use of 221 | the Work or out of inability to use the Work (including, but not limited 222 | to, loss of data, data being rendered inaccurate, or losses sustained by 223 | anyone as a result of any failure of the Work to operate with any other 224 | programs), even if the Copyright Holder or said author or said other 225 | party has been advised of the possibility of such damages. 226 | 227 | 228 | MAINTENANCE OF THE WORK 229 | ======================= 230 | 231 | The Work has the status `author-maintained' if the Copyright Holder 232 | explicitly and prominently states near the primary copyright notice in 233 | the Work that the Work can only be maintained by the Copyright Holder 234 | or simply that it is `author-maintained'. 235 | 236 | The Work has the status `maintained' if there is a Current Maintainer 237 | who has indicated in the Work that they are willing to receive error 238 | reports for the Work (for example, by supplying a valid e-mail 239 | address). It is not required for the Current Maintainer to acknowledge 240 | or act upon these error reports. 241 | 242 | The Work changes from status `maintained' to `unmaintained' if there 243 | is no Current Maintainer, or the person stated to be Current 244 | Maintainer of the work cannot be reached through the indicated means 245 | of communication for a period of six months, and there are no other 246 | significant signs of active maintenance. 247 | 248 | You can become the Current Maintainer of the Work by agreement with 249 | any existing Current Maintainer to take over this role. 250 | 251 | If the Work is unmaintained, you can become the Current Maintainer of 252 | the Work through the following steps: 253 | 254 | 1. Make a reasonable attempt to trace the Current Maintainer (and 255 | the Copyright Holder, if the two differ) through the means of 256 | an Internet or similar search. 257 | 258 | 2. If this search is successful, then enquire whether the Work 259 | is still maintained. 260 | 261 | a. If it is being maintained, then ask the Current Maintainer 262 | to update their communication data within one month. 263 | 264 | b. If the search is unsuccessful or no action to resume active 265 | maintenance is taken by the Current Maintainer, then announce 266 | within the pertinent community your intention to take over 267 | maintenance. (If the Work is a LaTeX work, this could be 268 | done, for example, by posting to comp.text.tex.) 269 | 270 | 3a. If the Current Maintainer is reachable and agrees to pass 271 | maintenance of the Work to you, then this takes effect 272 | immediately upon announcement. 273 | 274 | b. If the Current Maintainer is not reachable and the Copyright 275 | Holder agrees that maintenance of the Work be passed to you, 276 | then this takes effect immediately upon announcement. 277 | 278 | 4. If you make an `intention announcement' as described in 2b. above 279 | and after three months your intention is challenged neither by 280 | the Current Maintainer nor by the Copyright Holder nor by other 281 | people, then you may arrange for the Work to be changed so as 282 | to name you as the (new) Current Maintainer. 283 | 284 | 5. If the previously unreachable Current Maintainer becomes 285 | reachable once more within three months of a change completed 286 | under the terms of 3b) or 4), then that Current Maintainer must 287 | become or remain the Current Maintainer upon request provided 288 | they then update their communication data within one month. 289 | 290 | A change in the Current Maintainer does not, of itself, alter the fact 291 | that the Work is distributed under the LPPL license. 292 | 293 | If you become the Current Maintainer of the Work, you should 294 | immediately provide, within the Work, a prominent and unambiguous 295 | statement of your status as Current Maintainer. You should also 296 | announce your new status to the same pertinent community as 297 | in 2b) above. 298 | 299 | 300 | WHETHER AND HOW TO DISTRIBUTE WORKS UNDER THIS LICENSE 301 | ====================================================== 302 | 303 | This section contains important instructions, examples, and 304 | recommendations for authors who are considering distributing their 305 | works under this license. These authors are addressed as `you' in 306 | this section. 307 | 308 | Choosing This License or Another License 309 | ---------------------------------------- 310 | 311 | If for any part of your work you want or need to use *distribution* 312 | conditions that differ significantly from those in this license, then 313 | do not refer to this license anywhere in your work but, instead, 314 | distribute your work under a different license. You may use the text 315 | of this license as a model for your own license, but your license 316 | should not refer to the LPPL or otherwise give the impression that 317 | your work is distributed under the LPPL. 318 | 319 | The document `modguide.tex' in the base LaTeX distribution explains 320 | the motivation behind the conditions of this license. It explains, 321 | for example, why distributing LaTeX under the GNU General Public 322 | License (GPL) was considered inappropriate. Even if your work is 323 | unrelated to LaTeX, the discussion in `modguide.tex' may still be 324 | relevant, and authors intending to distribute their works under any 325 | license are encouraged to read it. 326 | 327 | A Recommendation on Modification Without Distribution 328 | ----------------------------------------------------- 329 | 330 | It is wise never to modify a component of the Work, even for your own 331 | personal use, without also meeting the above conditions for 332 | distributing the modified component. While you might intend that such 333 | modifications will never be distributed, often this will happen by 334 | accident -- you may forget that you have modified that component; or 335 | it may not occur to you when allowing others to access the modified 336 | version that you are thus distributing it and violating the conditions 337 | of this license in ways that could have legal implications and, worse, 338 | cause problems for the community. It is therefore usually in your 339 | best interest to keep your copy of the Work identical with the public 340 | one. Many works provide ways to control the behavior of that work 341 | without altering any of its licensed components. 342 | 343 | How to Use This License 344 | ----------------------- 345 | 346 | To use this license, place in each of the components of your work both 347 | an explicit copyright notice including your name and the year the work 348 | was authored and/or last substantially modified. Include also a 349 | statement that the distribution and/or modification of that 350 | component is constrained by the conditions in this license. 351 | 352 | Here is an example of such a notice and statement: 353 | 354 | %% pig.dtx 355 | %% Copyright 2005 M. Y. Name 356 | % 357 | % This work may be distributed and/or modified under the 358 | % conditions of the LaTeX Project Public License, either version 1.3 359 | % of this license or (at your option) any later version. 360 | % The latest version of this license is in 361 | % http://www.latex-project.org/lppl.txt 362 | % and version 1.3 or later is part of all distributions of LaTeX 363 | % version 2005/12/01 or later. 364 | % 365 | % This work has the LPPL maintenance status `maintained'. 366 | % 367 | % The Current Maintainer of this work is M. Y. Name. 368 | % 369 | % This work consists of the files pig.dtx and pig.ins 370 | % and the derived file pig.sty. 371 | 372 | Given such a notice and statement in a file, the conditions 373 | given in this license document would apply, with the `Work' referring 374 | to the three files `pig.dtx', `pig.ins', and `pig.sty' (the last being 375 | generated from `pig.dtx' using `pig.ins'), the `Base Interpreter' 376 | referring to any `LaTeX-Format', and both `Copyright Holder' and 377 | `Current Maintainer' referring to the person `M. Y. Name'. 378 | 379 | If you do not want the Maintenance section of LPPL to apply to your 380 | Work, change `maintained' above into `author-maintained'. 381 | However, we recommend that you use `maintained', as the Maintenance 382 | section was added in order to ensure that your Work remains useful to 383 | the community even when you can no longer maintain and support it 384 | yourself. 385 | 386 | Derived Works That Are Not Replacements 387 | --------------------------------------- 388 | 389 | Several clauses of the LPPL specify means to provide reliability and 390 | stability for the user community. They therefore concern themselves 391 | with the case that a Derived Work is intended to be used as a 392 | (compatible or incompatible) replacement of the original Work. If 393 | this is not the case (e.g., if a few lines of code are reused for a 394 | completely different task), then clauses 6b and 6d shall not apply. 395 | 396 | 397 | Important Recommendations 398 | ------------------------- 399 | 400 | Defining What Constitutes the Work 401 | 402 | The LPPL requires that distributions of the Work contain all the 403 | files of the Work. It is therefore important that you provide a 404 | way for the licensee to determine which files constitute the Work. 405 | This could, for example, be achieved by explicitly listing all the 406 | files of the Work near the copyright notice of each file or by 407 | using a line such as: 408 | 409 | % This work consists of all files listed in manifest.txt. 410 | 411 | in that place. In the absence of an unequivocal list it might be 412 | impossible for the licensee to determine what is considered by you 413 | to comprise the Work and, in such a case, the licensee would be 414 | entitled to make reasonable conjectures as to which files comprise 415 | the Work. 416 | 417 | -------------------------------------------------------------------------------- /l3charts.sty: -------------------------------------------------------------------------------- 1 | \NeedsTeXFormat{LaTeX2e} 2 | \ProvidesPackage{l3charts}[2022/08/01 0.7.0 Customizable tikz charts using latex3] 3 | 4 | \RequirePackage{expl3} 5 | \RequirePackage{xparse} 6 | \RequirePackage{tikz} 7 | \usetikzlibrary{shapes, shapes.misc, backgrounds, calc, fit, positioning} 8 | 9 | \usepackage{tcolorbox} 10 | 11 | \ExplSyntaxOn 12 | \makeatletter % to access latex2e variables 13 | 14 | % utility cs 15 | \cs_set:Npn \identity #1 {#1} % identity cs to return the first argument as is 16 | \cs_set:Npn \nop #1 {} % nop cs to remove the first argument from input 17 | \cs_set:Npn \percent #1 {#1\%} % nop cs to remove the first argument from input 18 | 19 | % Draw an arc from a center 20 | % #1: TikZ options to draw the arc with 21 | % #2: center (x,y) 22 | % #3: initial angle 23 | % #4: final angle 24 | % #5: radius 25 | \def\centerarc[#1](#2)(#3,#4,#5){ 26 | \draw[#1] ($(#2)+({#5*cos(#3)},{#5*sin(#3)})$) arc (#3\c_colon_str#4\c_colon_str#5) 27 | } 28 | 29 | % error messages 30 | \msg_new:nnn {l3charts} {unknown-choice} {#1\ key value should\ be\ among\ #2\ but\ "#3"\ was\ given} 31 | \msg_new:nnn {l3charts} {debug} {debug: #1} 32 | 33 | % Environment to draw kiviat charts 34 | % Inside kiviatchar environment at least one dims environment must be defined 35 | % followed by one or more set environment 36 | \NewDocumentEnvironment {kiviatchart} {o} { 37 | \prop_clear_new:N \l_tikz_prop % properties for tikzpicture 38 | \clist_clear_new:N \l_tikz_clist % properties for tikzpicture as keyval string 39 | % keyval options for the environment 40 | \keys_define:nn {kiviatchart} { 41 | radius .tl_set:N = \R, % maximal diagram radius 42 | units .int_set:N = \U, % number of scale units 43 | % forward all other options to tikzpicture 44 | unknown .code:n = {\prop_put:Nxx \l_tikz_prop {\l_keys_key_str} {##1}}, 45 | rounded .bool_set:N = \l_rounded_bool, % use circles and curves instead of polygons 46 | } 47 | % set default environment options 48 | \keys_set:nn {kiviatchart} { 49 | radius = 3.5cm, 50 | units = 5, 51 | rounded = false 52 | } 53 | % override with given options if any 54 | \IfValueT {#1} {\keys_set:nn {kiviatchart} {#1}} 55 | % turn the tikz prop into a keyval string 56 | \prop_map_inline:Nn \l_tikz_prop { 57 | \clist_put_right:Nn \l_tikz_clist {##1=##2} 58 | } 59 | \str_set:Nx \l_tikz_str {[\clist_use:Nn \l_tikz_clist {,}]} 60 | 61 | % Command to set dimentions and values 62 | \DeclareDocumentCommand \value {om} { 63 | % when used inside set environment 64 | \tl_if_eq:NnTF \@currenvir {set} { 65 | % add new value 66 | \seq_put_right:Nn \l_values_seq {##2} 67 | } {} 68 | % when used inside dims environment 69 | \tl_if_eq:NnTF \@currenvir {dims} { 70 | % add new dimension with its options 71 | \IfValueTF {##1} { 72 | \seq_put_right:Nn \l_labeloptions_seq {##1} 73 | } { 74 | % replace novalue with nothing 75 | \seq_put_right:Nn \l_labeloptions_seq {} 76 | } 77 | \seq_put_right:Nn \l_labels_seq {##2} 78 | } {} 79 | } 80 | 81 | % Environment for defining dimensions 82 | % draw the kiviat chart axis 83 | \NewDocumentEnvironment {dims} {o} { 84 | % keyval options for the environment 85 | \keys_define:nn {kiviatchart_dims} { 86 | radius .tl_set:N = \L, % radius to put dimension labels 87 | label-on .int_set:N = \l_labelon_int, % which dim axis to put labels on 88 | dim-options .tl_set:N = \l_dimoptions_tl, % options for dimensions axis 89 | unit-options .tl_set:N = \l_unitoptions_tl, % options for unit polygons 90 | label-options .tl_set:N = \l_labeloptions_tl, % options for unit labels 91 | label-cs .str_set:N = \l_labelcs_str, % name of the cs used to format label 92 | unit-cs .str_set:N = \l_unitcs_str, % name of the cs used to format label 93 | angle .fp_set:N = \l_angle_fp % angle of first dimension 94 | } 95 | \cs_set:Npn \tinytt ####1 {\texttt{\tiny ####1}} % cs to format unit labels 96 | % set default environment options 97 | \keys_set:nn {kiviatchart_dims} { 98 | radius = \R, 99 | label-on = 1, 100 | dim-options = {opacity=0.8}, 101 | unit-options = {opacity=0.3}, 102 | label-options = {opacity=0.5,above,xshift=1.5mm}, 103 | label-cs = identity, 104 | unit-cs = tinytt, 105 | angle = 90 106 | } 107 | % override with given options if any 108 | \IfValueT {##1} {\keys_set:nn {kiviatchart_dims} {##1}} 109 | \seq_clear_new:N \l_labels_seq % sequence to keep labels from \value cs 110 | \seq_clear_new:N \l_labeloptions_seq % sequence to keep label options from \value cs 111 | } { 112 | % now that we have all the dimensions we can draw the picture 113 | \tl_set:Nx \labels {\seq_use:Nn \l_labels_seq {,}} % , separated list of dimensions 114 | \tl_set:Nx \D {\seq_count:N \l_labels_seq} % number of dimensions 115 | 116 | \foreach \Y in {0,...,\U} { 117 | % save positions as (D\X-\Y) with \X dimension in [1,\D] and \Y value in [0,\U] 118 | \foreach \X in {1,...,\D} { 119 | \tl_set:Nx \l_tmpa_tl {\fp_to_decimal:n {\l_angle_fp+360-((\X-1)*360/\D)}} 120 | \path (\l_tmpa_tl \c_colon_str \Y*\R/\U) coordinate (D\X-\Y); 121 | % \fill (D\X-\Y) circle (1pt); % intersection points on the web 122 | } 123 | \bool_if:nTF {\l_rounded_bool} { 124 | \exp_last_unbraced:Ne \draw {[\l_unitoptions_tl]} (0,0) circle (\Y*\R/\U); 125 | } { 126 | % draw units polygons 127 | \exp_last_unbraced:Ne \draw {[\l_unitoptions_tl]} (D1-\Y) \foreach \X in {1,...,\D} { 128 | -- (D\X-\Y) 129 | } -- cycle; 130 | } 131 | } 132 | 133 | % draw the dimensions axis 134 | \foreach \X in {1,...,\D} { 135 | \tl_set:Nx \l_tmpa_tl {\int_use:N \U} 136 | \exp_last_unbraced:Ne \draw {[\l_dimoptions_tl]} (D\X-0) -- (D\X-\l_tmpa_tl); 137 | } 138 | 139 | % draw units labels 140 | \bool_if:nTF {\int_compare_p:n {\l_labelon_int >= 1} && \int_compare_p:n {\l_labelon_int <= \D}} { 141 | \foreach \Y in {1,...,\U} { 142 | \tl_set:Nx \l_tmpa_tl {\int_eval:n {\Y-1}} 143 | \exp_last_unbraced:Ne \node {[\l_labeloptions_tl]} at (D\int_use:N \l_labelon_int-\l_tmpa_tl) {\use:c {\l_unitcs_str} {\Y}}; 144 | } 145 | } {} 146 | 147 | % draw labels for each dimension axis 148 | \foreach \l [count=\X from 1] in \labels { 149 | \tl_set:Nx \l_tmpa_tl {\fp_to_decimal:n {\l_angle_fp+360-((\X-1)*360/\D)}} 150 | \tl_set:Nx \l_tmpb_tl {\seq_item:Nn \l_labeloptions_seq {\X}} 151 | \path (\l_tmpa_tl \c_colon_str \L) node [\l_tmpb_tl] {\use:c {\l_labelcs_str} {\l}}; 152 | } 153 | } 154 | 155 | % Environment for values set 156 | % draw set polygon 157 | \NewDocumentEnvironment {set} {o} { 158 | \seq_clear_new:N \l_values_seq % store the values of a set 159 | \prop_clear_new:N \l_setoptions_prop % prop to store keyval options 160 | \clist_clear_new:N \l_setoptions_clist % clist to get prop back to keyval str 161 | % keyval options for the environment 162 | \keys_define:nn {kiviatchart_set} { 163 | dot-options .tl_set:N = \l_dotoptions_tl, % options for polygon node 164 | % forward all other options to tikz 165 | unknown .code:n = {\prop_put:Nxx \l_setoptions_prop {\l_keys_key_str} {####1}} 166 | } 167 | % set default options (exp_args to expand \c_space_tl) 168 | \exp_args:Nnx \keys_set:nn {kiviatchart_set} { 169 | dot-options = {fill,circle,inner~sep=1pt}, 170 | % default options forwarded to tikzpicture 171 | color = black, 172 | line~width = 1.5pt, 173 | opacity = 1, 174 | fill~opacity = 0.3, 175 | fill = gray 176 | } 177 | % override with given options if any 178 | \IfValueT {##1} {\keys_set:nn {kiviatchart_set} {##1}} 179 | % turn the tikz prop into a keyval string 180 | \prop_map_inline:Nn \l_setoptions_prop { 181 | \clist_put_right:Nn \l_setoptions_clist {####1=####2} 182 | } 183 | \str_set:Nx \l_setoptions_str {\clist_use:Nn \l_setoptions_clist {,}} 184 | 185 | }{ 186 | % now that we have all the values, we can draw the points and the polygons 187 | \tl_set:Nx \values {\seq_use:Nn \l_values_seq {,}} 188 | \seq_get_left:NN \l_values_seq \firstval 189 | 190 | % dots on the dim axis 191 | \foreach \v [count=\i from 1] in \values { 192 | \exp_last_unbraced:Ne \node {[\l_dotoptions_tl]} at (D\i-\v) {}; 193 | } 194 | 195 | \bool_if:nTF {\l_rounded_bool} { 196 | % closed curve going through all the coordinates 197 | % construct the coordinates string (space separated list of defined coordinates) 198 | \str_new:N \l_coordinates_str 199 | \seq_map_indexed_inline:Nn \l_values_seq {\str_put_right:Nn \l_coordinates_str {(D####1-####2)}} 200 | % \msg_term:nnx {l3charts} {debug} {\l_coordinates_str} 201 | \exp_last_unbraced:Ne \draw {[\l_setoptions_str]} plot [smooth~cycle] coordinates {\l_coordinates_str}; 202 | } { 203 | % closed polygon going through all the coordinates 204 | \exp_last_unbraced:Ne \draw {[\l_setoptions_str]} (D1-\firstval) foreach \v [count=\i from 1] in \values {-- (D\i-\v)} -- cycle; 205 | } 206 | } 207 | 208 | % start a picture (expand option before begin) 209 | \exp_last_unbraced:Nno \begin {tikzpicture} {\l_tikz_str} 210 | }{ 211 | % close the picture 212 | \end{tikzpicture} 213 | } 214 | 215 | % Environment to hold a ball chart 216 | \NewDocumentEnvironment {ballchart} {o} { 217 | \prop_clear_new:N \l_tikz_prop % properties for tikzpicture 218 | \clist_clear_new:N \l_tikz_clist % properties for tikzpicture as keyval string 219 | \keys_define:nn {ballchart} { 220 | n .int_set:N = \l_n_int, % number of circles 221 | gap .dim_set:N = \l_gap_dim, % gap between bars 222 | cgap .dim_set:N = \l_cgap_dim, % gap between circles 223 | radius .dim_set:N = \l_radius_dim, % radius 224 | fill-options .tl_set:N = \l_filloptions_tl, % TikZ options to fill balls with 225 | draw-options .tl_set:N = \l_drawoptions_tl, % TikZ options to draw balls with 226 | label-options .tl_set:N = \l_labeloptions_tl, % TikZ options for dimensions axis 227 | label-cs .str_set:N = \l_labelcs_str, % cs name to format labels with 228 | label-pos .choices:nn = 229 | {left, right, above, below, above right, above left, below right, below left} 230 | {\str_set:Nx \l_labelpos_str {\l_keys_choice_tl}}, 231 | label-pos / unknown .code:n = { 232 | \msg_error:nnxxx { l3charts } { unknown-choice } 233 | { label-pos } % Name of choice key 234 | {left, right, above, below, above right, above left, below right, below left} % Valid choices 235 | { ##1 } % Invalid choice given 236 | }, 237 | value-cs .str_set:N = \l_valuecs_str, % cs name to format values with 238 | % forward all other options to tikzpicture 239 | unknown .code:n = {\prop_put:Nxx \l_tikz_prop {\l_keys_key_str} {##1}} 240 | } 241 | % set default options 242 | \keys_set:nn {ballchart} { 243 | n = 5, 244 | gap = 1ex, 245 | cgap = 1pt, 246 | radius = 2.5mm, 247 | fill-options = {fill=black}, 248 | draw-options = {draw=none}, 249 | label-options = {}, 250 | label-cs = identity, 251 | label-pos = left, 252 | value-cs = nop, 253 | } 254 | % override with given options if any 255 | \IfValueT {#1} {\keys_set:nn {ballchart} {#1}} 256 | % turn the tikz prop into a keyval string 257 | \prop_map_inline:Nn \l_tikz_prop { 258 | \clist_put_right:Nn \l_tikz_clist {##1=##2} 259 | } 260 | \str_set:Nx \l_tikz_str {[\clist_use:Nn \l_tikz_clist {,}]} 261 | 262 | \tl_set:Nn \l_width_tl {\dim_to_decimal_in_unit:nn {(2\l_radius_dim + \l_cgap_dim) * \l_n_int} {1cm}} 263 | 264 | \int_zero_new:N \l_count_int 265 | % Command to add a new ballbar 266 | % #1: label 267 | % #2: percentage 268 | \DeclareDocumentCommand \value {mm} { 269 | \tl_set:Nx \l_perc_tl {\fp_to_decimal:n {##2 / 100}} 270 | \tl_set:Nx \l_y_tl {\dim_to_decimal_in_unit:nn {(2\l_radius_dim + \l_gap_dim) * \l_count_int} {1cm}} 271 | \tl_set:Nx \l_m_tl {\int_eval:n {\l_n_int - 1}} 272 | 273 | \exp_last_unbraced:Ne \draw {[\l_drawoptions_tl, fill~fraction={\l_perc_tl}]} foreach \i in {0,...,\l_m_tl} {(2\l_radius_dim * \i + \l_cgap_dim * \i, -\l_y_tl) circle (\l_radius_dim)}; 274 | 275 | \exp_last_unbraced:Ne \node {(ball\int_use:N \l_count_int)} at (\l_width_tl/2, -\l_y_tl) {\makebox[\l_width_tl cm][c]{\use:c {\l_valuecs_str} {##2}}}; 276 | \exp_last_unbraced:Ne \node {[\l_labelpos_str=of~ball\int_use:N \l_count_int,\l_labeloptions_tl]} {\use:c {\l_labelcs_str} {##1}}; 277 | \int_incr:N \l_count_int 278 | } 279 | 280 | \exp_last_unbraced:Nno \begin {tikzpicture} {\l_tikz_str} 281 | \tikzset{fill~fraction/.style~n~args={1}{ 282 | path~picture={ 283 | \exp_last_unbraced:Ne \fill {[\l_filloptions_tl]} (path~picture~bounding~box.south~west) rectangle 284 | ($(path~picture~bounding~box.north~west)!##1!(path~picture~bounding~box.north~east)$); 285 | } 286 | } 287 | } 288 | }{ 289 | \end{tikzpicture} 290 | } 291 | 292 | % % Environment to hold a bar chart 293 | \NewDocumentEnvironment {barchart} {o} { 294 | \prop_clear_new:N \l_tikz_prop % properties for tikzpicture 295 | \clist_clear_new:N \l_tikz_clist % properties for tikzpicture as keyval string 296 | % keyval options for the environment 297 | \keys_define:nn {barchart} { 298 | width .dim_set:N = \l_w_dim, % maximum bar width 299 | height .dim_set:N = \l_h_dim, % bar height 300 | gap .dim_set:N = \l_gap_dim, % gap between bars 301 | fill-options .tl_set:N = \l_filloptions_str, % TikZ options to fill background with 302 | draw-options .tl_set:N = \l_drawoptions_str, % TikZ options to draw bar with 303 | label-options .tl_set:N = \l_labeloptions_tl, % TikZ options to draw the label with 304 | label-cs .str_set:N = \l_labelcs_str, % cs name to format labels with 305 | label-pos .choices:nn = 306 | {left, right, above, below, above right, above left, below right, below left} 307 | {\str_set:Nx \l_labelpos_str {\l_keys_choice_tl}}, 308 | label-pos / unknown .code:n = { 309 | \msg_error:nnxxx { l3charts } { unknown-choice } 310 | { label-pos } % Name of choice key 311 | {left, right, above, below, above right, above left, below right, below left} % Valid choices 312 | { ##1 } % Invalid choice given 313 | }, 314 | value-cs .str_set:N = \l_valuecs_str, % cs name to format values with 315 | % forward all other options to tikzpicture 316 | unknown .code:n = {\prop_put:Nxx \l_tikz_prop {\l_keys_key_str} {##1}} 317 | } 318 | % set default options 319 | \keys_set:nn {barchart} { 320 | width = 3cm, 321 | height = 3.5mm, 322 | gap = 1ex, 323 | fill-options = {fill=none}, 324 | draw-options = {fill=black}, 325 | label-options = {}, 326 | label-cs = identity, 327 | label-pos = left, 328 | value-cs = nop 329 | } 330 | % override with given options if any 331 | \IfValueT {#1} {\keys_set:nn {barchart} {#1}} 332 | % turn the tikz prop into a keyval string 333 | \prop_map_inline:Nn \l_tikz_prop { 334 | \clist_put_right:Nn \l_tikz_clist {##1=##2} 335 | } 336 | \str_set:Nx \l_tikz_str {[\clist_use:Nn \l_tikz_clist {,}]} 337 | 338 | \int_zero_new:N \l_count_int 339 | % Command to add a bar to the bar chart 340 | % #1: bar label 341 | % #2: percentage the current bar should take up of the total width 342 | \DeclareDocumentCommand \value {mm} { 343 | \tl_set:Nn \l_x_tl {0} 344 | \tl_set:Nx \l_y_tl {\dim_to_decimal_in_unit:nn {(\l_h_dim + \l_gap_dim) * \l_count_int} {1cm}} 345 | \tl_set:Nx \l_barx_tl {\dim_to_decimal_in_unit:nn {##2\l_w_dim / 100} {1cm}} 346 | \tl_set:Nx \l_bary_tl {\dim_to_decimal_in_unit:nn {((\l_h_dim + \l_gap_dim) * \l_count_int) + \l_h_dim} {1cm}} 347 | \tl_set:Nx \l_cx_tl {\dim_to_decimal_in_unit:nn {0.5\l_w_dim} {1cm}} 348 | \tl_set:Nx \l_cy_tl {\dim_to_decimal_in_unit:nn {((\l_h_dim + \l_gap_dim) * \l_count_int) + 0.5\l_h_dim} {1cm}} 349 | 350 | \exp_last_unbraced:Ne \fill {[\l_filloptions_str]} (\l_x_tl,-\l_y_tl) rectangle (\l_w_dim, -\l_bary_tl); 351 | \exp_last_unbraced:Ne \draw {[\l_drawoptions_str]} (\l_x_tl,-\l_y_tl) rectangle (\l_barx_tl, -\l_bary_tl); 352 | \exp_last_unbraced:Ne \node {(bar\int_use:N \l_count_int)} at (\l_cx_tl,-\l_cy_tl) {\makebox[\l_w_dim][c]{\use:c {\l_valuecs_str} {##2}}}; 353 | \exp_last_unbraced:Ne \node {[\l_labelpos_str=of~bar\int_use:N \l_count_int,\l_labeloptions_tl]} {\use:c {\l_labelcs_str} {##1}}; 354 | \int_incr:N \l_count_int 355 | } 356 | 357 | % start a picture (expand option before begin) 358 | \exp_last_unbraced:Nno \begin {tikzpicture} {\l_tikz_str} 359 | }{ 360 | \end{tikzpicture} 361 | } 362 | 363 | % Environment to hold a bubble chart 364 | \NewDocumentEnvironment {bubblechart} {o} { 365 | \prop_clear_new:N \l_tikz_prop % properties for tikzpicture 366 | \clist_clear_new:N \l_tikz_clist % properties for tikzpicture as keyval string 367 | % keyval options for the environment 368 | \keys_define:nn {bubblechart} { 369 | radius .dim_set:N = \l_radius_dim, % max radius 370 | gap .dim_set:N = \l_gap_dim, % gap between bubbles 371 | fill-options .tl_set:N = \l_filloptions_str, % TikZ options to draw the background with 372 | draw-options .tl_set:N = \l_drawoptions_str, % TikZ options to draw the bubble with 373 | label-options .tl_set:N = \l_labeloptions_tl, % TikZ options to draw the label with 374 | label-cs .str_set:N = \l_labelcs_str, % cs name to format labels with 375 | label-pos .choices:nn = 376 | {left, right, above, below, above right, above left, below right, below left} 377 | {\str_set:Nx \l_labelpos_str {\l_keys_choice_tl}}, 378 | label-pos / unknown .code:n = { 379 | \msg_error:nnxxx { l3charts } { unknown-choice } 380 | { label-pos } % Name of choice key 381 | {left, right, above, below, above right, above left, below right, below left} % Valid choices 382 | { ##1 } % Invalid choice given 383 | }, 384 | value-cs .str_set:N = \l_valuecs_str, % cs name to format values with 385 | vertical .bool_set:N = \l_vertical_bool, % stack bubbles vertically 386 | % forward all other options to tikzpicture 387 | unknown .code:n = {\prop_put:Nxx \l_tikz_prop {\l_keys_key_str} {##1}} 388 | } 389 | % set default options 390 | \keys_set:nn {bubblechart} { 391 | radius = 1cm, 392 | gap = 1ex, 393 | fill-options = {fill=none,draw=none}, 394 | draw-options = {fill=black}, 395 | label-options = {}, 396 | label-cs = identity, 397 | label-pos = above, 398 | value-cs = nop, 399 | vertical = false 400 | } 401 | % override with given options if any 402 | \IfValueT {#1} {\keys_set:nn {bubblechart} {#1}} 403 | % turn the tikz prop into a keyval string 404 | \prop_map_inline:Nn \l_tikz_prop { 405 | \clist_put_right:Nn \l_tikz_clist {##1=##2} 406 | } 407 | \str_set:Nx \l_tikz_str {[\clist_use:Nn \l_tikz_clist {,}]} 408 | 409 | \int_zero_new:N \l_count_int 410 | % Command to add a bubble 411 | % #1: label 412 | % #2: percent 413 | \DeclareDocumentCommand \value {mm} { 414 | \tl_set:Nx \l_bubbleradius_tl {\dim_to_decimal_in_unit:nn {##2\l_radius_dim / 100} {1cm}} 415 | \bool_if:nTF {\l_vertical_bool} { 416 | \tl_set:Nn \l_x_tl {0} 417 | \tl_set:Nx \l_y_tl {-\dim_to_decimal_in_unit:nn {(2\l_radius_dim + \l_gap_dim) * \l_count_int} {1cm}} 418 | }{ 419 | \tl_set:Nx \l_x_tl {\dim_to_decimal_in_unit:nn {(2\l_radius_dim + \l_gap_dim) * \l_count_int} {1cm}} 420 | \tl_set:Nn \l_y_tl {0} 421 | } 422 | % \tl_set:Nn \l_diam_tl {\fp_to_decimal:n {\l_radius_dim * 2}cm} % diameter for makebox 423 | 424 | \exp_last_unbraced:Ne \fill {[\l_filloptions_str]} (\l_x_tl, \l_y_tl) circle (\l_radius_dim); 425 | \exp_last_unbraced:Ne \draw {[\l_drawoptions_str]} (\l_x_tl, \l_y_tl) circle (\l_bubbleradius_tl); 426 | \exp_last_unbraced:Ne \node {(bubble\int_use:N \l_count_int)} at (\l_x_tl,\l_y_tl) {\makebox[2\l_radius_dim][c]{\use:c {\l_valuecs_str} {##2}}}; 427 | \exp_last_unbraced:Ne \node {[\l_labelpos_str=of~bubble\int_use:N \l_count_int,\l_labeloptions_tl]} {\use:c {\l_labelcs_str} {##1}}; 428 | \int_incr:N \l_count_int 429 | } 430 | 431 | % start a picture (expand option before begin) 432 | \exp_last_unbraced:Nno \begin {tikzpicture} {\l_tikz_str} 433 | }{ 434 | \end{tikzpicture} 435 | } 436 | 437 | % Environment to hold a radial chart 438 | \NewDocumentEnvironment {radialchart} {o} { 439 | \prop_clear_new:N \l_tikz_prop % properties for tikzpicture 440 | \clist_clear_new:N \l_tikz_clist % properties for tikzpicture as keyval string 441 | % keyval options for the environment 442 | \keys_define:nn {radialchart} { 443 | radius .dim_set:N = \l_radius_dim, % max radius 444 | gap .dim_set:N = \l_gap_dim, % gap between radials 445 | line~width .dim_set:N = \l_linewidth_dim, % line width 446 | fill-options .tl_set:N = \l_filloptions_tl, % TikZ options to fill/draw the center of the radial with 447 | draw-options .tl_set:N = \l_drawoptions_tl, % TikZ options to draw the radial with 448 | label-options .tl_set:N = \l_labeloptions_tl, % TikZ options to draw the label with 449 | label-cs .str_set:N = \l_labelcs_str, % cs name to format labels with 450 | label-pos .choices:nn = 451 | {left, right, above, below, above right, above left, below right, below left} 452 | {\str_set:Nx \l_labelpos_str {\l_keys_choice_tl}}, 453 | label-pos / unknown .code:n = { 454 | \msg_error:nnxxx { l3charts } { unknown-choice } 455 | { label-pos } % Name of choice key 456 | {left, right, above, below, above right, above left, below right, below left} % Valid choices 457 | { ##1 } % Invalid choice given 458 | }, 459 | value-cs .str_set:N = \l_valuecs_str, % cs name to format values with 460 | vertical .bool_set:N = \l_vertical_bool, % stack radials vertically 461 | % forward all other options to tikzpicture 462 | unknown .code:n = {\prop_put:Nxx \l_tikz_prop {\l_keys_key_str} {##1}} 463 | } 464 | % set default options 465 | \keys_set:nx {radialchart} { 466 | radius = 1cm, 467 | gap = 2.5ex, 468 | line~width = 3mm, 469 | fill-options = {fill=none,draw=black!10}, 470 | draw-options = black, 471 | label-options = {}, 472 | label-cs = identity, 473 | label-pos = above, 474 | value-cs = percent, 475 | vertical = false, 476 | % default options forwarded to tikzpicture 477 | line~cap = round 478 | } 479 | % override with given options if any 480 | \IfValueT {#1} {\keys_set:nn {radialchart} {#1}} 481 | % set tikz 'line width' option 482 | \prop_put:Nxx \l_tikz_prop {line~width} {\dim_use:N \l_linewidth_dim} 483 | % turn the tikz prop into a keyval string 484 | \prop_map_inline:Nn \l_tikz_prop { 485 | \clist_put_right:Nn \l_tikz_clist {##1=##2} 486 | } 487 | \str_set:Nx \l_tikz_str {[\clist_use:Nn \l_tikz_clist {,}]} 488 | 489 | \int_zero_new:N \l_count_int 490 | % Command to add a radial 491 | % #1: label 492 | % #2: percent 493 | \DeclareDocumentCommand \value {mm} { 494 | \tl_set:Nx \l_tmpa_tl {\fp_to_decimal:n {90 - 360 * ##2 / 100}} 495 | \bool_if:nTF {\l_vertical_bool} { 496 | \tl_set:Nn \l_x_tl {0} 497 | \tl_set:Nx \l_y_tl {-\dim_to_decimal_in_unit:nn {(2\l_radius_dim + \l_gap_dim) * \l_count_int} {1cm}} 498 | }{ 499 | \tl_set:Nx \l_x_tl {\dim_to_decimal_in_unit:nn {(2\l_radius_dim + \l_gap_dim) * \l_count_int} {1cm}} 500 | \tl_set:Nn \l_y_tl {0} 501 | } 502 | 503 | \exp_last_unbraced:Ne \fill {[\l_filloptions_tl]} (\l_x_tl,\l_y_tl) circle (\l_radius_dim); 504 | \exp_last_unbraced:Ne \centerarc {[\l_drawoptions_tl]} (\l_x_tl,\l_y_tl) (90,\l_tmpa_tl,\l_radius_dim); 505 | \exp_last_unbraced:Ne \node {(radial\int_use:N \l_count_int)} at (\l_x_tl,\l_y_tl) {\makebox[2\l_radius_dim][c]{\use:c {\l_valuecs_str} {##2}}}; 506 | \exp_last_unbraced:Ne \node {[\l_labelpos_str=of~radial\int_use:N \l_count_int,\l_labeloptions_tl]} {\use:c {\l_labelcs_str} {##1}}; 507 | \int_incr:N \l_count_int 508 | } 509 | % start a picture (expand option before begin) 510 | \exp_last_unbraced:Nno \begin {tikzpicture} {\l_tikz_str} 511 | }{ 512 | \end{tikzpicture} 513 | } 514 | 515 | % Environment to hold a arc chart 516 | \NewDocumentEnvironment {arcchart} {o} { 517 | \prop_clear_new:N \l_tikz_prop % properties for tikzpicture 518 | \clist_clear_new:N \l_tikz_clist % properties for tikzpicture as keyval string 519 | % keyval options for the environment 520 | \keys_define:nn {arcchart} { 521 | radius .dim_set:N = \l_radius_dim, % radius of outer arc 522 | gap .dim_set:N = \l_gap_dim, % gap between arcs 523 | line~width .dim_set:N = \l_linewidth_dim, % line width 524 | fill-options .tl_set:N = \l_filloptions_tl, % TikZ options to fill/draw background of the arcs with 525 | draw-options .tl_set:N = \l_drawoptions_tl, % TikZ options to draw the arcs with 526 | label-options .tl_set:N = \l_labeloptions_tl, % TikZ options to draw the label with 527 | label-cs .str_set:N = \l_labelcs_str, % cs name to format labels with 528 | value-options .tl_set:N = \l_valueoptions_tl, % TikZ options to draw values with 529 | value-cs .str_set:N = \l_valuecs_str, % cs name to format values with 530 | value-angle .fp_set:N = \l_valueangle_fp, % value angle 531 | % forward all other options to tikzpicture 532 | unknown .code:n = {\prop_put:Nxx \l_tikz_prop {\l_keys_key_str} {##1}} 533 | } 534 | % set default options 535 | \keys_set:nx {arcchart} { 536 | radius = 2cm, 537 | gap = 1mm, 538 | line~width = 4mm, 539 | fill-options = {fill=none,draw=black!10}, 540 | draw-options = black, 541 | label-options = {left}, 542 | label-cs = identity, 543 | value-options = {}, 544 | value-cs = nop, 545 | value-angle = 90, 546 | % default options forwarded to tikzpicture 547 | line~cap = round 548 | } 549 | % override with given options if any 550 | \IfValueT {#1} {\keys_set:nn {arcchart} {#1}} 551 | % set tikz 'line width' option 552 | \prop_put:Nxx \l_tikz_prop {line~width} {\dim_use:N \l_linewidth_dim} 553 | % turn the tikz prop into a keyval string 554 | \prop_map_inline:Nn \l_tikz_prop { 555 | \clist_put_right:Nn \l_tikz_clist {##1=##2} 556 | } 557 | \str_set:Nx \l_tikz_str {[\clist_use:Nn \l_tikz_clist {,}]} 558 | 559 | \int_zero_new:N \l_count_int 560 | % Command to add a radial 561 | % #1: TikZ drawing options 562 | % #2: label 563 | % #3: percent 564 | \DeclareDocumentCommand \value {O{}mm} { 565 | % 270 is 100% 566 | \tl_set:Nx \l_tmpa_tl {\fp_to_decimal:n {90 - 270 * ##3 / 100}} 567 | % radius is smaller at each step 568 | \dim_set:Nn \l_tmpa_dim {\l_radius_dim - \l_count_int\l_linewidth_dim - \l_count_int\l_gap_dim} 569 | 570 | \exp_last_unbraced:Ne \centerarc {[\l_filloptions_tl]} (0,0) (90,90-270,\l_tmpa_dim); 571 | \exp_last_unbraced:Ne \centerarc {[##1]} (0,0) (90,\l_tmpa_tl,\l_tmpa_dim); 572 | \exp_last_unbraced:Ne \node {[\l_valueoptions_tl]} at (\fp_use:N \l_valueangle_fp \c_colon_str \l_tmpa_dim) {\fontsize{0.5\l_linewidth_dim}{0.5\l_linewidth_dim}\selectfont\use:c {\l_valuecs_str} {##3}}; 573 | \exp_last_unbraced:Ne \node {[\l_labeloptions_tl]} at (90 \c_colon_str \l_tmpa_dim) {\fontsize{\l_linewidth_dim}{\l_linewidth_dim}\selectfont\use:c {\l_labelcs_str} {##2}}; 574 | \int_incr:N \l_count_int 575 | } 576 | % start a picture (expand option before begin) 577 | \exp_last_unbraced:Nno \begin {tikzpicture} {\l_tikz_str} 578 | }{ 579 | \end{tikzpicture} 580 | } 581 | 582 | \ExplSyntaxOff 583 | -------------------------------------------------------------------------------- /l3charts-doc.tex: -------------------------------------------------------------------------------- 1 | \documentclass{l3charts-doc} 2 | \usepackage{l3charts} 3 | 4 | % setup 5 | \hypersetup{ 6 | keeppdfinfo, 7 | pdftitle={l3charts}, 8 | pdfsubtitle={The LaTeX package}, 9 | pdfversionid=0.7.1, 10 | pdfauthor={Éric BURGHARD}, 11 | pdfcontactemail={ctan@itsufficient.me}, 12 | pdfcontacturl={https://itsufficient.me}, 13 | pdfsubject={Customizable charts made with TikZ and LaTeX3}, 14 | pdfkeywords={LaTeX3, TikZ}, 15 | pdfcopyright={\textcopyright\ 2022 Éric BURGHARD\textLF 16 | This work may be distributed and/or modified under the conditions 17 | of the LaTeX Project Public License, either version 1.3c of this 18 | license or (at your option) any later version.\textLF 19 | This work has the LPPL maintenance status `maintained'.}, 20 | pdflicenseurl={https://www.latex-project.org/lppl/}, 21 | pdflang={en-GB} 22 | } 23 | 24 | % bold with appended % 25 | \NewDocumentCommand\textbfp{m}{\textbf{\percent{#1}}} 26 | % format label white on black 27 | \NewDocumentCommand\textinv{m}{\colorbox{black}{\textcolor{white}{#1}}} 28 | % bold red 29 | \NewDocumentCommand\redbf{m}{\textcolor{red!50}{\textbf{#1}}} 30 | \NewDocumentCommand\redbfp{m}{\textcolor{red!50}{\textbfp{#1}}} 31 | % bold white 32 | \NewDocumentCommand\whitebf{m}{\textcolor{white}{\textbf{#1}}} 33 | \NewDocumentCommand\whitebfp{m}{\textcolor{white}{\textbfp{#1}}} 34 | % tenth rate 35 | \ExplSyntaxOn 36 | \NewDocumentCommand\tenrate{m}{\int_eval:n{#1/10}/10} 37 | \ExplSyntaxOff 38 | 39 | % Start document 40 | \RecordChanges 41 | \PageIndex 42 | 43 | \title{The \texttt{l3charts} package} 44 | \author{Éric BURGHARD} 45 | \date{2023/01/04} 46 | \def\githuburl{https://git.itsufficient.me/latex/l3charts} 47 | 48 | \begin{document} 49 | \newgeometry{scale={0.8,0.9},top=2cm} % title page is centered 50 | \maketitle 51 | \thispagestyle{empty} 52 | \begin{abstract} 53 | This package defines a few simple \TikZ{} charts that can be drawn using \LaTeX{} 54 | environments. This has mainly been developed as an experimentation of \texttt{expl3} for 55 | checking what \LaTeX3 really brought to facilitate package development (expansion control, 56 | \texttt{seq}, \texttt{prop},\texttt{ keys},\texttt{ int},\texttt{ bool},\texttt{ fp},\texttt{ 57 | dim},\texttt{msg}, ...). 58 | \end{abstract} 59 | \tableofcontents 60 | \newpage 61 | \restoregeometry% restore default geometry 62 | 63 | \section{About this documentation} 64 | 65 | I doubt that \LaTeX{} will have one day a modern documentation system as powerful as \href{https://doc.rust-lang.org/cargo/commands/cargo-doc.html}{\texttt{cargo doc}} due to its 66 | typeless and syntaxless nature. In my opinion \LaTeX{} literate programming with \pkg{docstrip} 67 | is just an ugly hack that turns the code and the documentation unmaintainable, and it's probably 68 | the component of \LaTeX{} which aged the most. 69 | 70 | So I chose to write the documentation separately and borrowed much of the style from the 71 | \href{https://github.com/schlcht/microtype}{\pkg{microtype}} package which by the way (if you 72 | are still curious about it), pushes the \pkg{docstrip} mastery to a \emph{black magic} level. 73 | 74 | \section{Motivation} 75 | 76 | This package has been developed mainly to typeset a fancy résumé but perhaps it could be 77 | used in other contexts too. I didn't want to write \TikZ{} charts directly in the document 78 | as it would have turned a simple typesetting file into an unreadable document, and I would have 79 | forgotten every details after just a few months. 80 | 81 | I wouldn't have the patience to develop this with \LaTeX{} or \TeX{} either, but I was curious 82 | enough about \pkg{expl3} to try an implementation. You should probably take this package as a rough 83 | tutorial on how to develop with \pkg{expl3} because it uses nearly all the types defined in the 84 | reference documentation (expansion control, \texttt{seq},\texttt{ prop},\texttt{ keys},\texttt{ 85 | int},\texttt{ bool},\texttt{ fp},\texttt{ dim},\texttt{ msg}, ...) in straightforward ways. 86 | 87 | \TeX{} will always be that dusty tech you can't ignore because but there are so many (unmatched) 88 | packages coming from academic circles, but \pkg{expl3} gives a touch of modernity and facilitates 89 | a lot package development by allowing to easily bridge \TeX{} packages (here \LaTeX{} and \TikZ). 90 | 91 | \section{Kiviat chart} 92 | 93 | \subsection{Usage} 94 | 95 | The \href{https://en.wikipedia.org/wiki/Radar_chart}{kiviat chart} or \emph{radar chart} allows 96 | to represent one or several set along several dimensions. 97 | 98 | \DescribeEnv{kiviatchart} 99 | Environment that hold a kiviat chart. Accepts an optional argument \oarg{clist} which 100 | is comma separated list of keywords and values : 101 | 102 | \Describe{Option}{radius}{:dim}[3.5cm] 103 | Maximal diagram radius 104 | 105 | \Describe{Option}{units}{:int}[5] 106 | Set the scale of units from 0 to the given number 107 | 108 | \Describe{Option}{rounded}{:bool}[false] 109 | Use circles for the scale and curves for the sets instead of polygons 110 | 111 | \Describe{Option}{*}{:keyval} 112 | All other options are passed to \env{tikzpicture} 113 | 114 | A \env{kiviatchart} should begin with a \env{dims}, followed by one or several \env{set}. 115 | 116 | \subsubsection{Dimensions} 117 | 118 | \DescribeEnv{dims} 119 | Environment that hold the definition of all dimensions. Accepts an optional argument \oarg{clist} 120 | which is comma separated list of keywords and values : 121 | 122 | \Describe{Option}{radius}{:dim}[\env{kiviatchart} \opt{radius}] 123 | Radius to put dimension labels on 124 | 125 | \Describe{Option}{label-on}{:int}[1] 126 | Dimension axis index (between 1 and number of dimensions) to put the labels on. In case of 127 | invalid value (0), the units labels are hidden. 128 | 129 | \Describe{Option}{dim-options}{:prop}[\{opacity=0.8\}] 130 | \TikZ{} options for drawing dimensions axis with 131 | 132 | \Describe{Option}{unit-options}{:prop}[\{opacity=0.3\}] 133 | \TikZ{} options for drawing unit polygons with 134 | 135 | \Describe{Option}{label-options}{:prop}[\{opacity=0.5,above,xshift=1.5mm\}] 136 | \TikZ{} options drawing for unit labels 137 | 138 | \Describe{Option}{label-cs}{:str}[identity] 139 | Name of the cs used to format labels 140 | 141 | \Describe{Option}{unit-cs}{:str}[tinytt] 142 | Name of the cs used to format unit scale 143 | 144 | \Describe{Option}{angle}{:fp}[90] 145 | Angle of the first dimension 146 | 147 | \DescribeMacro{value} 148 | \cs{value}\oarg{clist}\marg{label} is used to add a dimension to the kiviat chart. \oarg{clist} is 149 | passed to \TikZ{} to draw the nodes corresponding to the labels. 150 | 151 | \subsubsection{Set} 152 | 153 | \DescribeEnv{set} 154 | \env{set} is used to add a new set to the kiviat chart. Accepts an optional argument \oarg{clist} 155 | which is comma separated list of keywords and values : 156 | 157 | \Describe{Option}{dot-options}{:prop}[\{fill,circle,inner~sep=1pt\}] 158 | Options for polygon node 159 | 160 | \Describe{Option}{*}{:keyval}[color=black,line~width=1.5pt,opacity=1,fill~opacity=0.3,fill=gray] 161 | All other options are passed to \cs{draw} cs which draws the polygon 162 | 163 | \DescribeMacro{value} 164 | \cs{value}\marg{int} is used to add a value to the set. 165 | 166 | There must be the same number of \cs{value} inside \env{set} and \env{dims}, and each \cs{value} 167 | corresponds to the dimension in \env{dims} at the same index. 168 | 169 | \newpage 170 | \subsection{Examples} 171 | 172 | \subsubsection{Simple} 173 | 174 | \begin{example} 175 | % scale is passed to tikzpicture 176 | \begin{kiviatchart}[scale=0.75] 177 | % Define the dimensions 178 | \begin{dims}[ 179 | % inverted labels 180 | label-cs=textinv, 181 | % value scale on dim2 axis 182 | label-on=2] 183 | % Specify placement of each 184 | % labels 185 | \value[above]{Dim 1} 186 | \value[right]{Dim 2} 187 | \value[below]{Dim 3} 188 | \value[below]{Dim 4} 189 | \value[left]{Dim 5} 190 | \end{dims} 191 | 192 | % Add least one set should 193 | % be defined. 194 | \begin{set} 195 | \value{4} % Dim 1 196 | \value{3} % Dim 2 197 | \value{4} % Dim 3 198 | \value{5} % Dim 4 199 | \value{3} % Dim 5 200 | \end{set} 201 | \end{kiviatchart} 202 | \end{example} 203 | 204 | \subsubsection{Rounded} 205 | 206 | \begin{example} 207 | % rounded replace polygons by circles 208 | % curves 209 | \begin{kiviatchart}[ 210 | scale=0.75, 211 | rounded] 212 | % Define the dimensions 213 | \begin{dims}[ 214 | % inverted labels 215 | label-cs=textinv, 216 | % value scale on dim2 axis 217 | label-on=2] 218 | % Specify placement of each 219 | % labels 220 | \value[above]{Dim 1} 221 | \value[right]{Dim 2} 222 | \value[below]{Dim 3} 223 | \value[below left]{Dim 4} 224 | \value[left]{Dim 5} 225 | \end{dims} 226 | 227 | % Add least one set should 228 | % be defined. 229 | \begin{set} 230 | \value{4} % Dim 1 231 | \value{3} % Dim 2 232 | \value{4} % Dim 3 233 | \value{5} % Dim 4 234 | \value{3} % Dim 5 235 | \end{set} 236 | \end{kiviatchart} 237 | \end{example} 238 | 239 | \subsubsection{Multi-set} 240 | 241 | \begin{example} 242 | \begin{kiviatchart}[ 243 | scale=0.75, 244 | rounded] 245 | \begin{dims}[ 246 | % bigger radius for labels 247 | radius=3.7cm, 248 | % hide unit labels 249 | label-on=0] 250 | \value[above]{Dim 1} 251 | \value[right]{Dim 2} 252 | \value[below]{Dim 3} 253 | \value[below]{Dim 4} 254 | \value[left]{Dim 5} 255 | \end{dims} 256 | 257 | \begin{set}[% red set 258 | fill=red, 259 | % big rectangle dots 260 | dot-options={ 261 | fill,rectangle, 262 | inner sep=2pt 263 | }] 264 | \value{4} % Dim 1 265 | \value{3} % Dim 2 266 | \value{4} % Dim 3 267 | \value{5} % Dim 4 268 | \value{2} % Dim 5 269 | \end{set} 270 | 271 | \begin{set}[% green set 272 | fill=green, 273 | % big diamond dots 274 | dot-options={ 275 | fill,diamond, 276 | inner sep=2pt 277 | }] 278 | \value{2} % Dim 1 279 | \value{4} % Dim 2 280 | \value{3} % Dim 3 281 | \value{4} % Dim 4 282 | \value{5} % Dim 5 283 | \end{set} 284 | 285 | \begin{set}[% blue set 286 | fill=blue, 287 | % big semicircle dots 288 | dot-options={ 289 | fill,semicircle, 290 | inner sep=2pt 291 | }] 292 | \value{5} % Dim 1 293 | \value{2} % Dim 2 294 | \value{4} % Dim 3 295 | \value{3} % Dim 4 296 | \value{4} % Dim 5 297 | \end{set} 298 | \end{kiviatchart} 299 | \end{example} 300 | 301 | \subsection{To do} 302 | 303 | At the moment the environments are not user friendly. We could provide basic sanity checks, 304 | with error messages when theses rules are violated : 305 | 306 | \begin{itemize} 307 | \item one and only one \env{dims} declared before any \env{set} 308 | \item at least 3 dimensions are declared 309 | \item all \env{set} have the same number of \cs{value} than the \env{dims} 310 | \item \cs{value} in \env{set} is between 0 and \key{units} 311 | \end{itemize} 312 | 313 | \section{Ball chart} 314 | 315 | \subsection{Usage} 316 | 317 | \DescribeEnv{ballchart} 318 | Environment that hold a ball chart. Accepts an optional argument \oarg{clist} which is comma 319 | separated list of keywords and values : 320 | 321 | \Describe{Option}{n}{:int}[5] 322 | The number of circles per bar 323 | 324 | \Describe{Option}{gap}{:dim}[1ex] 325 | Gap between bars 326 | 327 | \Describe{Option}{cgap}{:dim}[1pt] 328 | Gap between circles 329 | 330 | \Describe{Option}{radius}{:dim}[2.5mm] 331 | Radius of the circles 332 | 333 | \Describe{Option}{label-cs}{:str}[identity] 334 | Macro name to format labels 335 | 336 | \Describe{Option}{fill-options}{:prop}[\{fill=black\}] 337 | \TikZ{} options to fill the balls with 338 | 339 | \Describe{Option}{draw-options}{:prop}[\{draw=none\}] 340 | \TikZ{} options to draw the balls with 341 | 342 | \Describe{Option}{label-options}{:prop}[\{left\}] 343 | \TikZ{} options for dimensions axis 344 | 345 | \Describe{Option}{label-cs}{:str}[identity] 346 | Macro name to format labels 347 | 348 | \Describe{Option}{label-pos}{:str}[left] 349 | Position of the label. Possible values : 350 | \begin{itemize} 351 | \item \texttt{left}, \texttt{right} 352 | \item \texttt{above}, \texttt{below} 353 | \item \texttt{above right}, \texttt{above left} 354 | \item \texttt{below right}, \texttt{below left} 355 | \end{itemize} 356 | 357 | \Describe{Option}{value-cs}{:str}[nop] 358 | cs name to format values with 359 | 360 | \Describe{Option}{*}{:keyval} 361 | All other options are passed to \env{tikzpicture} 362 | 363 | \DescribeMacro{value} 364 | \cs{value}\marg{label}\marg{percent} is used to add a new bar. 365 | 366 | \subsection{Examples} 367 | 368 | \subsubsection{Simple} 369 | 370 | \begin{example} 371 | \begin{ballchart} 372 | \value{Dim 1}{95} 373 | \value{Dim 2}{80} 374 | \value{Dim 3}{80} 375 | \value{Dim 4}{20} 376 | \end{ballchart} 377 | \end{example} 378 | 379 | \begin{example} 380 | \begin{ballchart}[ 381 | % inverted labels 382 | label-cs=textinv, 383 | % to the right 384 | label-pos=right, 385 | % closer to the bar 386 | label-options={xshift=-8mm}, 387 | % show circle 388 | draw-options={draw=black!30}] 389 | \value{Dim 1}{95} 390 | \value{Dim 2}{80} 391 | \value{Dim 3}{80} 392 | \value{Dim 4}{20} 393 | \end{ballchart} 394 | \end{example} 395 | 396 | \subsubsection{Delimited} 397 | 398 | \begin{example} 399 | \begin{ballchart}[ 400 | % 6 circles per bar 401 | n=6, 402 | % red labels 403 | label-cs=redbf, 404 | % closer to bar 405 | label-options={xshift=4mm}, 406 | % bigger gap 407 | gap=1.5ex, 408 | cgap=3pt, 409 | % fill in red 410 | fill-options={fill=red!50}, 411 | % black circle 412 | draw-options={draw=black}] 413 | \value{Dim 1}{95} 414 | \value{Dim 2}{80} 415 | \value{Dim 3}{80} 416 | \value{Dim 4}{20} 417 | \end{ballchart} 418 | \end{example} 419 | 420 | \section{Bar chart} 421 | 422 | \subsection{Usage} 423 | 424 | \DescribeEnv{barchart} 425 | Environment that hold a bar chart. Accepts an optional argument \oarg{clist} which is comma 426 | separated list of keywords and values : 427 | 428 | \Describe{Option}{width}{:dim}[3cm] 429 | Maximum width 430 | 431 | \Describe{Option}{height}{:dim}[3.5mm] 432 | Bar height 433 | 434 | \Describe{Option}{gap}{:dim}[1ex] 435 | Gap between bars 436 | 437 | \Describe{Option}{fill-options}{:prop}[\{fill=none\}] 438 | \TikZ{} options to fill the bar with 439 | 440 | \Describe{Option}{draw-options}{:prop}[\{fill=black\}] 441 | \TikZ{} options to draw the bar with 442 | 443 | \Describe{Option}{label-options}{:prop}[\{\}] 444 | \TikZ{} options for dimensions axis 445 | 446 | \Describe{Option}{label-cs}{:str}[identity] 447 | Macro name to format labels 448 | 449 | \Describe{Option}{label-pos}{:str}[left] 450 | Position of the label. Possible values : 451 | \begin{itemize} 452 | \item \texttt{left}, \texttt{right} 453 | \item \texttt{above}, \texttt{below} 454 | \item \texttt{above right}, \texttt{above left} 455 | \item \texttt{below right}, \texttt{below left} 456 | \end{itemize} 457 | 458 | \Describe{Option}{value-cs}{:str}[nop] 459 | cs name to format values with 460 | 461 | \Describe{Option}{*}{:keyval} 462 | All other options are passed to \env{tikzpicture} 463 | 464 | \DescribeMacro{value} 465 | \cs{value}\marg{label}\marg{percent} is used to add a new bar. 466 | 467 | \subsection{Examples} 468 | 469 | \subsubsection{Simple} 470 | 471 | \begin{example} 472 | \begin{barchart} 473 | \value{Dim 1}{60} 474 | \value{Dim 2}{100} 475 | \value{Dim 3}{70} 476 | \value{Dim 4}{70} 477 | \value{Dim 5}{40} 478 | \value{Dim 6}{60} 479 | \end{barchart} 480 | \end{example} 481 | 482 | \begin{example} 483 | \begin{barchart}[ 484 | % inverted labels 485 | label-cs=textinv, 486 | % to the right 487 | label-pos=right, 488 | % closer to bar 489 | label-options={xshift=-8mm}] 490 | \value{Dim 1}{60} 491 | \value{Dim 2}{100} 492 | \value{Dim 3}{70} 493 | \value{Dim 4}{70} 494 | \value{Dim 5}{40} 495 | \value{Dim 6}{60} 496 | \end{barchart} 497 | \end{example} 498 | 499 | \subsubsection{Gauge} 500 | 501 | \begin{example} 502 | \begin{barchart}[ 503 | % 4cm wide bars 504 | width=4cm, 505 | % inverted labels 506 | label-cs=redbf, 507 | % closer to bar 508 | label-options={xshift=4mm}, 509 | % show values 510 | value-cs=whitebfp, 511 | % bigger gap 512 | gap=1.5ex, 513 | % bar in red 514 | draw-options={ 515 | draw=red!50, 516 | fill=red!50}, 517 | % show borders in red 518 | fill-options={ 519 | fill=red!30, 520 | draw=red!30}] 521 | \value{Dim 1}{60} 522 | \value{Dim 2}{100} 523 | \value{Dim 3}{70} 524 | \value{Dim 4}{70} 525 | \value{Dim 5}{40} 526 | \value{Dim 6}{60} 527 | \end{barchart} 528 | \end{example} 529 | 530 | \section{Bubble chart} 531 | 532 | \subsection{Usage} 533 | 534 | \DescribeEnv{bubblechart} 535 | Environment that hold a bubble chart. Accepts an optional argument \oarg{clist} which 536 | is comma separated list of keywords and values : 537 | 538 | \Describe{Option}{radius}{:dim}[1cm] 539 | Max radius 540 | 541 | \Describe{Option}{gap}{:dim}[1ex] 542 | Gap between bubbles 543 | 544 | \Describe{Option}{fill-options}{:prop}[\{fill=none,draw=none\}] 545 | \TikZ{} options to fill/draw the background with 546 | 547 | \Describe{Option}{draw-options}{:prop}[\{fill=black\}] 548 | \TikZ{} options to fill/draw the bubble with 549 | 550 | \Describe{Option}{label-cs}{:str}[identity] 551 | Macro name to format labels 552 | 553 | \Describe{Option}{label-pos}{:str}[above] 554 | Position of the label. Possible values : 555 | \begin{itemize} 556 | \item \texttt{left}, \texttt{right} 557 | \item \texttt{above}, \texttt{below} 558 | \item \texttt{above right}, \texttt{above left} 559 | \item \texttt{below right}, \texttt{below left} 560 | \end{itemize} 561 | 562 | \Describe{Option}{value-cs}{:str}[nop] 563 | cs name to format values with 564 | 565 | \Describe{Option}{vertical}{:bool}[false] 566 | Stack the bubble vertically instead of horizontally 567 | 568 | \Describe{Option}{*}{:keyval} 569 | All other options are passed to \env{tikzpicture} 570 | 571 | \DescribeMacro{value} 572 | \cs{value}\marg{label}\marg{percent} is used to add a new bubble. 573 | 574 | \subsection{Examples} 575 | 576 | \subsubsection{Horizontal} 577 | 578 | \begin{example} 579 | \begin{bubblechart} 580 | \value{Dim 1}{80} 581 | \value{Dim 2}{90} 582 | \value{Dim 3}{50} 583 | \end{bubblechart} 584 | \end{example} 585 | 586 | \begin{example} 587 | \begin{bubblechart}[ 588 | % inverted labels 589 | label-cs=textinv, 590 | % below bubble 591 | label-pos=below, 592 | % show borders 593 | fill-options={ 594 | fill=none, 595 | draw=black!30}] 596 | \value{Dim 1}{80} 597 | \value{Dim 2}{90} 598 | \value{Dim 3}{50} 599 | \end{bubblechart} 600 | \end{example} 601 | 602 | \begin{example} 603 | \begin{bubblechart}[ 604 | % label in red 605 | label-cs=redbf, 606 | % below bubble 607 | label-pos=below, 608 | % show value 609 | value-cs=whitebfp, 610 | % bubble in red 611 | draw-options={ 612 | draw=red!50, 613 | fill=red!50}, 614 | % background in light red 615 | fill-options={ 616 | fill=red!10}] 617 | \value{Dim 1}{80} 618 | \value{Dim 2}{90} 619 | \value{Dim 3}{50} 620 | \end{bubblechart} 621 | \end{example} 622 | 623 | \subsubsection{Vertical} 624 | 625 | \begin{example} 626 | \begin{bubblechart}[ 627 | % stack bubbles vertically 628 | vertical, 629 | % label in bold 630 | label-cs=textbf, 631 | % show values 632 | value-cs=whitebfp, 633 | % to the right 634 | label-pos=right, 635 | % show max as dotted line 636 | fill-options={ 637 | fill=none, 638 | draw=black, 639 | dotted}] 640 | \value{Dim 1}{80} 641 | \value{Dim 2}{90} 642 | \value{Dim 3}{50} 643 | \end{bubblechart} 644 | \end{example} 645 | 646 | \section{Radial chart} 647 | 648 | \subsection{Usage} 649 | 650 | \DescribeEnv{radialchart} 651 | Environment that hold a radial chart. Accepts an optional argument \oarg{clist} which 652 | is comma separated list of keywords and values : 653 | 654 | \Describe{Option}{radius}{:dim}[1cm] 655 | Max radius 656 | 657 | \Describe{Option}{gap}{:dim}[2.5ex] 658 | Gap between radials 659 | 660 | \Describe{Option}{line width}{:dim}[3mm] 661 | Line width to draw the radials with 662 | 663 | \Describe{Option}{fill-options}{:prop}[\{fill=none,draw=black!10\}] 664 | \TikZ{} options to fill/draw the center of the radial with 665 | 666 | \Describe{Option}{draw-options}{:prop}[black] 667 | \TikZ{} options to draw the radial with 668 | 669 | \Describe{Option}{label-options}{:prop}[\{\}] 670 | \TikZ{} options drawing for unit labels 671 | 672 | \Describe{Option}{label-cs}{:str}[identity] 673 | cs name to format labels with 674 | 675 | \Describe{Option}{label-pos}{:str}[above] 676 | Position of the label. Possible values : 677 | \begin{itemize} 678 | \item \texttt{left}, \texttt{right} 679 | \item \texttt{above}, \texttt{below} 680 | \item \texttt{above right}, \texttt{above left} 681 | \item \texttt{below right}, \texttt{below left} 682 | \end{itemize} 683 | 684 | \Describe{Option}{value-cs}{:str}[identity] 685 | cs name to format values with 686 | 687 | \Describe{Option}{vertical}{:bool}[false] 688 | Stack radials vertically instead of horizontally 689 | 690 | \Describe{Option}{*}{:keyval}[line cap=round] 691 | All other options are passed to \env{tikzpicture} 692 | 693 | \subsection{Examples} 694 | 695 | \subsubsection{Horizontal} 696 | 697 | \begin{example} 698 | \begin{radialchart} 699 | \value{Dim 1}{80} 700 | \value{Dim 2}{90} 701 | \value{Dim 3}{50} 702 | \end{radialchart} 703 | \end{example} 704 | 705 | \begin{example} 706 | \begin{radialchart}[ 707 | % inverted label, 708 | label-cs=textinv, 709 | % below radial, 710 | label-pos=below, 711 | % in red bold. 712 | value-cs=redbfp, 713 | % ring is red 714 | draw-options={red!50}, 715 | % disk is light red 716 | fill-options={ 717 | fill=red!10}] 718 | \value{Dim 1}{80} 719 | \value{Dim 2}{90} 720 | \value{Dim 3}{50} 721 | \end{radialchart} 722 | \end{example} 723 | 724 | \subsubsection{Vertical} 725 | 726 | \begin{example} 727 | \begin{radialchart}[ 728 | % stack radials vertically 729 | vertical, 730 | % label as tenth fraction 731 | value-cs=tenrate, 732 | % to the right 733 | label-pos=right, 734 | % thicker line 735 | line width=3.5mm, 736 | % with rect end 737 | line cap=butt, 738 | % same color for disk and ring 739 | fill-options={ 740 | draw=black!10, 741 | fill=black!10}] 742 | \value{Dim 1}{80} 743 | \value{Dim 2}{90} 744 | \value{Dim 3}{50} 745 | \end{radialchart} 746 | \end{example} 747 | 748 | \section{Arc chart} 749 | 750 | \subsection{Usage} 751 | 752 | \DescribeEnv{arcchart} 753 | Environment that hold an arc chart. Accepts an optional argument \oarg{clist} which 754 | is comma separated list of keywords and values : 755 | 756 | \Describe{Option}{radius}{:dim}[1cm] 757 | Radius of outer arc 758 | 759 | \Describe{Option}{gap}{:dim}[2.5ex] 760 | Gap between arcs 761 | 762 | \Describe{Option}{line width}{:dim}[4mm] 763 | Line width to draw the arc with 764 | 765 | \Describe{Option}{fill-options}{:prop}[\{fill=none,draw=black!10\}] 766 | \TikZ{} options to fill/draw the background of the arcs with 767 | 768 | \Describe{Option}{draw-options}{:prop}[black] 769 | \TikZ{} options to draw the arcs with 770 | 771 | \Describe{Option}{label-options}{:prop}[\{\}] 772 | \TikZ{} options drawing for unit labels 773 | 774 | \Describe{Option}{label-cs}{:str}[identity] 775 | cs name to format labels with 776 | 777 | \Describe{Option}{value-options}{:prop}[{}] 778 | \TikZ{} options to draw values with 779 | 780 | \Describe{Option}{value-cs}{:str}[nop] 781 | cs name to format values with 782 | 783 | \Describe{Option}{value-angle}{:fp}[90] 784 | Angle at which to draw the values 785 | 786 | \Describe{Option}{*}{:keyval}[line cap=round] 787 | All other options are passed to \env{tikzpicture} 788 | 789 | \subsection{Examples} 790 | 791 | \subsubsection{Simple} 792 | 793 | \begin{example} 794 | \begin{arcchart} 795 | \value{Dim 1}{100} 796 | \value{Dim 2}{90} 797 | \value{Dim 3}{60} 798 | \value{Dim 4}{45} 799 | \end{arcchart} 800 | \end{example} 801 | 802 | \subsubsection{Colorful} 803 | 804 | \begin{example} 805 | \begin{arcchart}[ 806 | % bigger radius, 807 | radius=2.5cm, 808 | % and gap 809 | gap=1.5mm, 810 | % show values 811 | value-cs=whitebf, 812 | % at 30° 813 | value-angle=30, 814 | % thicker line width, 815 | line width=4.5mm, 816 | % with square end 817 | line cap=butt] 818 | % each ring has its own color 819 | \value[blue!50]{Dim 1}{100} 820 | \value[cyan!50]{Dim 2}{90} 821 | \value[purple!50]{Dim 3}{60} 822 | \value[red!50]{Dim 4}{45} 823 | \end{arcchart} 824 | \end{example} 825 | 826 | \section{Macros} 827 | 828 | \subsection{Package} 829 | 830 | These are macros defined in \pkg{l3charts.sty} and used as default value for \opt{label-cs} 831 | or \opt{value-cs} options. 832 | 833 | \DescribeMacro{tinytt} 834 | Macro used to format its argument as tiny monospace 835 | 836 | \begin{minted}{latex} 837 | \cs_set:Npn \tinytt #1 {\texttt{\tiny #1}} 838 | \end{minted} 839 | 840 | \DescribeMacro{identity} 841 | Macro used to return the first argument as is 842 | 843 | \begin{minted}{latex} 844 | \cs_set:Npn \identity #1 {#1} 845 | \end{minted} 846 | 847 | \DescribeMacro{nop} 848 | Macro used to consume the first argument and do nothing 849 | 850 | \begin{minted}{latex} 851 | \cs_set:Npn \nop #1 {} 852 | \end{minted} 853 | 854 | \DescribeMacro{percent} 855 | Macro used to append a percent to its argument 856 | 857 | \begin{minted}{latex} 858 | \cs_set:Npn \percent #1 {#1\%} 859 | \end{minted} 860 | 861 | \subsection{Examples} 862 | 863 | These macros are defined for the examples presented in this document and are not part of 864 | the module \pkg{l3charts.sty}. 865 | 866 | \DescribeMacro{textbfp} 867 | Macro used to format its argument as bold with appended \% 868 | 869 | \begin{minted}{latex} 870 | \NewDocumentCommand\textbfp{m}{\textbf{\percent{#1}}} 871 | \end{minted} 872 | 873 | \DescribeMacro{tenrate} 874 | Macro used to format its argument as fraction of ten 875 | 876 | \begin{minted}{latex} 877 | \ExplSyntaxOn 878 | \NewDocumentCommand\tenrate{m}{\int_eval:n{#1/10}/10} 879 | \ExplSyntaxOff 880 | \end{minted} 881 | 882 | \DescribeMacro{textinv} 883 | Macro used to format its argument as white text on black background 884 | 885 | \begin{minted}{latex} 886 | \NewDocumentCommand\textinv{m}{\colorbox{black}{\textcolor{white}{#1}}} 887 | \end{minted} 888 | 889 | \DescribeMacro{redbf} 890 | Macro used to format its argument as bold and red 891 | 892 | \begin{minted}{latex} 893 | \NewDocumentCommand\redbf{m}{\textcolor{red!50}{\textbf{#1}}} 894 | \end{minted} 895 | 896 | \DescribeMacro{redbfp} 897 | Macro used to format its argument as bold and red with appended \% 898 | 899 | \begin{minted}{latex} 900 | \NewDocumentCommand\redbfp{m}{\textcolor{red!50}{\textbfp{#1}}} 901 | \end{minted} 902 | 903 | \DescribeMacro{whitebf} 904 | Macro used to format its argument as bold and white 905 | 906 | \begin{minted}{latex} 907 | \NewDocumentCommand\whitebf{m}{\textcolor{white}{\textbf{#1}}} 908 | \end{minted} 909 | 910 | \DescribeMacro{whitebfp} 911 | Macro used to format its argument as bold and white with appended \% 912 | 913 | \begin{minted}{latex} 914 | \NewDocumentCommand\whitebfp{m}{\textcolor{white}{\textbfp{#1}}} 915 | \end{minted} 916 | 917 | % \newpage 918 | \PrintIndex 919 | 920 | \section{Changes} 921 | 922 | \begin{History} 923 | 924 | \Version{0.7.1}{2023/01/04} 925 | \item add a \opt{rounded} option to \env{kiviatchart} to use circles for the scale and curves 926 | for the sets. 927 | 928 | \Version{0.7.0}{2022/08/01} 929 | \item add a \env{arcchart} 930 | \item rename \opt{line-width} to \opt{line width} for consistency with \TikZ 931 | \item use choice to restrict values on \opt{label-pos} 932 | \item remove spurious ; and replace \texttt{c\_space\_tl} by \textasciitilde 933 | 934 | \Version{0.6.1}{2022/07/26} 935 | \item add a \opt{label-on} option for \env{dims} of \env{kiviatchart} 936 | 937 | \Version{0.6.0}{2022/07/26} 938 | \item draw \env{kiviatchart} dimensions clockwise with a starting \opt{angle} of 90 939 | \item allow value of 0 for \env{set} 940 | \item rename \opt{labels-radius} to \opt{radius} and move to \env{dims} 941 | 942 | \Version{0.5.1}{2022/07/19} 943 | \item remove hard coded \% in value. 944 | 945 | \Version{0.5.0}{2022/07/18} 946 | \item convert all \type{fp} to \type{dim} for usability 947 | \item rename \opt{v-sep} and \opt{h-sep} options of \env{ballchart} to \opt{gap} and \opt{cgap} 948 | for consistency 949 | 950 | \Version{0.4.0}{2022/07/17} 951 | \item add values to \env{bubblechart} 952 | \item label positioning on \env{barchart} and \env{ballchart} 953 | \item swap \opt{fill-options} and \opt{draw-options} for \env{barchart} for consistency 954 | 955 | \Version{0.3.0}{2022/07/15} 956 | \item add a \env{radialchart} to draw radials 957 | \item add a vertical mode to \env{bubblechart} and allow positioning of the label 958 | \item swap \opt{fill-options} and \opt{draw-options} for \env{bubblechart} for consistency 959 | 960 | \Version{0.2.0}{2022/07/04} 961 | \item define a document class borrowed from 962 | \href{https://github.com/schlcht/microtype}{\pkg{microtype}} 963 | 964 | \Version{0.1.0}{2022/07/01} 965 | \item Initial version 966 | 967 | \end{History} 968 | 969 | \end{document} 970 | --------------------------------------------------------------------------------