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?

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

1 Answer 1

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

13
  • Thanks for excellent help.
    – user61681
    Jan 16, 2020 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, 2020 at 7:04
  • 1
    @user61681 Nice challange. I updated my answer but without further explanation. Feb 13, 2020 at 9:47
  • Thank you for the wonderful solution. Sorry, but I didn't mean to challenge anything. Thanks again.
    – user61681
    Feb 13, 2020 at 13:23
  • For some reason it is not supporting log(x).
    – user61681
    Feb 17, 2020 at 16:05

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .