Bases de TikZ

TikZ est le langage de dessin qui habite LaTeX, et son nom est déjà un avertissement. C’est un acronyme récursif de l’allemand TikZ ist kein Zeichenprogramm — « TikZ n’est pas un programme de dessin » — qui annonce d’emblée qu’on ne déplacera aucune forme à la souris. On écrit plutôt \draw (0,0) -- (2,1); dans le même fichier que le texte, et la figure est composée avec les mêmes polices et la même qualité que le paragraphe voisin. Le marché est clair : on renonce au confort, on gagne la précision et la reproductibilité. Cette page le suit depuis une première forme jusqu’aux coordonnées et aux nœuds, puis jusqu’à la question du temps de compilation et des remèdes.

Pourquoi TikZ tient à n’être « pas un programme de dessin » — TikZ et PGF

Le nom est une plaisanterie dans la tradition GNU, et le manuel le dit lui-même : TikZ est un acronyme récursif dans la lignée de « gnu’s Not Unix », placé là pour prévenir le lecteur de ce qui l’attend. En échange de la souris, on reçoit l’approche TeX appliquée aux images — placement exact, réemploi par macros, lettrage accordé au texte environnant. Ce qu’on cède, c’est le WYSIWYG. Le manuel énonce aussi franchement le reste de la facture et cite dès ses premières pages la courbe d’apprentissage raide et la recompilation qui suit la moindre retouche.

En dessous, ce qui trace réellement les traits est PGF, pour « portable graphics format », et le nom a une origine limpide. Son auteur voulait écrire des macros graphiques utilisables aussi bien avec pdfLaTeX qu’avec le LaTeX classique passant par PostScript ; il fallait donc une couche capable d’absorber les différences entre les chaînes de sortie — d’où portable. Comme le PGF brut est pénible à écrire, TikZ a été posé par-dessus comme une syntaxe lisible et tapable. On dit « PGF/TikZ » d’un seul souffle, mais ce que l’on tape est presque toujours le côté TikZ, et \usepackage{tikz} charge PGF avec lui.

Les deux sont l’œuvre de Till Tantau. De son propre récit, il a commencé à bricoler avec TeX pendant ses études, ce qui a donné la classe de présentation beamer ; il a ensuite écrit TikZ pour les figures de sa thèse de doctorat. La façon standard de faire des diapositives en LaTeX et la façon standard d’y dessiner, deux choses apparemment sans rapport, viennent donc de la même personne. Un chiffre dit à quel point le projet a grandi depuis : le manuel de la version 3.1.10, pgfmanual.pdf, compte 1 321 pages. Sa couverture est elle-même une image TikZ — arbres fractals et flocons de Koch sur un paysage crépusculaire — imprimée à côté du code source qui la produit.

Il n’y a presque rien à configurer. Une ligne, \usepackage{tikz}, dans le préambule suffit pour pdfLaTeX, LuaLaTeX et XeLaTeX. Seul le passage par DVI demande un soin supplémentaire : avec pLaTeX ou upLaTeX, il faut aussi nommer le pilote — généralement dvipdfmx — en option de classe. Voici un document minimal qui compile.

document.tex
\documentclass{article}   % with pLaTeX: \documentclass[dvipdfmx]{jsarticle}
\usepackage{tikz}
\begin{document}
A line and a circle:
\begin{tikzpicture}
  \draw (0,0) -- (2,1);
  \draw (2.8,0.5) circle [radius=0.5cm];
\end{tikzpicture}
\end{document}

Dessiner une première forme : l’environnement tikzpicture et le point-virgule

Toute figure se dessine à l’intérieur de \begin{tikzpicture}\end{tikzpicture}, et chaque commande de dessin se termine par un point-virgule ;. L’environnement est la feuille qui porte une image ; les options placées entre crochets après le nom de l’environnement valent pour la feuille entière — [scale=2] la double, [thick] épaissit tous les traits. Pour une image tenant sur une ligne, inutile d’ouvrir l’environnement : il existe les formes en ligne \tikz{...} et, pour une commande unique, \tikz \draw ...;.

latex
% inline: one command, no environment
\tikz \draw (0,0) -- (1.5,0);

% environment: one canvas, several commands, shared options
\begin{tikzpicture}[scale=1.2, thick]
  \draw (-1.5,0) -- (1.5,0);
  \draw (0,-1.5) -- (0,1.5);
\end{tikzpicture}

Un point-virgule oublié donne ! Package tikz Error: Giving up on this path. Did you forget a semicolon?. Le désagrément vient de ce que le numéro de ligne signalé n’est pas celui de la ligne fautive mais celui de la suivante. TikZ continue d’avaler des tokens tant qu’ils peuvent encore appartenir au chemin, et ne renonce qu’en rencontrant quelque chose d’impossible — le plus souvent le \draw suivant. Quand l’erreur pointe un \draw, c’est donc la fin de la ligne au-dessus qu’il faut examiner.

Coordonnées et chemins : \draw, \fill, -- et circle

Le dessin dans TikZ tourne autour du chemin — une route disant comment aller de point en point — et le nom de la commande décide de la façon dont cette route apparaît. \draw la trace, \fill remplit l’intérieur d’une route fermée, \filldraw remplit puis trace le contour, et \path définit la route sans rien dessiner. Ce dernier semble inutile ; c’est pourtant ainsi qu’on dépose discrètement des coordonnées et des nœuds pour un usage ultérieur. Reprenez la même suite de points en changeant seulement le nom de la commande, et un contour devient un aplat : cette orthogonalité est l’ossature de la conception.

Un point s’écrit de trois façons. En cartésien, (x,y) compte par défaut en centimètres : (1,2) vaut 1 cm à droite et 2 cm vers le haut, et les unités peuvent être explicites, comme (1cm,2pt). En polaire, (30:1cm) se lit « 1 cm dans la direction 30° ». Enfin une coordonnée nommée, posée une fois par \coordinate (P) at (1,1);, s’appelle ensuite simplement (P). La différence entre + et ++ relatifs mérite d’être apprise avant qu’elle ne morde : ++(1,0) avance de 1 cm à droite en emportant le point courant, tandis que +(1,0) atteint le même endroit mais laisse le point courant sur place. Plusieurs ++ enchaînés donnent une ligne brisée ; plusieurs + donnent des rayons partant d’un même point d’ancrage.

La progression de la route est fixée par les opérations de chemin. -- est une droite du point précédent au suivant ; rectangle prend deux coins opposés, circle un centre, auxquels s’ajoutent ellipse, arc et grid. Les courbes viennent de .. controls .. (Bézier) ou de to[bend left], et une forme se ferme par un -- cycle final qui ramène au départ. Les arguments de forme s’écrivent aujourd’hui en paires clé-valeur entre crochets : circle [radius=10pt], ellipse [x radius=20pt, y radius=10pt], arc [start angle=0, end angle=30, radius=3mm], grid [step=.5cm]. L’ancienne forme entre parenthèses circle (10pt) fonctionne encore, mais le code neuf se lit mieux entre crochets, et ajouter une clé n’y coûte rien.

latex
\begin{tikzpicture}
  \draw[step=.5cm, gray, very thin] (-1.4,-1.4) grid (1.4,1.4);
  \draw (0,0) circle [radius=1cm];
  \draw (0,0) -- (30:1cm);                       % polar: 1 cm at 30 degrees
  \fill[blue!20] (0,0) rectangle (0.5,0.5);
  \draw[red] (-1.2,-1.2) -- ++(0.6,0) -- ++(0,0.6);   % ++ carries the current point
\end{tikzpicture}

Le résultat tient en une image : une grille gris pâle au pas de 0,5 cm ; un cercle de rayon 1 cm centré à l’origine ; un court segment quittant l’origine à 30° ; un carré de 0,5 cm à l’origine, rempli de bleu clair ; et, en bas à gauche, un coude rouge qui va 0,6 cm à droite puis 0,6 cm vers le haut. La couleur blue!20 signifie « 20 % de bleu », les 80 % restants étant du blanc, d’où la teinte pâle. Deux couleurs, comme dans red!50!black, se mélangent ; la notation vaut aussi bien pour les remplissages que pour les traits.

Nœuds : poser du texte et des boîtes, puis les relier par leur nom

Un nœud (node) pose du texte ou une boîte à une coordonnée, selon la forme \node[options] (name) at (coordinate) {contents};. Les crochets accueillent les options : draw pour une bordure, circle ou rectangle pour la forme, fill=blue!20 pour le remplissage, et ainsi de suite. Tout l’intérêt de la construction tient à ceci : le nom entre parenthèses devient une poignée sur ce nœud. Dès lors (name) désigne sa position, et un ancrage tel que (name.north) ou (name.east) nomme un point précis de son contour.

De là vient le motif qu’on emploie sans cesse : poser d’abord des nœuds nommés, puis relier les noms. Relier par nom de nœud plutôt que par coordonnées brutes signifie que la ligne suit d’elle-même si l’on déplace un nœud d’un centimètre plus tard. Et comme la ligne s’arrête au bord du nœud, elle ne pénètre jamais dans le cercle ni dans la boîte. Un nœud peut aussi se poser le long d’un chemin : \draw (a) -- (b) node[midway, above] {$f$}; place une étiquette juste au-dessus du milieu du trait — la façon habituelle de nommer une flèche.

latex
\begin{tikzpicture}
  \node (a) at (0,0)   [draw, circle, fill=blue!20] {A};
  \node (b) at (2.5,0) [draw, circle, fill=blue!20] {B};
  \draw[->] (a) -- (b) node[midway, above] {$f$};
  \draw[dashed] (a.south) -- (b.south);   % .south is an anchor on the border
\end{tikzpicture}

L’image dispose deux nœuds circulaires bleu pâle étiquetés « A » et « B », distants de 2,5 cm, trace une flèche de A vers B avec $f$ posé au-dessus de son milieu, et relie le bas des deux cercles par un trait pointillé. Une mine mérite d’être contournée ici : un nœud doit être défini avant d’être référencé. Nommer un nœud non encore posé, ou mal orthographier un nœud existant, donne ! Package pgf Error: No shape named ‘b’ is known. Les noms se tapent de travers facilement ; devant ce message, il faut vérifier l’orthographe et s’assurer que le \node figure bien plus haut.

Options de trait et styles réutilisables avec \tikzset

L’aspect d’un trait est réglé par les options entre crochets : les épaisseurs thin / thick / very thick, les couleurs comme red ou blue!50, dashed et dotted, rounded corners, et les flèches -> (pointe à la fin), <- (au début), <-> (aux deux bouts). Un raccourci pragmatique se cache derrière les flèches : TikZ considère toute option inconnue contenant un - comme une spécification de flèche. Une clé mal tapée connaît donc deux sorts, selon son orthographe. Sans -, TikZ prévient : ! Package pgfkeys Error: I do not know the key ‘/tikz/thikc’ and I am going to ignore it. Perhaps you misspelled it. Avec un - à l’intérieur, la coquille est lue comme une spécification de flèche et peut échouer en silence sur un résultat étrange.

OptionSens
thick / very thickTraits plus épais (jusqu’à ultra thick)
red, fill=blue!50Couleur de trait ou de remplissage ; !n règle l’intensité, red!50!black mélange
dashed / dottedLignes tiretées ou pointillées
rounded cornersArrondir les coins d’un chemin
->, <-, <->Pointe de flèche à la fin, au début ou aux deux bouts
scale=2Mettre cette image ou ce chemin à l’échelle 2
node distance=1cmÉcart par défaut pour le placement relatif avec positioning

Écrire la même décoration une troisième fois, c’est le signal d’en faire un style. Défini dans le préambule par \tikzset{name/.style={...}}, il ne demande ensuite qu’un nom ; et si l’apparence doit changer, une ligne de la définition change tout d’un coup. Si le style ne vaut que dans une image, il se déclare sur place dans les options de tikzpicture. Il existe en outre des styles qui portent sur tous les éléments d’un type — every node/.style={...} — et ils deviennent indispensables dès qu’une figure comme un organigramme exige des nœuds uniformes.

latex
\tikzset{help lines/.style={color=blue!50, very thin}}

\begin{tikzpicture}
  \draw[help lines] (0,0) grid (3,2);
  \draw[thick, red, ->, rounded corners] (0,0) -- (1,2) -- (3,2);
\end{tikzpicture}

On obtient une grille bleu pâle de (0,0) à (3,2), puis un trait rouge épais qui part de l’origine vers (1,2) puis (3,2), coins arrondis et pointe de flèche à la fin. Comme la grille passe par un style, remplacer very thin par thin ne modifie pas seulement cette grille-ci mais toutes celles tracées avec help lines.

Quelles \usetikzlibrary charger réellement

Le cœur de TikZ est volontairement réduit ; les parties spécialisées sont réparties en bibliothèques, chargées depuis le préambule par \usetikzlibrary{...}, séparées par des virgules s’il y en a plusieurs. Tant qu’une bibliothèque n’est pas chargée, sa syntaxe commode n’existe pas : écrire right=of a sans positioning mène tout droit à l’erreur « I do not know the key » de la section précédente. Voici celles qui méritent vraiment leur place la première année.

  • arrows.meta — un riche ensemble de pointes de flèche réglables (-{Stealth}, -{Latex}, …). Le manuel signale les anciennes arrows et arrows.spaced comme obsolètes ; le code neuf prend celle-ci.
  • positioning — placement relatif des nœuds : right=of a, below=1cm of b, l’écart par défaut étant fixé par node distance.
  • calc — calcul de coordonnées : ($(a)+(1,0)$) vaut « 1 cm à droite de a » et ($(a)!0.5!(b)$) « le milieu de a et b ».
  • shapes — des formes de nœud au-delà du cercle et du rectangle (losanges, étoiles, bulles), réparties en sous-parties comme shapes.geometric.
  • decorations — décorer un chemin en vague, zigzag, accolade et davantage (decorations.pathmorphing et consorts).
  • patterns — remplir avec un motif : hachures, lignes ou points.
  • fit — construire automatiquement une boîte qui enferme exactement plusieurs nœuds ; commode pour grouper des parties d’une figure.
  • backgrounds — tracer un cadre ou un fond sur un calque situé derrière l’image, sans jamais heurter le premier plan.
  • matrix — aligner des nœuds en grille (en matrice). graphdrawing fait au contraire la mise en page automatique, mais exige LuaTeX.

arrows.meta associé à positioning suffit déjà pour un organigramme utilisable. L’astuce consiste à ne donner de coordonnée qu’au premier nœud et à placer les autres par relation : « à droite de celui-ci », « en dessous de celui-là ». Si un nœud s’ajoute plus tard, aucune coordonnée n’est à renuméroter.

document.tex
\usepackage{tikz}
\usetikzlibrary{arrows.meta, positioning}

\begin{tikzpicture}[node distance=1cm, every node/.style={draw, rounded corners}]
  \node (start)                  {Start};
  \node (proc)  [right=of start] {Process};
  \node (end)   [right=of proc]  {End};
  \draw[-{Stealth}] (start) -- (proc);
  \draw[-{Stealth}] (proc)  -- (end);
\end{tikzpicture}

Le résultat est un organigramme de gauche à droite : trois boîtes à coins arrondis portant « Start », « Process » et « End », espacées de 1 cm et reliées dans l’ordre par des pointes Stealth nettes. every node/.style fixe d’un coup l’aspect des trois, et node distance=1cm fixe l’écart qu’utilise right=of. Placer ces deux réglages dans les options de l’image, c’est précisément ce qui rend une figure facile à retoucher ensuite.

Pourquoi la compilation de TikZ est lente, et quand recourir à external ou standalone

La lenteur vient de ce que TeX effectue lui-même les calculs, en macros. Additionner des coordonnées, convertir un angle en point, subdiviser une courbe de Bézier : tout cela tourne à l’intérieur de la passe de composition, et non dans un moteur externe. Rien n’est mis en cache, si bien que chaque \draw est recalculé de zéro à chaque compilation. Avec quelques figures, cela ne se remarque pas ; accumulez des grid denses, des tracés à nombreux points et des images comptant des dizaines de nœuds, et l’attente devient sensible. Le manuel le reconnaît dès ses premières pages et range les longues recompilations après de petites retouches parmi les coûts de l’approche TeX.

Le remède classique est la bibliothèque external. Après \usetikzlibrary{external} et la déclaration \tikzexternalize, chaque tikzpicture est écrit une seule fois dans son propre PDF, puis mis en cache. Les noms sont attribués automatiquement : depuis main.tex, on obtient main-figure0, main-figure1, et ainsi de suite. Aux exécutions suivantes, si le source d’une image n’a pas bougé, le calcul est entièrement sauté et le PDF déjà produit est simplement inclus ; toute modification déclenche la reconstruction. Le mode par défaut mode=convert with system call fait que LaTeX s’appelle lui-même pour fabriquer les images, ce qui impose de compiler avec -shell-escape (pdflatex -shell-escape main). Sans cette option, aucune figure n’est produite : c’est le premier point à vérifier.

main.tex
\usepackage{tikz}
\usetikzlibrary{external}
\tikzexternalize          % caches each picture as main-figure0.pdf, main-figure1.pdf, ...

% compile with:  pdflatex -shell-escape main

L’autre voie est la classe standalone. La figure passe dans un .tex à part et se compile seule avec \documentclass[tikz]{standalone} ; l’option tikz charge le package tikz et produit un PDF d’une page rogné à la taille naturelle de l’image (plusieurs tikzpicture donnent plusieurs pages). Le document principal se contente ensuite de \includegraphics{figure}. La règle empirique : external tant que la figure grandit avec le texte — le code reste auprès de la prose et l’on ne gagne que le cache. standalone dès que la figure est partagée avec d’autres documents ou des diapositives, ou qu’elle va être reprise des dizaines de fois — un petit document qui compile en quelques secondes reste l’endroit le plus rapide pour expérimenter.

figure.tex
% compiles on its own to a one-page PDF, cropped to the picture
\documentclass[tikz]{standalone}
\usetikzlibrary{arrows.meta}
\begin{document}
\begin{tikzpicture}
  \draw[-{Stealth}, thick] (0,0) -- (2,1);
\end{tikzpicture}
\end{document}

% then in the main document:  \includegraphics{figure}