Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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.

share|improve this question
1  
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
add comment

2 Answers

up vote 4 down vote accepted

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
share|improve this answer
add comment

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.
\endmplibcode ...adirect {luamplib.processlines()}

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?

share|improve this answer
    
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
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.