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
The best way to do this is to make your external file into a module. That means, you make sure that all variables are local
and in the end you return a table which exports all user-accessible functions. For example f(x,y,z)
is not going to be used outside, so I won't export it. As you can see I can also export functions under a different name.
-- Differential equation of the Lorenz attractor
local function f(x,y,z)
local sigma = 3
local rho = 26.5
local beta = 1
return {sigma*(y-x), -x*z + rho*x - y, x*y - beta*z}
end
-- Code to write PGFplots data as coordinates
local function print_LorAttrWithEulerMethod(h,npoints,option)
-- The initial point (x0,y0,z0)
local x0 = 0.0
local y0 = 1.0
local z0 = 0.0
-- we add a random number between -0.25 and 0.25
local x = x0 + (math.random()-0.5)/2
local y = y0 + (math.random()-0.5)/2
local z = z0 + (math.random()-0.5)/2
if option~=[[]] then
tex.sprint("\\addplot3["..option.."] coordinates{")
else
tex.sprint("\\addplot3 coordinates{")
end
-- we dismiss the first 100 points to go into the attractor
for i=1, 100 do
local m = f(x,y,z)
x = x + h * m[1]
y = y + h * m[2]
z = z + h * m[3]
end
for i=1, npoints do
local m = f(x,y,z)
x = x + h * m[1]
y = y + h * m[2]
z = z + h * m[3]
tex.sprint("("..x..","..y..","..z..")")
end
tex.sprint("}")
end
return { LorenzAttractor = print_LorAttrWithEulerMethod }
An advantage of having the Lua code in a separate file is that you do not have to worry about catcodes. Therefore you don't need the luacode
package at all. The module you created in scrap.lua
can be loaded like any regular Lua module. Usually you would load it like
local scrap = require("scrap")
but that won't work in LuaTeX because the scrap
variable would only be local to the \directlua
chunk it is mentioned in and can hence not be used in other chunks. Therefore you have to make a global variable scrap
to encapsulate the module
\directlua{scrap = require("scrap")}
The full document would read
\documentclass{article}
\usepackage{pgfplots}
\usepackage{tikz}
\directlua{scrap = require("scrap")}
\newcommand\addLUADEDplot[3][]{%
\directlua{scrap.LorenzAttractor(#2,#3,[[#1]])}%
}
\begin{document}
\begin{tikzpicture}
\begin{axis}
% SYNTAX: Solution of the Lorenz system
% with step h=0.02 sampled at 1000 points.
\addLUADEDplot[color=red,smooth]{0.02}{1000};
\addLUADEDplot[color=green,smooth]{0.02}{1000};
\addLUADEDplot[color=blue,smooth]{0.02}{1000};
\addLUADEDplot[color=cyan,smooth]{0.02}{1000};
\addLUADEDplot[color=magenta,smooth]{0.02}{1000};
\addLUADEDplot[color=yellow,smooth]{0.02}{1000};
\end{axis}
\end{tikzpicture}
\end{document}
Best Answer
The file mplibapi.pdf from the metapost source distrobution documents the low-level interface. Still, it is probably wise to read the luamplib code as well, because the conversion from mplib's return table (containing images) to PDF literal code is not quite trivial. http://www.tug.org/metapost/src/manual/mplibapi.pdf