Playing Around With tikz
If you are a fan of the Jupyter Notebooks as I am, and you want to be able to quickly draw some graphics in your reproducible science notebook or whatever, then you will be interested in the ipython-tikzmagic. If you already know about LaTeX, then maybe this will be very useful in simplifying your workflow and making creating nice graphics without native LaTeX boilerplate for figures. In any case, I recently discovered pgf/tikZ, a portable graphic format for TeX, along with the afore mentioned ipython magics, and I decided to try it.
Before we start
The pre-requisites for following this article are:
-
a proper jupyter installation:
python -m pip install jupyter -
the LaTeX system:
# On ubuntu [sudo] apt install -y texlive-full # On macos (mactex does not install itself under /usr/local, # so I figured out I would symlink all of its binary there # so that I can have LaTeX binaries right away in my $PATH # without modifying the later) brew cask install mactex for bin in $(ls -1 /Library/TeX/texbin/);do ln -s /Library/TeX/texbin/$bin /usr/local/bin/$bin;done # Test the installation pdflatex --version -
pdf2svg to extract svg images generated by the ipython-tikzmagic package:
# On ubuntu [sudo] apt install -y pdf2svg # On macos brew install pdf2svg -
imagemagick for jugling within various image formats and more:
# On ubuntu [sudo] apt install -y imagemagick # On macos brew install imagemagick
Let’s start the fun!
Now that we have all the pre-requisites, we can go on and install the tikzmagic in the kernel we use for our jupyter notebook and start the notebook server:
python -m pip install ipython-tikzmagic
jupyter notebook
Create a new notebook, assigning it the kernel where you just installed ipython-tikzmagic, and load the extension in the first cell of the notebook:
%load_ext tikzmagic
You can see the magic docs by typing the following in a cell
%tikz?
Below are the optional arguments you can pass to a call to the tikzmagic:
-sc or --scale: scaling factor of plots, default=1
-s or --size: pixel size of plots, e.g., -s width,height, default=400,240
-f or --format: plot format (png, svg or jpg), default=png
-e or --encoding: text encoding, default=utf-8
-x or --preamble: LaTeX preamble to insert before tikz figure, default=None
-p or --package: LaTeX packages to load, separated by comma, e.g., -p pgfplots,textcomp, default=None
-l or --library: TikZ libraries to load, separated by comma, e.g., -l matrix,arrows, default=None
-S or --save: save a copy to file, e.g., -S filename, default=None
Let’s draw an beautiful envelope without ever taking the pen up, and save the image as a svg image:
%%tikz -f svg
\draw[thick,rounded corners=8pt]
(0,0) -- (0,2) -- (1,3.95) -- (2,2) -- (2,0) -- (0,2) -- (2,2) -- (0,0) -- (2,0);
And a svg math frame without ticks:
%%tikz -f svg
\draw (-1.5,0) -- (1.5,0);
\draw (0,-1.5) -- (0,1.5);
Now for something more…technical, I named a Bézier curve:
- We first draw 4 points, represented by small
circles. The language says: draw a circle centered at
(0,0)(for the first point), and of size2pt. All these circles are filled with thegraycolor - Then the Bézier curve, controlled the 2 middle points
and extending from the first to the last point. The
language says: draw a Bézier curve, starting at point
(0,0), controlled by points(1,1)and(2,1), and ending at point(2,0)
%%tikz -f svg
\filldraw [gray] (0,0) circle (2pt)
(1,1) circle (2pt)
(2,1) circle (2pt)
(2,0) circle (2pt);
\draw (0,0) .. controls (1,1) and (2,1) .. (2,0);
Now 2 symetric Bézier curves about the y-axis of the previously defined frame:
%%tikz -f svg
\draw (-1.5,0) -- (1.5,0);
\draw (0, -1.5) -- (0,1.5);
\filldraw [gray] (-1,0.555) circle (2pt)
(-0.555,1) circle (2pt)
(0.555,1) circle (2pt)
(1,0.555) circle (2pt);
\draw (-1,0) .. controls (-1,0.555) and (-0.555, 1) .. (0,1)
.. controls (0.555,1) and (1,0.555) .. (1,0);
Next, we draw 1 circle, a concentric ellipse and the same
ellipse rotated by 30 degrees. Note how we can add comments
using % (“cercle” means “circle” in french, “inclinée”
means “skewed” 😉):
%%tikz -f svg
\draw (0,0) circle (10pt); % cercle
\draw (0,0) circle (20pt and 10pt); % ellipse
\draw[rotate=30] (0,0) circle (20pt and 10pt); % ellipse inclinée
Next, a simple circle centered on a frame. Another way of
drawing a circle is showed: specify the center and the
radius expressed with the unit cm for this case.
%%tikz -f svg
\draw (-1.5,0) -- (1.5,0);
\draw (0,-1.5) -- (0,1.5);
\draw (0,0) circle (1cm);
Now we see how to draw a square. We maintain the previously
drawn circle and frame, and draw 2 squares on top. The
language says: draw a rectangle (yes, a square is a
rectangle) along the diagonal (0,0) - (0.5,0.5) (for the
first square):
%%tikz -f svg
\draw (-1.5,0) -- (1.5,0);
\draw (0,-1.5) -- (0,1.5);
\draw (0,0) circle (1cm);
\draw (0,0) rectangle (0.5,0.5);
\draw (-0.5,-0.5) rectangle (-1,-1);
It’s pretty neat. The magic uses symmetry internally to draw
a rectangle. Note that the order of the points defining the
diagonal along which the symmetry is applied does not
matter. You can as well start with (0.5,0.5) and finish at
(0,0), the result will be the same.
Let’s a grid to our frame and draw a circle on it:
%%tikz -f svg
\draw (-1.5,0) -- (1.5,0);
\draw (0,-1.5) -- (0,1.5);
\draw (0,0) circle (1cm);
\draw[step=.5cm] (-1.4,-1.4) grid (1.4,1.4);
If we want to give the impression that the circle and the
frame are drawn on top of the grid, we have to play a little
bit with the properties of the grid. Below we use very thin lines, and a gray color to make the grid look far
away in the background:
%%tikz -f svg
\draw[step=.5cm,gray,very thin] (-1.4,-1.4) grid (1.4,1.4);
\draw (-1.5,0) -- (1.5,0);
\draw (0,-1.5) -- (0,1.5);
\draw (0,0) circle (1cm);
The \clip command is used to zoom in and out. Here is the
same graph as above, with a zoom in:
%%tikz -f svg
\clip (-0.1,-0.2) rectangle (1.1,0.75);
\draw[step=.5cm,gray,very thin] (-1.4,-1.4) grid (1.4,1.4);
\draw (-1.5,0) -- (1.5,0);
\draw (0,-1.5) -- (0,1.5);
\draw (0,0) circle (1cm);
It’s also possible to draw maths function curves, like the
sin:
%%tikz -f svg
\draw[x=1ex,y=1ex] (0,0) sin (1.67,1);
We close this article with a “very complicated” graph, illustrating:
- how to define a color variable for reuse (
definecolor) - how to repeat elements (
foreach) - and everything we learned so far:
%%tikz -l arrows,positioning -f svg
\definecolor{zzttqq}{rgb}{0.6,0.2,0}
\definecolor{qqqqff}{rgb}{0,0,1}
\draw[->,color=black] (-4.3,0) -- (26.16,0);
\foreach \x in {-4,-3,-2,-1,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26}
\draw[shift={(\x,0)},color=black] (0pt,2pt) -- (0pt,-2pt) node[below] {\footnotesize $\x$};
\draw[->,color=black] (0,-12.48) -- (0,6.3);
\foreach \y in {-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,1,2,3,4,5,6}
\draw[shift={(0,\y)},color=black] (2pt,0pt) -- (-2pt,0pt) node[left] {\footnotesize $\y$};
\draw[color=black] (0pt,-10pt) node[right] {\footnotesize $0$};
\clip(-4.3,-12.48) rectangle (26.16,6.3);
\fill[color=zzttqq,fill=zzttqq,fill opacity=0.1] (6.58,5.58) -- (3.14,1.84) -- (11.54,1.88) -- cycle;
\draw [color=zzttqq] (6.58,5.58)-- (3.14,1.84);
\draw [color=zzttqq] (3.14,1.84)-- (11.54,1.88);
\draw [color=zzttqq] (11.54,1.88)-- (6.58,5.58);
\begin{scriptsize}
\fill [color=qqqqff] (6.58,5.58) circle (1.5pt);
\draw[color=qqqqff] (6.74,5.84) node {$A$};
\fill [color=qqqqff] (3.14,1.84) circle (1.5pt);
\draw[color=qqqqff] (3.3,2.1) node {$B$};
\fill [color=qqqqff] (11.54,1.88) circle (1.5pt);
\draw[color=qqqqff] (11.7,2.14) node {$C$};
\fill [color=qqqqff] (4.56,2.62) circle (1.5pt);
\draw[color=qqqqff] (4.72,2.88) node {$D$};
\end{scriptsize}
Now you know you can make inline drawings in your Jupyter notebooks.