2

Is it possible to draw graph of standard functions using mpgraph (probably using lua, mplib)? For example something like gdraw(sin(x)+cos(x)), gdraw(2x+3) and in general gdraw(f(x)) where f(x) is finite combination of standard functions?

  • 2
    You should check out the Metafun XL format (from ConTeXt). It has graph drawing functions. – TeXnician Jan 15 at 17:09
4

This questions seems to be a follow-up of Using Lua functions with mplib and mpgraph

You can simply construct the function that you want to plot from the macro parameter.

\documentclass{article}
\usepackage{luamplib}
\directlua{userdata = userdata or {}}
\begin{document}
\def\plot#1{%
  \directlua{
  function userdata.f(x)
      local _ENV = math
      return (#1)
  end
  luamplib.process_mplibcode([[
    vardef f primary x =
        runscript("mp.print(userdata.f(" & decimal x & "))")
    enddef ;
    input graph ;
    beginfig(0)
    draw begingraph(5cm,3cm)
       gdraw (0,f(0)) for x = 1 upto 1000: .. (x/100, f(x/100)) endfor ;
    endgraph ;
    endfig ;
  ]])}%
}

\plot{sin(x) + cos(x)}

\plot{sin(2*pi*x) * exp(-x/2)}

\end{document}

enter image description here

With a tiny bit of extra work you can also get keys and value to configure the plot.

\documentclass{article}
\usepackage{luamplib}
\usepackage{luacode}
\begin{luacode*}
userdata = userdata or {}

userdata.mplibcode = [[
vardef f primary x =
    runscript("mp.print(userdata.f(" & decimal x & "))")
enddef ;
numeric xmin, xmax, samples, width ,height ;
xmin := runscript("mp.print(userdata.xmin)");
xmax := runscript("mp.print(userdata.xmax)");
samples := runscript("mp.print(userdata.samples)");
width := runscript("mp.print(userdata.width)");
height := runscript("mp.print(userdata.height)");
input graph ;
beginfig(0)
draw begingraph(width,height)
    gdraw (xmin,f(xmin)) for i = 1 upto samples:
        -- ((xmax - xmin) / samples * i + xmin, f((xmax - xmin) / samples * i + xmin))
    endfor ;
endgraph ;
endfig ;
]]

function userdata.graph(settings)
    settings = settings or {}
    userdata.xmin = tostring(settings.xmin or "0")
    userdata.xmax = tostring(settings.xmax or "1")
    userdata.samples = tostring(settings.samples or "100")
    userdata.width = tostring(settings.width or "5cm")
    userdata.height = tostring(settings.height or "3cm")
    luamplib.process_mplibcode(userdata.mplibcode)
end
\end{luacode*}

\newcommand\plot[2][]{%
  \directlua{
    local userdata = userdata
    local _ENV = math
    function userdata.f(x)
        return (#2)
    end
    userdata.graph({#1})
  }%
}

\begin{document}

\plot[xmin=-pi,xmax=pi,width="7cm"]{sin(x) + cos(x)}
%                            ^^^^^
%          need to be quoted because this is parsed by Lua

\plot[xmin=0,xmax=10,samples=1000]{sin(2*pi*x) * exp(-x/2)}

\end{document}

enter image description here


\documentclass{article}
\usepackage{pgffor}
\usepackage{luamplib}
\usepackage{luacode}
\begin{luacode*}

userdata = userdata or {}

userdata.mplibcode = [[
color clr ;
numeric Nf, rmin, rmax, xmin, xmax, ymin, ymax, samples, width ,height ;
Nf := runscript("mp.print(#userdata.f)");
rmin := runscript("mp.print(userdata.range[1])");
rmax := runscript("mp.print(userdata.range[2])");
xmin := runscript("mp.print(userdata.xmin)");
xmax := runscript("mp.print(userdata.xmax)");
ymin := runscript("mp.print(userdata.ymin)");
ymax := runscript("mp.print(userdata.ymax)");
samples := runscript("mp.print(userdata.samples)");
width := runscript("mp.print(userdata.width)");
height := runscript("mp.print(userdata.height)");

input graph ;

beginfig(0)
draw begingraph(width,height)
    setrange(xmin, ymin, xmax, ymax) ;
    for n = 1 upto Nf:
        vardef f primary x =
            runscript("mp.print(userdata.f[" & decimal n & "](" & decimal x & "))")
        enddef ;

        clr := runscript("mp.print(userdata.color[" & decimal n & "] or 'black')") ;

        drawoptions(runscript("mp.print(userdata.style[" & decimal n & "] or '')")) ;
        gdraw (rmin,f(rmin)) for i = 1 upto samples:
            -- ((rmax - rmin) / samples * i + rmin, f((rmax - rmin) / samples * i + rmin))
        endfor withcolor clr ;
        drawoptions() ;
    endfor ;
endgraph ;
endfig ;
]]

function userdata.graph(settings)
    settings = settings or {}
    settings.range = settings.range or {}
    userdata.range = {
        tostring(settings.range[1] or "0"),
        tostring(settings.range[2] or "1")
    }
    userdata.xmin = tostring(settings.xmin or "0")
    userdata.xmax = tostring(settings.xmax or "1")
    userdata.ymin = tostring(settings.ymin or "whatever")
    userdata.ymax = tostring(settings.ymax or "whatever")
    userdata.samples = tostring(settings.samples or "100")
    userdata.width = tostring(settings.width or "5cm")
    userdata.height = tostring(settings.height or "3cm")
    userdata.color = settings.color or {}
    userdata.style = settings.style or {}
    luamplib.process_mplibcode(userdata.mplibcode)
end

\end{luacode*}

\newcommand\plot[2][]{%
  \directlua{userdata.f = {}}%
  \foreach \f [count=\i] in {#2} {%
    \directlua{
      local userdata = userdata
      local _ENV = math
      userdata.f[\i] = function(x)
          return (\f)
      end
    }%
  }%
  \directlua{
    local userdata = userdata
    local _ENV = math
    userdata.graph({#1})
  }%
}

\begin{document}

\plot[
    range={ 0, 10 },
    xmin=-1, xmax=11,
    ymin=-sqrt(2), ymax=sqrt(2),
    samples=1000,
    color={ "red", "blue", "(0,.8,.1)" },
    style={ "", "dashed evenly", "dashed withdots" }
]{sin(2*pi*x) * exp(-x/2), exp(-x/2), sin(2*pi*x)}

\end{document}

enter image description here


\documentclass{article}
\usepackage{luamplib}
\usepackage{luacode}
\begin{luacode*}

userdata = userdata or {}

userdata.mplibcode = [[
numeric Nf, xmin, xmax, ymin, ymax, width ,height ;
Nf := runscript("mp.print(#userdata.functions)");
xmin := runscript("mp.print(userdata.axis.xmin)");
xmax := runscript("mp.print(userdata.axis.xmax)");
ymin := runscript("mp.print(userdata.axis.ymin)");
ymax := runscript("mp.print(userdata.axis.ymax)");
width := runscript("mp.print(userdata.axis.width)");
height := runscript("mp.print(userdata.axis.height)");

input graph ;

beginfig(0)
draw begingraph(width,height)
    setrange(xmin, ymin, xmax, ymax) ;
    for n = 1 upto Nf:
        vardef f primary x =
            runscript("mp.print(userdata.functions[" & decimal n & "].f(" & decimal x & "))")
        enddef ;
        numeric rmin, rmax, samples ;
        rmin := runscript("mp.print(userdata.functions[" & decimal n & "].range[1] or userdata.axis.range[1])");
        rmax := runscript("mp.print(userdata.functions[" & decimal n & "].range[2] or userdata.axis.range[2])");
        samples := runscript("mp.print(userdata.functions[" & decimal n & "].samples or userdata.axis.samples)");
        color clr ;
        clr := runscript("mp.print(userdata.functions[" & decimal n & "].color or userdata.axis.color)") ;

        drawoptions(runscript("mp.print(userdata.functions[" & decimal n & "].style or userdata.axis.style)")) ;
        gdraw (rmin,f(rmin)) for i = 1 upto samples:
            -- ((rmax - rmin) / samples * i + rmin, f((rmax - rmin) / samples * i + rmin))
        endfor withcolor clr ;
        drawoptions() ;
    endfor ;
endgraph ;
endfig ;
]]

function userdata.draw_graph()
    local axis = userdata.axis
    axis.range = {
        tostring(axis.range[1] or "0"),
        tostring(axis.range[2] or "1")
    }
    axis.xmin = tostring(axis.xmin or "whatever")
    axis.xmax = tostring(axis.xmax or "whatever")
    axis.ymin = tostring(axis.ymin or "whatever")
    axis.ymax = tostring(axis.ymax or "whatever")
    axis.samples = tostring(axis.samples or "100")
    axis.width = tostring(axis.width or "5cm")
    axis.height = tostring(axis.height or "3cm")
    axis.color = tostring(axis.color or "black")
    axis.style = tostring(axis.style or "")
    luamplib.process_mplibcode(userdata.mplibcode)
end

\end{luacode*}

\newenvironment{luamplibplots}[1][]%
  {\directlua{
       local userdata = userdata
       local _ENV = math
       userdata.functions = {}
       userdata.axis = {#1}
   }}%
  {\directlua{userdata.draw_graph()}}

\newcommand\addplot[2][]{%
  \directlua{
    local userdata = userdata
    local tableinsert = table.insert
    local _ENV = math
    tableinsert(userdata.functions, {
        f = function(x) return (#2) end,
        range = {},
        #1
    })
  }%
}

\begin{document}

\begin{luamplibplots}
    [range={0,10},
     xmin=-1,
     xmax=11,
     ymin=-sqrt(2),
     ymax=sqrt(2),
     samples=1000]

    \addplot[color="red", samples=50]{sin(2*pi*x) * exp(-x/2)}
    \addplot[color="blue", style="dashed evenly"]{exp(-x/2)}
    \addplot[color="(0,.8,.1)", style="scaled 2pt dashed withdots"]{sin(2*pi*x)}
    \addplot[range={exp(-2),exp(2)}]{log(x)}

\end{luamplibplots}

\end{document}

enter image description here

|improve this answer|||||
  • Thanks for excellent help. – user61681 Jan 16 at 1:53
  • How can the option for ymin, ymax and color of graphs can be added? Also is it possible to draw graphs of multiple functions in single figure? – user61681 Feb 13 at 7:04
  • 1
    @user61681 Nice challange. I updated my answer but without further explanation. – Henri Menke Feb 13 at 9:47
  • Thank you for the wonderful solution. Sorry, but I didn't mean to challenge anything. Thanks again. – user61681 Feb 13 at 13:23
  • For some reason it is not supporting log(x). – user61681 Feb 17 at 16:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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