# Tikz externalize + macro's ? Or how would you do it?

I'm currently building courses for some of my college teachers. The aim is to make their Word/MS paint generated study material into high grade Latex/Tikz material.

I'm aiming for high consistency in figures (expecially plots) and I've therefore written a bunch of macro's that generate tikz pictures for me from data I get form Matlab using the matlab2tikz.m code (which was written at my college!).

What I've done is write a bunch of "set" macro's that set a current variable to be something e.g. \figxlabel{$x$-axis} (see my code below) would set another macro \thefigxlabelto contain $x$-axis. All of this info is then used in the next \matlabfig{data.tikz}, after which it is reset to neutral values. Since everything is in macro's I don't need to remember what the special settings for a plot were, and even better I can change the \matlabfig macro to change all pictures at once!

But, then I discovered the Tikz externalize library which, since my documents are getting large (I split them up but still...), would come in very handy. Yet, this library doesn't support macro usage in tikzpictures.. What would you do to combine the best of two worlds?

I was thinking to use the macro's to set global tikz properties that apply to all figures (I don't know if i can do \tikzset{every xlabel={$x$-axis}}) and then change this between pictures, so it would only apply to the next one. This way there wouldn't be any macro's inside the tikz environment. I'd have to use a non-macro \begin and \end structure then I'm guessing.

All my written macro's are given in the next piece of code (these should work):

\def\figurewidth{7.5cm}
\def\figureheight{4cm}

\newcommand{\thefigtitle}[0]{\null}%title
\newcommand{\thefigtith}[0]{0cm}
\newcommand{\figtitle}[1]{%
\renewcommand{\thefigtitle}{#1}%
\renewcommand{\thefigtith}{0.35cm}%
}

\def\figysep{1}

\newcommand{\thefigxlabel}[0]{\null}%xlabel
\newcommand{\thefigxh}[0]{0}

\newcommand{\figxlabel}[1]{%
\renewcommand{\thefigxlabel}{#1}%
\renewcommand{\thefigxh}{-0.35}%
}

\newcommand{\thefigylabel}{}%ylabel

\newcommand{\thefiglabel}{ }%label
\newcommand{\figlabel}[1]{%
\renewcommand{\thefiglabel}{\label{#1}}%
}

\newcommand{\thefigxrange}[0]{ }%x-range
\newcommand{\figxrange}[2]{
\renewcommand{\thefigxrange}{xmin=#1,xmax=#2,}
}

\newcommand{\thefigyrange}[0]{ }%y-range
\newcommand{\figyrange}[2]{%
\renewcommand{\thefigyrange}{ymin=#1,ymax=#2,}
}

\newcommand{\thefiglegend}{ }%legend
\newcommand{\figlegend}[2]{%
\renewcommand{\thefiglegend}{legend entries={#1},legend pos={#2}}%
}

\newcommand{\thefigcaption}{\caption{}}%caption

\newcommand{\thefigextra}{ }%extra options

\newcommand{\thefigpos}{htp}

\newcommand{\matlabfig}[1]{%figuur zelf
\begin{figure}[\thefigpos]
\centering
\begin{tikzpicture}[]
\draw[use as bounding box,draw=none](0,0+\thefigxh)rectangle(\figurewidth,\figureheight+\thefigtith);
\begin{axis}[%
view={0}{90},
scale only axis,
width=\figurewidth,
height=\figureheight,
y tick label style={font={\tiny}},
x tick label style={font={\tiny}},
title={\textbf{\textsc{\thefigtitle}}},
xlabel={\textbf{\thefigxlabel}},
ylabel={\textbf{\thefigylabel}},
\thefigxrange
\thefigyrange
axis on top,\thefiglegend,
\thefigextra,
minor tick num=1
]
\input{#1}
\end{axis}
\end{tikzpicture}
\thefigcaption\thefiglabel
\end{figure}
%alles resetten
\figreset}

\newcommand{\figreset}[0]{
\renewcommand{\thefigtitle}{ }%
\renewcommand{\thefigxlabel}{ }%
\renewcommand{\thefigxh}{0}%
\renewcommand{\thefigtith}{0cm}%
\renewcommand{\thefigylabel}{}%
\renewcommand{\thefigcaption}{\caption{}}%
\renewcommand{\thefiglabel}{}%
\renewcommand{\thefiglegend}{}%
\renewcommand{\thefigxrange}{}%
\renewcommand{\thefigyrange}{}%
\renewcommand{\thefigextra}{ }%
\renewcommand{\thefigpos}{htp}%
}

EDIT: added a working minimal example

contents of example.tex:

\documentclass{article}

\usepackage{tikz}
\usepackage{pgfplots}

\def\figurewidth{7.5cm}
\def\figureheight{4cm}

\newcommand{\thefigtitle}[0]{\null}%title
\newcommand{\thefigtith}[0]{0cm}
\newcommand{\figtitle}[1]{%
\renewcommand{\thefigtitle}{#1}%
\renewcommand{\thefigtith}{0.45cm}%
}

\def\figysep{1}

\newcommand{\thefigxlabel}[0]{\null}%xlabel
\newcommand{\thefigxh}[0]{0}

\newcommand{\figxlabel}[1]{%
\renewcommand{\thefigxlabel}{#1}%
\renewcommand{\thefigxh}{-0.35}%
}

\newcommand{\thefigylabel}{}%ylabel

\newcommand{\thefiglabel}{ }%label
\newcommand{\figlabel}[1]{%
\renewcommand{\thefiglabel}{\label{#1}}%
}

\newcommand{\thefigxrange}[0]{ }%x-range
\newcommand{\figxrange}[2]{
\renewcommand{\thefigxrange}{xmin=#1,xmax=#2,}
}

\newcommand{\thefigyrange}[0]{ }%y-range
\newcommand{\figyrange}[2]{%
\renewcommand{\thefigyrange}{ymin=#1,ymax=#2,}
}

\newcommand{\thefiglegend}{ }%legend
\newcommand{\figlegend}[2]{%
\renewcommand{\thefiglegend}{legend entries={#1},legend pos={#2}}%
}

\newcommand{\thefigcaption}{\caption{}}%caption

\newcommand{\thefigextra}{ }%extra options

\newcommand{\thefigpos}{htp}

\newcommand{\matlabfig}[1]{%figuur zelf
\begin{figure}[\thefigpos]
\centering
\begin{tikzpicture}[]
\draw[use as bounding box,draw=none](0,0+\thefigxh)rectangle(\figurewidth,\figureheight+\thefigtith);
\begin{axis}[%
view={0}{90},
scale only axis,
width=\figurewidth,
height=\figureheight,
y tick label style={font={\tiny}},
x tick label style={font={\tiny}},
title={\textbf{\textsc{\thefigtitle}}},
xlabel={\textbf{\thefigxlabel}},
ylabel={\textbf{\thefigylabel}},
\thefigxrange
\thefigyrange
axis on top,\thefiglegend,
\thefigextra,
minor tick num=1
]
\input{#1}
\end{axis}
\end{tikzpicture}
\thefigcaption\thefiglabel
\end{figure}
%alles resetten
\figreset}

\newcommand{\figreset}[0]{
\renewcommand{\thefigtitle}{ }%
\renewcommand{\thefigxlabel}{ }%
\renewcommand{\thefigxh}{0}%
\renewcommand{\thefigtith}{0cm}%
\renewcommand{\thefigylabel}{}%
\renewcommand{\thefigcaption}{\caption{}}%
\renewcommand{\thefiglabel}{}%
\renewcommand{\thefiglegend}{}%
\renewcommand{\thefigxrange}{}%
\renewcommand{\thefigyrange}{}%
\renewcommand{\thefigextra}{ }%
\renewcommand{\thefigpos}{htp}%
}

\pgfplotsset{
every tick/.style={thin,color=black},
every axis legend/.append style={nodes={right}},
every axis legend/.append style={font=\tiny},
}

\tikzstyle{every node}=[font=\scriptsize]

\begin{document}

%%
\figcaption{an example fig}
\figlabel{fig:example}
\figtitle{example}
\figxlabel{$x$ axis}
\figylabel{$y$ axis}
\figlegend{data.tikz}{north west}
\figxrange{0}{6}
\figyrange{0}{5}
\figextra{}
\matlabfig{data.tikz}
%%

%another fig, but as you can see all was resetted
\matlabfig{data.tikz}

\end{document}

contents of data.tikz:

color=black,
solid
]
coordinates{(0,0) (3,3) (4,5) (5,4)};

## How to to it with environments?

The following example contains the most basic newenvironment to create a tikzpicture. What I'm actually going for has a bit more options, but the problem remains the same.

Here's a small piece of sample code:

\documentclass[a4paper]{article}

\usepackage{tikz}
\usetikzlibrary{external}

\newenvironment{mytikz}{%
\begin{figure}[htp]
\centering
\begin{tikzpicture}}
{\end{tikzpicture}
\caption{}
\end{figure}}

\tikzexternalize

\begin{document}

\begin{mytikz}
\draw(0,0) circle (1cm);
\end{mytikz}

\end{document}
-

The MWE works just fine using the externalisation library. Converting the syntax to use pgfkeys actually solves the scoping problem since keys are local to the current scope. Thus you can pass the keys and values as an option to the matlabfig command and they will be used only internally to that command.

I'm only a newcomer to the world of of pgfkeys so there may be better ways of implementing this, but here's a conversion of your macros into pgfkeys language. I only changed one thing in the input syntax: the ranges are specified as x range=0:6 instead of x range={0}{6}.

When putting this in to an environment, the problem is that the externalisation library looks for \end{tikzpicture} without doing any expansion. In an ordinary environment, this doesn't work because the \end{tikzpicture} is hidden behind a \end{myenvironment}. So we have to have a method whereby the \end{myenvironment} is expanded at the same time as the \begin{tikzpicture}. Fortunately such a method exists and is to use the environ package. This makes environments behave a little like commands.

\documentclass{article}
\usepackage{environ}
\usepackage{tikz}
\usetikzlibrary{external}
\tikzexternalize
\tikzset{external/force remake=true}
\usepackage{pgfplots}

\def\figurewidth{7.5cm}
\def\figureheight{4cm}

\pgfkeys{/matlab plot/.cd,
title/.default={\null},
title height/.default={0cm},
x label/.default={\null},
x label height/.default={0},
x range/.code args={#1:#2}{\pgfkeyssetvalue{/matlab plot/x range}{xmin=#1,xmax=#2}},
y range/.code args={#1:#2}{\pgfkeyssetvalue{/matlab plot/y range}{ymin=#1,ymax=#2}},
legend/.code args={#1#2}{\pgfkeyssetvalue{/matlab plot/legend}{legend entries=#1,legend pos=#2}},
title/.code={%
\pgfkeyssetvalue{/matlab plot/title}{#1}%
\pgfkeyssetvalue{/matlab plot/title height}{0.45cm}},
x label/.code={%
\pgfkeyssetvalue{/matlab plot/x label}{#1}%
\pgfkeyssetvalue{/matlab plot/x label height}{-0.35}},
caption/.code={%
\pgfkeyssetvalue{/matlab plot/caption}{\caption{#1}}},
label/.code={\pgfkeyssetvalue{/matlab plot/caption}{\label{#1}}}%
}

% Initialise
\pgfkeyssetvalue{/matlab plot/caption}{}
\pgfkeyssetvalue{/matlab plot/label}{}
\pgfkeyssetvalue{/matlab plot/x label}{}
\pgfkeyssetvalue{/matlab plot/y label}{}
\pgfkeyssetvalue{/matlab plot/extra}{}
\pgfkeyssetvalue{/matlab plot/legend}{}
\pgfkeyssetvalue{/matlab plot/x range}{}
\pgfkeyssetvalue{/matlab plot/x range}{}
\pgfkeyssetvalue{/matlab plot/y range}{}
\pgfkeyssetvalue{/matlab plot/y range}{}
\pgfkeyssetvalue{/matlab plot/pos}{}

\def\figysep{1}

\newcommand{\matlabfig}[2][]{%figuur zelf
\begingroup
\pgfkeys{/matlab plot/.cd,#1}
\begin{figure}[\pgfkeysvalueof{/matlab plot/pos}]
\centering
\begin{tikzpicture}[]
%            \draw[use as bounding box,draw=none](0,0+\pgfkeysvalueof{x label height})rectangle(\figurewidth,\figureheight+\pgfkeysvalueof{title height});
\begin{axis}[%
view={0}{90},
scale only axis,
width=\figurewidth,
height=\figureheight,
y tick label style={font={\tiny}},
x tick label style={font={\tiny}},
title={\textbf{\textsc{\pgfkeysvalueof{/matlab plot/title}}}},
xlabel={\textbf{\pgfkeysvalueof{/matlab plot/x label}}},
ylabel={\textbf{\pgfkeysvalueof{/matlab plot/y label}}},
\pgfkeysvalueof{/matlab plot/x range},
\pgfkeysvalueof{/matlab plot/y range},
axis on top,
\pgfkeysvalueof{/matlab plot/legend},
\pgfkeysvalueof{/matlab plot/extra}
minor tick num=1
]
\input{#2}
\end{axis}
\end{tikzpicture}
\pgfkeysvalueof{/matlab plot/caption}
\pgfkeysvalueof{/matlab plot/label}
\end{figure}
\endgroup}

\NewEnviron{MatlabFig}[1][]{%
\begingroup
\pgfkeys{/matlab plot/.cd,#1}
\begin{figure}[\pgfkeysvalueof{/matlab plot/pos}]
\centering
\begin{tikzpicture}
%            \draw[use as bounding box,draw=none](0,0+\pgfkeysvalueof{x label height})rectangle(\figurewidth,\figureheight+\pgfkeysvalueof{title height});
\begin{axis}[%
view={0}{90},
scale only axis,
width=\figurewidth,
height=\figureheight,
y tick label style={font={\tiny}},
x tick label style={font={\tiny}},
title={\textbf{\textsc{\pgfkeysvalueof{/matlab plot/title}}}},
xlabel={\textbf{\pgfkeysvalueof{/matlab plot/x label}}},
ylabel={\textbf{\pgfkeysvalueof{/matlab plot/y label}}},
\pgfkeysvalueof{/matlab plot/x range},
\pgfkeysvalueof{/matlab plot/y range},
axis on top,
\pgfkeysvalueof{/matlab plot/legend},
\pgfkeysvalueof{/matlab plot/extra}
minor tick num=1
]
\BODY
\end{axis}
\end{tikzpicture}
\pgfkeysvalueof{/matlab plot/caption}
\pgfkeysvalueof{/matlab plot/label}
\end{figure}
\endgroup}

\pgfplotsset{
every tick/.style={thin,color=black},
every axis legend/.append style={nodes={right}},
every axis legend/.append style={font=\tiny},
}

\tikzstyle{every node}=[font=\scriptsize]

\begin{document}

%%
\matlabfig[
caption=an example fig,
label=fig:example,
title=example,
x label=$x$ axis,
y label=$y$ axis,
legend={data.tikz}{north west},
x range=0:6,
y range=0:5,
]{tikzextkeys_data.tikz}

%%

%another fig, but as you can see all was resetted
\matlabfig{tikzextkeys_data.tikz}

\begin{MatlabFig}[
caption=an example fig,
label=fig:example,
title=example,
x label=$x$ axis,
y label=$y$ axis,
legend={data.tikz}{north west},
x range=0:6,
y range=0:5,
]
\input{tikzextkeys_data.tikz}
\end{MatlabFig}

\end{document}

(NB In this code, something is going wrong when I uncomment the bounding box line. It's to do with getting the lengths and values of the keys to interact properly.)

-
I admit I hadn't tried using macro's in a non-macro'ed tikzpicture, because the problem is really how to add all my macro's to one figure every time without forgetting one? Could I set up one pgfkey that includes all these macro's at once? –  romeovs Apr 7 '11 at 8:02
@romeovs: Since the entire picture is in a macro, I don't think that there's any difficulty. TeX expands the macro \matlabfig first, then TikZ notices that it is picture and tries to externalise it. But as TeX has already expanded the \matlabfig, TikZ "sees" the picture just as if it were included in your document as-is. (Could you add a sample data file to your question, otherwise I can't experiment with it.) –  Andrew Stacey Apr 7 '11 at 8:07
@romeovs: To be clear: I think that there are two questions here. 1) would this work with externalisation? To which I think that the answer is "Yes", but you should really just try it and see. 2) how could this be done using pgfkeys? To which the answer is: make sure that they keys are set and used in the same grouping, then there's no need to reset things. To add to my request for sample data, please also add a sample of usage: namely, how you'd normally set all these values. –  Andrew Stacey Apr 7 '11 at 8:10
@romeovs: I can't think of any reason why it wouldn't work. –  Andrew Stacey Apr 10 '11 at 17:37
@romeovs: Can you edit your attempt at putting it in to an environment in to the question? I think I know why that's happening, but seeing your attempt will confirm it and also clarify what you want from such an environment. –  Andrew Stacey Apr 11 '11 at 12:47