[Tex/LaTex] Ploting a function using mplib in lualatex

graphicsluatex

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.

Best Answer

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
Related Question