在 lualatex 中使用 mplib 绘制函数

在 lualatex 中使用 mplib 绘制函数

这个问题询问如何在 LaTeX 中生成漂亮的函数图。有几个很好的答案,但是,大多数答案要么依赖外部程序来生成图,要么(如果使用 TeX 来计算图的坐标)仅限于相当简单的函数。

我想用 luatex 来找到答案,用 lua 进行计算,用 mplib 来实际绘制图形。但是,我不知道如何将 lua 和 mplib 一起使用。在 lua 中生成图形并不难,我可以将生成的路径传递给 tikz 进行渲染:

\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}

我想知道使用 lua 生成绘图并使用 mplib 渲染它的最佳方法是什么。

答案1

这是我在 ConTeXt 中执行的操作。我不知道 LaTeX 包中是否有类似的功能。基本上,我在 lua 中进行计算,然后将结果写入\startMPcode..\stopMPcode环境中。

\启动lua代码
  需要(“数学”)
  局部正弦 = math.sin
  本地 cos = math.cos
  本地 pi = math.pi

  局部函数 scale(x)
    返回 x ..“ * 1cm”
  结尾
  局部函数对(x,y)
    返回“(” .. 比例(x) .. “,” .. 比例(y) .. “)”
  结尾

  局部函数点(f,x)
    返回对(x,f(x))
  结尾

  用户数据 = 用户数据或 {}

  函数用户数据.draw_function(f,a,b,n)
    局部步骤 = (ba)/n
    本地路径 = {}
    局部 i = 0
    对于 x = a, b,步骤执行
      路径[i] = 点(f,x)
      我=我+ 1
    结尾  
    路径 = 表.concat(路径,“--”)

    context("绘制" .. 路径 .. " withpen pencircle 缩放 2bp;")
  结尾

  函数 userdata.draw_x_axis(从,到)
      上下文(“绘制箭头” .. 对(从,0) .. “--” .. 对(到,0) .. “;”)
      上下文(“标签.rt(btex $x$ etex,“..对(to,0)..“);”)
  结尾

  函数 userdata.draw_y_axis(从,到)
      上下文(“绘制箭头” .. 对(0,从) .. “--” .. 对(0,到) .. “;”)
      上下文(“标签.top(btex $y$ etex,“..对(0,to)..“);”)
  结尾

  函数用户数据.plot()

    用户数据.draw_x_axis(-2, 6.5)
    用户数据.draw_y_axis(-2.2, 2.2)


    局部 f = 函数 (x)
      返回 sin(2*x) + cos(x)
    结尾

    用户数据.draw_function(f, 0, 2*pi, 40)
  结尾

\stoplua代码

\开始文本
  \ctxlua
    {上下文.startMPcode();
      用户数据.绘图();
     上下文.stopMPcode() ; }
\停止文本

但是,这假设函数中写入的所有内容userdata.plot()都是 MP 代码。如果您还希望该函数将内容打印到 TeX 流,那么您可以将所有 MP 代码存储在\startMPdrawingand\stopMPdrawing环境中并使用 检索它们\getMPdrawing

\启动lua代码
  需要(“数学”)
  局部正弦 = math.sin
  本地 cos = math.cos
  本地 pi = math.pi

  局部函数 scale(x)
    返回 x ..“ * 1cm”
  结尾
  局部函数对(x,y)
    返回“(” .. 比例(x) .. “,” .. 比例(y) .. “)”
  结尾

  局部函数点(f,x)
    返回对(x,f(x))
  结尾

  用户数据 = 用户数据或 {}

  函数用户数据.draw_function(f,a,b,n)
    局部步骤 = (ba)/n
    本地路径 = {}
    局部 i = 0
    对于 x = a, b,步骤执行
      路径[i] = 点(f,x)
      我=我+ 1
    结尾  
    路径 = 表.concat(路径,“--”)

  context.startMPdrawing()
    context("绘制" .. 路径 .. " withpen pencircle 缩放 2bp;")
  context.stopMPdrawing()
  结尾

  函数 userdata.draw_x_axis(从,到)
    context.startMPdrawing()
      上下文(“绘制箭头” .. 对(从,0) .. “--” .. 对(到,0) .. “;”)
      上下文(“标签.rt(btex $x$ etex,“..对(to,0)..“);”)
    context.stopMPdrawing()
  结尾

  函数 userdata.draw_y_axis(从,到)
    context.startMPdrawing()
      上下文(“绘制箭头” .. 对(0,从) .. “--” .. 对(0,到) .. “;”)
      上下文(“标签.top(btex $y$ etex,“..对(0,to)..“);”)
    context.stopMPdrawing()
  结尾

  函数用户数据.plot()
    用户数据.draw_x_axis(-2, 6.5)
    用户数据.draw_y_axis(-2.2, 2.2)


    局部 f = 函数 (x)
      返回 sin(2*x) + cos(x)
    结尾

    用户数据.draw_function(f, 0, 2*pi, 40)
  结尾

\stoplua代码

\开始文本
  \ctxlua
    { 上下文.resetMPdrawing() ;
      用户数据.绘图();
      上下文.MPdrawingdonetrue();
      上下文.获取MPdrawing();}
\停止文本

答案2

我将 Aditya 的 ConTeXt 解决方案与我的原始代码相结合,并针对 LaTeX 进行了修改。它基本可以正常工作,但我似乎在使用btex ... etex标签时遇到了麻烦。如果我将下面代码中的两行 mpost 标签替换为它们的注释版本,我会得到

! 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}

以下工作:

\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} 

关于如何改善这一点以及如何解决标签问题,您有什么想法吗?

相关内容