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 thegray
color - 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.