# Ploting a function using mplib in lualatex

This question asked about ways to generate a nice function graph in LaTeX. There are several great answers, however, most of the either rely on an external program to generate the plot, or, if they use TeX to calculate the coordinates of the graph, are restricted to only fairly simple functions.

I wanted to come up with an answer using luatex, using lua to do the calculations, and mplib to actually draw the graph. However, I could not figure out how to use lua and mplib together. Generating the graph in lua is not hard, and I can pass the generated path to tikz for rendering:

\documentclass{article}
\usepackage{luacode}
\usepackage{tikz}

\begin{luacode}
require("math")
sin=math.sin;cos=math.cos;pi=math.pi
point = function (f,x) return "("..x..","..f(x)..")" end;
path = function (f,a,b,n)
local step = (b-a)/n; local s = "";
for x = a, b - step/2, step do s = s .. point(f,x) .. "--" end;
s = s ..  point(f,b);
return s
end;
\end{luacode}

\def\plot#1#2#3#4{%
\directlua{
do local f = function (x) return #1 end;
tex.print(path(f,#2,#3,#4)) end;
}%
}

\begin{document}
\begin{tikzpicture}
\draw[thin,->] (-.2,0)--(6.5,0)node[right]{$x$};
\draw[thin,->] (0,-2.2)--(0,2.2)node[above]{$y$};
\draw[thick] \plot{sin(2*x)+cos(x)}{0}{2*pi}{40}node[above]{$y=\sin(2x)+\cos(x)$};
\end{tikzpicture}
\end{document}


I would like to know what would be a best way to generate a drawing using lua and render it using mplib.

• Note that there is ongoing work to provide LUA as backend math engine within tikz and pgfplots. Once this is done, you get the same effect. Note, however, that even now you can express very complicated plots within pgfplots. The limitations are not expressed by means of the math engine (due to the fpu library). Current limitations are the number of sample points (and runtime- and memory limits). – Christian Feuersänger Oct 24 '11 at 17:42

This is how I would do it in ConTeXt. I don't know if similar functionality is available in a LaTeX package. Basically, I do the calculations in lua and write the result inside a \startMPcode .. \stopMPcode environment.

\startluacode
require("math")
local sin = math.sin
local cos = math.cos
local pi  = math.pi

local function scale(x)
return x .. " * 1cm"
end
local function pair(x,y)
return "(" .. scale(x) .. "," .. scale(y) .. ")"
end

local function point(f,x)
return pair(x, f(x))
end

userdata = userdata or {}

function userdata.draw_function(f, a, b, n)
local step = (b-a)/n
local path = {}
local i = 0
for x = a, b, step do
path[i] = point(f,x)
i = i + 1
end
path = table.concat(path, "--")

context("draw " .. path .. " withpen pencircle scaled 2bp;")
end

function userdata.draw_x_axis(from, to)
context(" drawarrow " .. pair(from,0) .. " -- " .. pair(to,0) .. "; ")
context(" label.rt ( btex $x$ etex , " .. pair(to,0) .. ") ;")
end

function userdata.draw_y_axis(from, to)
context(" drawarrow " .. pair(0, from) .. " -- " .. pair(0,to) .. "; ")
context(" label.top ( btex $y$ etex , " .. pair (0,to) .. ") ;")
end

function userdata.plot()

userdata.draw_x_axis(-2, 6.5)
userdata.draw_y_axis(-2.2, 2.2)

local f = function (x)
return sin(2*x) + cos(x)
end

userdata.draw_function(f, 0, 2*pi, 40)
end

\stopluacode

\starttext
\ctxlua
{context.startMPcode() ;
userdata.plot() ;
context.stopMPcode() ; }
\stoptext


However, this assumes that everything that is written inside teh userdata.plot() function is MP code. If you also want that function to print stuff to the TeX stream, then you can store all the MP code inside a \startMPdrawing and \stopMPdrawing environment and retrieve them using \getMPdrawing.

\startluacode
require("math")
local sin = math.sin
local cos = math.cos
local pi  = math.pi

local function scale(x)
return x .. " * 1cm"
end
local function pair(x,y)
return "(" .. scale(x) .. "," .. scale(y) .. ")"
end

local function point(f,x)
return pair(x, f(x))
end

userdata = userdata or {}

function userdata.draw_function(f, a, b, n)
local step = (b-a)/n
local path = {}
local i = 0
for x = a, b, step do
path[i] = point(f,x)
i = i + 1
end
path = table.concat(path, "--")

context.startMPdrawing()
context("draw " .. path .. " withpen pencircle scaled 2bp;")
context.stopMPdrawing()
end

function userdata.draw_x_axis(from, to)
context.startMPdrawing()
context(" drawarrow " .. pair(from,0) .. " -- " .. pair(to,0) .. "; ")
context(" label.rt ( btex $x$ etex , " .. pair(to,0) .. ") ;")
context.stopMPdrawing()
end

function userdata.draw_y_axis(from, to)
context.startMPdrawing()
context(" drawarrow " .. pair(0, from) .. " -- " .. pair(0,to) .. "; ")
context(" label.top ( btex $y$ etex , " .. pair (0,to) .. ") ;")
context.stopMPdrawing()
end

function userdata.plot()
userdata.draw_x_axis(-2, 6.5)
userdata.draw_y_axis(-2.2, 2.2)

local f = function (x)
return sin(2*x) + cos(x)
end

userdata.draw_function(f, 0, 2*pi, 40)
end

\stopluacode

\starttext
\ctxlua
{ context.resetMPdrawing() ;
userdata.plot() ;
context.MPdrawingdonetrue() ;
context.getMPdrawing() ; }
\stoptext


I combined Aditya's ConTeXt solution with my original code and modified it for LaTeX. It mostly works, except I seem to have trouble with using btex ... etex in labels. If I replace the two lines with mpost labels with their commented version in the code below, I get

! LuaTeX error ...share/texmf-texlive/tex/luatex/luamplib/luamplib.lua:74: invalid option '%C' to 'format'
stack traceback:
[C]: in function 'format'
...share/texmf-texlive/tex/luatex/luamplib/luamplib.lua:74: in function 'log'
...share/texmf-texlive/tex/luatex/luamplib/luamplib.lua:143: in function 'report'
...share/texmf-texlive/tex/luatex/luamplib/luamplib.lua:173: in function 'process'
...share/texmf-texlive/tex/luatex/luamplib/luamplib.lua:101: in function 'processlines'
<\directlua >:1: in main chunk.

l.49 \plot{sin(2*x)+cos(x)}{0}{2*pi}{40}{-2}{2}


The following works:

\documentclass{article}
\usepackage{luatextra}
\usepackage{luamplib}

\begin{luacode}
scale = function (x) return x .. " * 1cm" end;
pair = function (x,y) return "("..scale(x)..","..scale(y)..")" end;
point = function (f,x) return pair (x,f(x)) end;
path = function (f,a,b,n)
local step = (b-a)/n; local s = "";
for x = a, b - step/2, step do s = s .. point(f,x) .. "--" end;
s = s ..  point(f,b);
return s
end;
draw_x_axis = function (from, to)
tex.print(" drawarrow " .. pair(from,0) .. " -- " .. pair(to,0) .. "; ")
tex.print([[ label.rt ( "x" , ]] .. pair(to,0) .. ") ;")
% The following does not work, though:
%tex.print(" label.rt ( btex $x$ etex, " .. pair(to,0) .. ") ;")
end

draw_y_axis = function (from, to)
tex.print(" drawarrow " .. pair(0, from) .. " -- " .. pair(0,to) .. "; ")
tex.print([[ label.top ( "y" , ]] .. pair (0,to) .. ") ;")
% The following does not work, though:
%tex.print(" label.top ( btex $y$ etex , " .. pair (0,to) .. ") ;")
end
beginMPcode = function ()
tex.print("\string\\begin{mplibcode}")
tex.print("beginfig(0)")
end;
endMPcode = function ()
tex.print("endfig;")
tex.print("\string\\end{mplibcode}")
end;
\end{luacode}

\def\plot#1#2#3#4#5#6{%
\directlua{
require("math")
local sin=math.sin;local cos=math.cos;local pi=math.pi % ... and so on
local f = function (x) return #1 end;

beginMPcode()
draw_x_axis(#2-.2,#3+.2)
draw_y_axis(#5-.2,#6+.2)
tex.print("draw " .. path(f,#2,#3,#4) .. " withpen pencircle scaled 2pt;" )
endMPcode()
}%
}

\begin{document}
\plot{sin(2*x)+cos(x)}{0}{2*pi}{40}{-2}{2}
\end{document}


Any ideas on how to make this better, and how to fix the problem with labels?

• IIRC, mplib does not handle labels. ConTeXt does a two pass run over MP environments. In the first pass, it captures all btex ... etex stuff and typesets them into boxes. In the second run, it passes the dimension of these boxes to mplib, and finally places the boxes at the right location. A lot of this is built upon how text inside metapost was handled in pdftex. – Aditya Oct 5 '10 at 4:09
• Does luamplib work with a simple example that uses labels? For example, draw (btex Hello world etex);  – Aditya Oct 5 '10 at 4:10
• That example fails with ! You can only use btex' or verbatimtex' in a file. but if I change it to label (btex Hello World etex, (0,0));, it will fail with LuaTeX error ...share/texmf-texlive/tex/luatex/luamplib/luamplib.lua:74: invalid option '%C' to 'format' just like the original example. – Jan Hlavacek Oct 5 '10 at 16:42