summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/generic/pgfplots/lua
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/generic/pgfplots/lua')
-rw-r--r--Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots.lua68
-rw-r--r--Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/binary.lua41
-rw-r--r--Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/colormap.lua98
-rw-r--r--Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/meshplothandler.lua294
-rw-r--r--Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/pgfplotstexio.lua464
-rw-r--r--Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/pgfplotsutil.lua139
-rw-r--r--Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/plothandler.lua962
-rw-r--r--Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/statistics.lua400
-rw-r--r--Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/streamer.lua193
-rw-r--r--Master/texmf-dist/tex/generic/pgfplots/lua/pgfplotsoldpgfsupp/luamath/functions.lua649
-rw-r--r--Master/texmf-dist/tex/generic/pgfplots/lua/pgfplotsoldpgfsupp/luamath/parser.lua495
11 files changed, 3773 insertions, 30 deletions
diff --git a/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots.lua b/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots.lua
index dcb8ea94007..23aeaab2a84 100644
--- a/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots.lua
+++ b/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots.lua
@@ -1,37 +1,45 @@
-pgfplotsGetLuaBinaryStringFromCharIndicesChunkSize = 7000;
+require("pgfplots.binary")
-if unpack == nil then
- -- LUA 0.76 renamed unpack to table.unpack
- pgfplotsUnpack = table.unpack;
+-- all classes/globals will be added to this table:
+pgfplots = {}
+
+-- will be set by TeX:
+pgfplots.pgfplotsversion = nil
+
+if _VERSION == "Lua 5.1" or _VERSION == "Lua 5.0" then
+ texio.write("pgfplots: setting 'lua support=false': the lua version on this system is " .. _VERSION .. "; expected at least 'Lua 5.2'. Use a more recent TeX distribution to benefit from LUA in pgfplots.\n")
+
+ -- the entire lua backend will be switched off if this is false:
+ tex.sprint("\\pgfplotsset{lua support=false}")
+ return
else
- pgfplotsUnpack = unpack;
+ -- well, 5.2 is what this stuff has been written for.
+ -- Is there a good reason why it shouldn't work on 5.1 !? No, I guess not. Except that it took me a long time
+ -- to figure out that 5.2 broke compatibility in lots of ways - and it was difficult enough to get it up and running.
+ -- If someone wants (and needs) to run it in 5.1 - I would accept patches.
end
--- Takes a table containing an arbitrary number of integers in the range 0..255 and converts it
--- into a binary stream of the corresponding binary chars.
---
--- @param charIndices a table containing 0...N arguments; each in the range 0..255
---
--- @return a string containing binary content, one byte for each input integer.
-function pgfplotsGetLuaBinaryStringFromCharIndices(charIndices)
- -- unpack extracts only the indices (we can't provide a table to string.char).
- -- note that pdf.immediateobj has been designed to avoid sanity checking for invalid UTF strings -
- -- in other words: it accepts binary strings.
- --
- -- unfortunately, this here fails for huge input tables:
- -- pgfplotsretval=string.char(unpack(charIndices));
- -- we have to create it incrementally using chunks:
- local len = #charIndices;
- local chunkSize = pgfplotsGetLuaBinaryStringFromCharIndicesChunkSize;
- local buf = {};
- -- ok, append all full chunks of chunkSize first:
- local numFullChunks = math.floor(len/chunkSize);
- for i = 0, numFullChunks-1, 1 do
- table.insert(buf, string.char(pgfplotsUnpack(charIndices, 1+i*chunkSize, (i+1)*chunkSize)));
- end
- -- append the rest:
- table.insert(buf, string.char(pgfplotsUnpack(charIndices, 1+numFullChunks*chunkSize)));
- return table.concat(buf);
+require("pgfplots.pgfplotsutil")
+
+-- see pgfrcs.code.tex -- all versions after 3.0.0 (excluding 3.0.0) will set this version:
+if not pgf or not pgf.pgfversion then
+ pgfplots.log("log", "pgfplots.lua: loading complementary lua code for your pgf version...\n")
+ pgfplots.pgfluamathfunctions = require("pgfplotsoldpgfsupp.luamath.functions")
+ pgfplots.pgfluamathparser = require("pgfplotsoldpgfsupp.luamath.parser")
+else
+ pgfplots.pgfluamathparser = require("pgf.luamath.parser")
+ pgfplots.pgfluamathfunctions = require("pgf.luamath.functions")
end
+pgfplots.pgftonumber = pgfplots.pgfluamathfunctions.tonumber
+pgfplots.tostringfixed = pgfplots.pgfluamathfunctions.tostringfixed
+pgfplots.toTeXstring = pgfplots.pgfluamathfunctions.toTeXstring
+
+
+require("pgfplots.plothandler")
+require("pgfplots.meshplothandler")
+require("pgfplots.colormap")
+require("pgfplots.streamer")
+-- hm. perhaps this here should become a separate module:
+require("pgfplots.pgfplotstexio")
diff --git a/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/binary.lua b/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/binary.lua
new file mode 100644
index 00000000000..704778440bc
--- /dev/null
+++ b/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/binary.lua
@@ -0,0 +1,41 @@
+
+-- Attention: functions in this file are part of the backend driver.
+-- They are supposed to work back with Lua 5.1 .
+-- Note that most of the 'lua backend' requires Lua 5.2 (currently)
+
+pgfplotsGetLuaBinaryStringFromCharIndicesChunkSize = 7000;
+
+if unpack == nil then
+ -- LUA 0.76 renamed unpack to table.unpack
+ pgfplotsUnpack = table.unpack;
+else
+ pgfplotsUnpack = unpack;
+end
+
+-- Takes a table containing an arbitrary number of integers in the range 0..255 and converts it
+-- into a binary stream of the corresponding binary chars.
+--
+-- @param charIndices a table containing 0...N arguments; each in the range 0..255
+--
+-- @return a string containing binary content, one byte for each input integer.
+function pgfplotsGetLuaBinaryStringFromCharIndices(charIndices)
+ -- unpack extracts only the indices (we can't provide a table to string.char).
+ -- note that pdf.immediateobj has been designed to avoid sanity checking for invalid UTF strings -
+ -- in other words: it accepts binary strings.
+ --
+ -- unfortunately, this here fails for huge input tables:
+ -- pgfplotsretval=string.char(unpack(charIndices));
+ -- we have to create it incrementally using chunks:
+ local len = #charIndices;
+ local chunkSize = pgfplotsGetLuaBinaryStringFromCharIndicesChunkSize;
+ local buf = {};
+ -- ok, append all full chunks of chunkSize first:
+ local numFullChunks = math.floor(len/chunkSize);
+ for i = 0, numFullChunks-1, 1 do
+ table.insert(buf, string.char(pgfplotsUnpack(charIndices, 1+i*chunkSize, (i+1)*chunkSize)));
+ end
+ -- append the rest:
+ table.insert(buf, string.char(pgfplotsUnpack(charIndices, 1+numFullChunks*chunkSize)));
+ return table.concat(buf);
+end
+
diff --git a/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/colormap.lua b/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/colormap.lua
new file mode 100644
index 00000000000..28fd6ddd56b
--- /dev/null
+++ b/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/colormap.lua
@@ -0,0 +1,98 @@
+--
+-- This file contains parts of pgfplotscolormap.code.tex
+--
+
+local math=math
+local pgfplotsmath = pgfplots.pgfplotsmath
+local io=io
+local type=type
+local tostring=tostring
+local error=error
+local table=table
+
+do
+-- all globals will be read from/defined in pgfplots:
+local _ENV = pgfplots
+-----------------------------------
+
+ColorSpace = newClass()
+function ColorSpace:constructor(numComponents)
+ self.numComponents=numComponents
+end
+
+rgb = ColorSpace.new(3)
+cmyk = ColorSpace.new(4)
+gray = ColorSpace.new(1)
+
+
+ColorMap = newClass()
+
+ColorMap.range =1000
+
+-- h: mesh width between adjacent values
+-- colorspace: an instance of ColorSpace
+-- values: an array (1-based table) with color components. Each color component is supposed to be a table with K entries where K is colorspace:numComponents
+function ColorMap:constructor( h, colorspace, values)
+ if not h or not colorspace or not values then error("arguments must not be nil")end
+
+ self.name = name
+ self.h = h
+ self.invh = 1/h
+ self.colorspace = colorspace
+ self.values = values
+
+ local numComponents = self.colorspace.numComponents
+ for i = 1,#self.values do
+ local value = self.values[i]
+ if #value ~= numComponents then
+ error("Some value has an unexpected number of color components, expected " .. self.colorspace.numComponents .. " but was ".. #value);
+ end
+ end
+end
+
+function ColorMap:findPrecomputed(inMin, inMax, x)
+ local transformed
+ if inMin == 0 and inMax == ColorMap.range then
+ transformed = x
+ else
+ local scale = ColorMap.range / (inMax - inMin)
+
+ transformed = (x - inMin) * scale
+ end
+ transformed = math.max(0, transformed)
+ transformed = math.min(ColorMap.range, transformed)
+
+ local divh = transformed * self.invh
+ local intervalno = math.floor(divh)
+ local factor = divh - intervalno
+ local factor_two = 1-factor
+
+
+ -- Step 2: interpolate the desired RGB value using vector valued interpolation on the identified interval
+ if intervalno+1 == #self.values then
+ -- ah- we are at the right end!
+ return self.values[#self.values]
+ end
+
+ local left = self.values[intervalno+1]
+ local right = self.values[intervalno+2]
+ if not left or not right then error("Internal error: the color map does not have enough values for interval no " .. intervalno )end
+
+ local result = {}
+ for i = 1,self.colorspace.numComponents do
+ local result_i = factor_two * left[i] + factor * right[i]
+
+ result[i] = result_i
+ end
+
+ return result
+end
+
+-----------------------------------
+
+-- global registry of all colormaps.
+-- Key: colormap name
+-- Value: an instance of ColorMap
+ColorMaps = {}
+
+end
diff --git a/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/meshplothandler.lua b/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/meshplothandler.lua
new file mode 100644
index 00000000000..a3b492a5296
--- /dev/null
+++ b/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/meshplothandler.lua
@@ -0,0 +1,294 @@
+-- This file has dependencies to BOTH, the TeX part of pgfplots and the LUA part.
+-- It is the only LUA component with this property.
+--
+-- Its purpose is to encapsulate the communication between TeX and LUA in a central LUA file
+
+local pgfplotsmath = pgfplots.pgfplotsmath
+local error=error
+local table=table
+local string=string
+local tostring=tostring
+local type=type
+local io=io
+
+do
+-- all globals will be read from/defined in pgfplots:
+local _ENV = pgfplots
+
+
+-------------------------------------------------------
+-- A patch type.
+-- @see \pgfplotsdeclarepatchclass
+
+PatchType = newClass()
+
+function PatchType:constructor(name, numVertices)
+ self.name = name
+ self.numVertices = numVertices
+end
+
+function PatchType:__tostring()
+ return self.name
+end
+
+function PatchType:newPatch(coords)
+ return Patch.new(self,coords)
+end
+
+LinePatchType = newClassExtends(PatchType)
+
+function LinePatchType:constructor()
+ PatchType.constructor(self, "line", 2)
+end
+
+TrianglePatchType = newClassExtends(PatchType)
+
+function TrianglePatchType:constructor()
+ PatchType.constructor(self, "triangle", 3)
+end
+
+
+RectanglePatchType = newClassExtends(PatchType)
+
+function RectanglePatchType:constructor()
+ PatchType.constructor(self, "rectangle", 4)
+end
+
+-------------------------------------------------------
+--
+-- a single patch.
+-- @see \pgfplotsdeclarepatchclass
+
+Patch = newClass()
+
+function Patch:constructor(patchtype, coords)
+ if not patchtype or not coords then error("arguments must not be nil") end
+ if #coords ~= patchtype.numVertices then error("Unexpected number of coordinates provided; expected " .. tostring(patchtype.numVertices) .. " but got " .. tostring(#coords)) end
+
+ self.patchtype = patchtype
+ self.coords = coords
+end
+
+-------------------------------------------------------
+-- Replicates \pgfplotsplothandlermesh (to some extend)
+MeshPlothandler = newClassExtends(Plothandler)
+
+function MeshPlothandler:constructor(axis, pointmetainputhandler)
+ Plothandler.constructor(self,"mesh", axis, pointmetainputhandler)
+end
+
+-- see \pgfplot@apply@zbuffer
+function MeshPlothandler:reverseScanline(scanLineLength)
+ local coords = self.coords
+ local tmp
+ local scanlineOff
+ local numScanLines = #coords / scanLineLength
+ for scanline = 0,numScanLines-1,1 do
+ scanlineOff = scanline * scanLineLength
+ local reverseindex = scanlineOff + scanLineLength
+ for i = 0,scanLineLength/2-1,1 do
+ tmp = coords[1+scanlineOff+i]
+ coords[1+scanlineOff+i] = coords[reverseindex]
+ coords[reverseindex] = tmp
+
+ reverseindex = reverseindex-1
+ end
+ end
+end
+
+-- see \pgfplot@apply@zbuffer
+function MeshPlothandler:reverseTransposed(scanLineLength)
+ local coords = self.coords
+ local tmp
+ local scanlineOff
+ local numScanLines = #coords / scanLineLength
+ local reverseScanline = numScanLines-1
+ for scanline = 0,numScanLines/2-1,1 do
+ scanlineOff = 1+scanline * scanLineLength
+ reverseScanlineOff = 1+reverseScanline * scanLineLength
+ for i = 0,scanLineLength-1 do
+ tmp = coords[scanlineOff+i]
+ coords[scanlineOff+i] = coords[reverseScanlineOff+i]
+ coords[reverseScanlineOff+i] = tmp
+ end
+
+ reverseScanline = reverseScanline-1
+ end
+end
+
+-- see \pgfplot@apply@zbuffer
+function MeshPlothandler:reverseStream()
+ local coords = self.coords
+ local tmp
+ local reverseindex = #coords
+ for i = 1,#coords/2 do
+ tmp = coords[i]
+ coords[i] = coords[reverseindex]
+ coords[reverseindex] = tmp
+ reverseindex = reverseindex-1
+ end
+end
+
+
+
+-------------------------------------------------------
+--
+-- The (LUA!) visualizer for patch plots. It prepares stuff such that TeX only needs to work with lowlevel driver (PGF) streams.
+--
+
+MeshVisualizer = newClassExtends(PlotVisualizer)
+
+local COORDINATE_VALUE_OF_JUMPS = -16000
+local meshVisualizerTagEmptyCoordinates = function(pt)
+ pt.pgfXY= { COORDINATE_VALUE_OF_JUMPS, COORDINATE_VALUE_OF_JUMPS }
+ pt.x = { COORDINATE_VALUE_OF_JUMPS, COORDINATE_VALUE_OF_JUMPS, COORDINATE_VALUE_OF_JUMPS }
+end
+
+function MeshVisualizer:constructor(sourcePlotHandler, patchType, rows, cols, isXVariesOrdering, isMatrixInput, isMatrixOutput, isZBufferSort)
+ PlotVisualizer.constructor(self,sourcePlotHandler)
+ self.patchType = patchType
+ self.isMatrixInput = isMatrixInput
+ self.isMatrixOutput = isMatrixOutput
+ self.isZBufferSort = isZBufferSort
+ self.rows = rows
+ self.cols = cols
+ self.isXVariesOrdering =isXVariesOrdering
+
+ self.isOneDimMode= false
+ self.scanLineLength =-1
+ if isMatrixInput then
+ -- isOneDimMode is ONLY interesting for matrix input
+ if cols <= 1 or rows <=1 then
+ self.isOneDimMode = true
+ self.patchType = LinePatchType.new()
+ -- this is not yet implemented (and cannot happen since the TeX call does catch this)
+ error("UNSUPPORTED OPERATION EXCEPTION")
+ end
+
+ if isXVariesOrdering then
+ -- x varies (=rowwise)
+ self.scanLineLength = cols
+ else
+ -- y varies (=colwise)
+ self.scanLineLength = rows
+ end
+
+ self.notifyJump = meshVisualizerTagEmptyCoordinates
+ else
+ -- disable any special handling
+ self.isXVariesOrdering = true
+ end
+
+ -- log("initialized MeshVisualizer with " .. tostring(sourcePlotHandler) .. ", " .. tostring(patchType) .. ", isMatrixInput = " .. tostring(isMatrixInput) .. ", isMatrixOutput = " .. tostring(isMatrixOutput) .. ", isZBufferSort = " .. tostring(isZBufferSort) .. " rows = " ..tostring(rows) .. " cols = " ..tostring(cols) .. " is x varies=" .. tostring(isXVariesOrdering))
+end
+
+function MeshVisualizer:getVisualizationOutput()
+ local result = PlotVisualizer.getVisualizationOutput(self)
+
+ if self.isMatrixInput and not self.isMatrixOutput then
+ result = self:decodeIntoPatches(result)
+ end
+
+ if self.isZBufferSort then
+ result = self:applyZBufferSort(result)
+ end
+
+ return result
+end
+
+-- @param coords an array of Coord
+function MeshVisualizer:applyZBufferSort(coords)
+ -- in order to sort this thing, we need to compute the sort key (view depth) for each coord.
+ -- furthermore, each list entry must be single patch... that means we need a (huge?) temporary table.
+
+ local patchType = self.patchType
+ local numVertices = patchType.numVertices
+
+ if (#coords % numVertices) ~= 0 then error("Got an unexpected number of input coordinates: each patch has " .. tostring(numVertices) .. " vertices, but the number of coords " .. tostring(#coords) .. " is no multiple of this number") end
+ local numPatches = #coords / numVertices
+
+ -- STEP 1: compute an array of patches.
+ local patches = {}
+ local off=1
+ for i = 1,numPatches do
+ local patchCoords = {}
+ for j = 1,numVertices do
+ local pt = coords[off]
+ off = off+1
+ patchCoords[j] = pt
+ end
+ local patch = patchType:newPatch(patchCoords)
+ patches[i] = patch
+ end
+ if off ~= 1+#coords then error("Internal error: not all coordinates are part of patches (got " .. tostring(off) .. "/" .. tostring(#coords) ..")") end
+
+ -- STEP 2: assign the sort key: the "element depth".
+ --
+ -- the "element depth" is defined to be the MEAN of all
+ -- vertex depths.
+ -- And since the mean is 1/n * sum_{i=1}^n V_i, we can
+ -- directly omit the 1/n --- it is the same for every
+ -- vertex anyway, and we only want to compare the depth
+ -- values.
+ local axis = self.axis
+ local getVertexDepth = axis.getVertexDepth
+ for i=1,numPatches do
+ local patch = patches[i]
+ local patchcoords = patch.coords
+
+ local sumOfVertexDepth = 0
+ for j = 1,numVertices do
+ local vertex = patchcoords[j]
+
+ local vertexDepth = getVertexDepth(axis,vertex)
+
+ sumOfVertexDepth = sumOfVertexDepth + vertexDepth
+ end
+ patch.elementDepth = sumOfVertexDepth
+ end
+
+ -- STEP 3: SORT.
+ local comparator = function(patchA, patchB)
+ return patchA.elementDepth > patchB.elementDepth
+ end
+ table.sort(patches, comparator)
+
+ -- STEP 4: convert back into a list (in-place).
+ local off = 1
+ for i=1,numPatches do
+ local patch = patches[i]
+ local patchcoords = patch.coords
+ for j = 1,numVertices do
+ coords[off] = patchcoords[j]
+ off = off+1
+ end
+ end
+ if off ~= 1+#coords then error("Internal error: not all coordinates are part of patches (got " .. tostring(off) .. "/" .. tostring(#coords) ..")") end
+
+ return coords
+end
+
+function MeshVisualizer:decodeIntoPatches(coords)
+ local result = {}
+
+ local scanLineLength = self.scanLineLength
+ local length = #coords
+
+ local i = scanLineLength
+ while i < length do
+ local im = i-scanLineLength
+
+ for j = 2,scanLineLength do
+ table.insert(result, coords[im+j-1]) -- (i-1,j-1)
+ table.insert(result, coords[im+j]) -- (i-1,j )
+ table.insert(result, coords[i+j]) -- (i ,j )
+ table.insert(result, coords[i+j-1]) -- (i ,j-1)
+ end
+
+ i = i + scanLineLength
+ end
+
+ return result
+end
+
+end
diff --git a/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/pgfplotstexio.lua b/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/pgfplotstexio.lua
new file mode 100644
index 00000000000..ca8b894a7c3
--- /dev/null
+++ b/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/pgfplotstexio.lua
@@ -0,0 +1,464 @@
+-- This file has dependencies to BOTH, the TeX part of pgfplots and the LUA part.
+-- It is the only LUA component with this property.
+--
+-- Its purpose is to encapsulate the communication between TeX and LUA in a central LUA file
+--
+--
+-- Here is the idea how the TeX backend communicates with the LUA backend:
+--
+-- * TeX can call LUA methods in order to "do something". The reverse direction is not true: LUA cannot call TeX methods.
+--
+-- * the only way that LUA can read TeX input values or write TeX output values is the top layer (at the time of this writing: only pgfplotstexio.lua ).
+--
+-- * The LUA backend has one main purpose: scalability and performance.
+-- Its purpose is _not_ to run a standalone visualization.
+--
+-- The precise meaning of "scalability" is: the LUA backend should do as much
+-- as possible which is done for single coordinates. Coordinates constitute
+-- "scalability": the number of coordinates can become arbitrarily large.
+--
+-- "Performance" is related to scalability. But it is more: some dedicated
+-- utility function might have a TeX backend and will be invoked whenever it
+-- is needed. Candidates are colormap functions, z buffer arithmetics etc.
+--
+-- Thus, the best way to ensure "scalability" is to move _everything_ which is to be done for a single coordinate to LUA.
+--
+-- Sometimes, this will be impossible or too expensive.
+-- Here, "Performance" might still be optimized by some dedicated LUA function.
+--
+--
+-- Unfortunately, the LUA backend does not simplify the code base - it makes it more complicated.
+-- This is due to the way it is used: one needs to know when the TeX backend
+-- delegates all its work to LUA. It is also due to the fact that it
+-- duplicates code: the same code is present in TeX and in LUA. I chose to keep
+-- the two code bases close to each other. This has a chance to simplify maintenance: if I know
+-- how something works in TeX, and I find some entry point in LUA, it will
+-- hopefully be closely related.
+--
+--
+-- It follows an overview over entry points into the LUA backend:
+--
+-- * \begin{axis}. It invokes \pgfplots@prepare@LUA@api .
+-- The purpose is to define the global pgfplots.gca "_g_et _c_urrent _a_xis" and to transfer some key presets.
+--
+-- Log message: "lua backend=true: Activating LUA backend for axis."
+--
+-- * \end{axis}. It invokes \pgfplots@LUA@visualization@update@axis .
+-- The purpose is to transfer results of the survey phase to LUA; in particular axis properties like
+-- the view direction, data scale transformations, axis wide limits and some related properties.
+-- This has nothing to do with coordinates; the survey phase of coordinates is handled in a different way (see below).
+--
+-- Eventually, \pgfplots@LUA@cleanup will clear the global pgfplots.gca .
+--
+-- * \addplot . This has much to do with scalability, so much of its functionality is done in the LUA backend.
+--
+-- Keep in mind that \addplot is part of the survey phase: it collects coordinates and updates axis limits.
+-- Afterwards, it stores the survey results in the current axis.
+--
+-- The survey phase currently has two different ways to communicate with the LUA backend:
+--
+-- 1. PARTIAL MODE. In this mode, the coordinate stream comes from TeX:
+-- some TeX code generates the coordinates. Whenever the stream is ready,
+-- it will invoke \pgfplots@LUA@survey@point . This, in turn, calls the
+-- LUA backend in order to complete the survey (which boils down to
+-- pgfplots.texSurveyPoint). PARTIAL MODE saves lots of time, but its
+-- scalability is limited due to the intensive use of TeX, it is less
+-- powerful than COMPLETE MODE.
+--
+-- 2. COMPLETE MODE. In this mode, the entire coordinate stream is on the
+-- LUA side. The TeX code will merely call start the survey phase, call
+-- LUA, and end the survey phase. This is the most efficient
+-- implementation. At the time of this writing, it is limited to `\addplot
+-- expression`: the code for `\addplot expression` tries to transfer the
+-- entire processing to the LUA backend. If it succeeds, it will do
+-- nothing on the TeX side.
+--
+-- Both PARTIAL MODE and COMPLETE MODE call
+-- \pgfplots@LUA@survey@start : transfer plot type and current axis arguments to LUA
+-- and
+-- \pgfplots@LUA@survey@end : copy LUA axis arguments back to TeX.
+--
+-- Eventually, the axis will initiate the visualization phase for each plot. This is done by
+-- a) \pgfplots@LUA@visualization@init : it calls pgfplots.texVisualizationInit() and results in the log message
+-- "lua backend=true: Activating partial LUA backend for visualization of plot 0".
+-- b) \pgfplots@LUA@visualization@of@current@plot : it transfers control
+-- to LUA (pgfplots.texVisualizePlot) and does as much with the
+-- coordinates as possible. Eventually, it streams the result back to
+-- TeX which will visualize the stream by means of PGF's plot streams.
+-- This is somewhat complicated since it modifies the TeX streaming.
+local pgfplotsmath = pgfplots.pgfplotsmath
+local tex=tex
+local tostring=tostring
+local error=error
+local table=table
+local string=string
+local pairs=pairs
+local pcall=pcall
+local type=type
+local lpeg = require("lpeg")
+local math = math
+
+do
+-- all globals will be read from/defined in pgfplots:
+local _ENV = pgfplots
+
+local pgftonumber = pgfluamathfunctions.tonumber
+
+-- will be assigned by pgfplots at boot time.
+LOAD_TIME_CATCODETABLE = nil
+
+-- Called during \addplot, i.e. during the survey phase. It is only called in PARTIAL MODE (see above).
+function texSurveyPoint(x,y,z,meta)
+ local pt = Coord.new()
+ pt.x[1] = x
+ pt.x[2] = y
+ pt.x[3] = z
+ pt.meta = meta
+
+ gca.currentPlotHandler:surveypoint(pt)
+end
+
+-- Copies survey results of the current plot back to TeX. It prints a couple of executable TeX statements as result.
+-- @see \pgfplots@LUA@survey@end
+function texSurveyEnd()
+ local result = gca:surveyToPgfplots(gca.currentPlotHandler, true)
+ --log("returning " .. result .. "\n\n")
+
+ tex.sprint(LOAD_TIME_CATCODETABLE, result);
+ gca.currentPlotHandler=nil
+end
+
+-- A performance optimization: point meta transformation is done on the LUA side.
+--
+-- expands to the transformed point meta
+function texPerpointMetaTrafo(metaStr)
+ local meta = pgftonumber(metaStr)
+ local transformed = gca.currentPlotHandler:visualizationTransformMeta(meta);
+ tex.sprint(LOAD_TIME_CATCODETABLE, tostringfixed(transformed));
+end
+
+-- Called at the beginning of each plot visualization.
+--
+-- expands to '1' if LUA is available for this plot and '0' otherwise.
+-- @see texVisualizePlot
+function texVisualizationInit(plotNum, plotIs3d)
+ if not plotNum or plotIs3d==nil then error("arguments must not be nil") end
+
+ local currentPlotHandler = gca.plothandlers[plotNum+1]
+ gca.currentPlotHandler = currentPlotHandler;
+ if currentPlotHandler then
+ currentPlotHandler.plotIs3d = plotIs3d
+ currentPlotHandler:visualizationPhaseInit();
+ tex.sprint("1")
+ else
+ -- ok, this plot has no LUA support.
+ tex.sprint("0")
+ end
+end
+
+local pgfXyCoordSerializer = function(pt)
+ -- FIXME : it is unsure of whether this here really an improvement - or if it would be faster to compute that stuff in TeX...
+ if pt.pgfXY ~=nil then
+ return "{" .. tostringfixed(pt.pgfXY[1]) .. "}{" .. tostringfixed(pt.pgfXY[2]) .. "}"
+ else
+ return "{0}{0}"
+ end
+end
+
+-- Actually does as much of the visualization of the current plot: it transforms all coordinates to some point where the TeX visualization mode continues.
+--
+-- It expands to the resulting coordinates. Note that these coordinates are already mapped somehow (typically: to fixed point)
+-- @see texVisualizationInit
+function texVisualizePlot(visualizerFactory)
+ if not visualizerFactory then error("arguments must not be nil") end
+ if type(visualizerFactory) ~= "function" then error("arguments must be a function (a factory)") end
+
+ local currentPlotHandler = gca.currentPlotHandler
+ if not currentPlotHandler then error("Illegal state: The current plot has no LUA plot handler!") end
+
+ local visualizer = visualizerFactory(currentPlotHandler)
+
+ local result = visualizer:getVisualizationOutput()
+ local result_str = currentPlotHandler:getCoordsInTeXFormat(gca, result, pgfXyCoordSerializer)
+ --log("returning " .. result_str .. "\n\n")
+ tex.sprint(LOAD_TIME_CATCODETABLE, result_str)
+end
+
+-- Modifies the Surveyed coordinate list.
+-- Expands to nothing
+function texApplyZBufferReverseScanline(scanLineLength)
+ local currentPlotHandler = gca.currentPlotHandler
+ if not currentPlotHandler then error("This function cannot be used in the current context") end
+
+ currentPlotHandler:reverseScanline(scanLineLength)
+end
+
+-- Modifies the Surveyed coordinate list.
+-- Expands to nothing
+function texApplyZBufferReverseTransposed(scanLineLength)
+ local currentPlotHandler = gca.currentPlotHandler
+ if not currentPlotHandler then error("This function cannot be used in the current context") end
+
+ currentPlotHandler:reverseTransposed(scanLineLength)
+end
+
+-- Modifies the Surveyed coordinate list.
+-- Expands to nothing
+function texApplyZBufferReverseStream()
+ local currentPlotHandler = gca.currentPlotHandler
+ if not currentPlotHandler then error("This function cannot be used in the current context") end
+
+ currentPlotHandler:reverseStream(scanLineLength)
+end
+
+-- Modifies the Surveyed coordinate list.
+--
+-- Note that this is UNRELATED to mesh/surface plots! They have their own (patch-based) z buffer.
+--
+-- Expands to nothing
+function texApplyZBufferSort()
+ local currentPlotHandler = gca.currentPlotHandler
+ if not currentPlotHandler then error("This function cannot be used in the current context") end
+
+ currentPlotHandler:sortCoordinatesByViewDepth()
+end
+
+-- Modifies the Surveyed coordinate list.
+-- Expands to the resulting coordinates
+function texGetSurveyedCoordsToPgfplots()
+ local currentPlotHandler = gca.currentPlotHandler
+ if not currentPlotHandler then error("This function cannot be used in the current context") end
+
+ tex.sprint(LOAD_TIME_CATCODETABLE, currentPlotHandler:surveyedCoordsToPgfplots(gca))
+end
+
+-- Performance optimization: computes the colormap lookup.
+function texColorMapPrecomputed(mapName, inMin, inMax, x)
+ local colormap = ColorMaps[mapName];
+ if colormap then
+ local result = colormap:findPrecomputed(
+ pgftonumber(inMin),
+ pgftonumber(inMax),
+ pgftonumber(x))
+
+ local str = ""
+ for i = 1,#result do
+ if i>1 then str = str .. "," end
+ str = str .. tostringfixed(result[i])
+ end
+ tex.sprint(LOAD_TIME_CATCODETABLE, str)
+ end
+end
+
+local function isStripPrefixOrSuffixChar(char)
+ return char == ' ' or char == '{' or char == "}"
+end
+
+-- Expressions can be something like
+-- ( {(6+(sin(3*(x+3*y))+1.25)*cos(x))*cos(y)},
+-- {(6+(sin(3*(x+3*y))+1.25)*cos(x))*sin(y)},
+-- {((sin(3*(x+3*y))+1.25)*sin(x))} );
+--
+-- These result in expressions = { " {...}", " {...}", " {...} " }
+-- -> this function removes the surrounding braces and the white spaces.
+local function removeSurroundingBraces(expressions)
+ for i=1,#expressions do
+ local expr = expressions[i]
+ local startIdx
+ local endIdx
+ startIdx=1
+ while startIdx<#expr and isStripPrefixOrSuffixChar(string.sub(expr,startIdx,startIdx)) do
+ startIdx = startIdx+1
+ end
+ endIdx = #expr
+ while endIdx > 0 and isStripPrefixOrSuffixChar(string.sub(expr,endIdx,endIdx)) do
+ endIdx = endIdx-1
+ end
+
+ expr = string.sub(expr, startIdx, endIdx )
+ expressions[i] = expr
+ end
+end
+
+-------------
+-- A parser for foreach statements - at least those which are supported in this LUA backend.
+--
+local samplesAtToDomain
+do
+ local P = lpeg.P
+ local C = lpeg.C
+ local V = lpeg.V
+ local match = lpeg.match
+ local space_pattern = P(" ")^0
+
+ local Exp = V"Exp"
+ local comma = P"," * space_pattern
+ -- this does not catch balanced braces. Ok for now... ?
+ local argument = C( ( 1- P"," )^1 ) * space_pattern
+ local grammar = P{ "initialRule",
+ initialRule = space_pattern * Exp * -1,
+ Exp = lpeg.Ct(argument * comma * argument * comma * P"..." * space_pattern * comma *argument )
+ }
+
+ -- Converts very simple "samples at" values to "domain=A:B, samples=N"
+ --
+ -- @param foreachString something like -5,-4,...,5
+ -- @return a table where
+ -- [0] = domain min
+ -- [1] = domain max
+ -- [2] = samples
+ -- It returns nil if foreachString is no "very simple value of 'samples at'"
+ samplesAtToDomain = function(foreachString)
+ local matches = match(grammar,foreachString)
+
+ if not matches or #matches ~= 3 then
+ return nil
+ else
+ local arg1 = matches[1]
+ local arg2 = matches[2]
+ local arg3 = matches[3]
+ arg1= pgfluamathparser.pgfmathparse(arg1)
+ arg2= pgfluamathparser.pgfmathparse(arg2)
+ arg3= pgfluamathparser.pgfmathparse(arg3)
+
+ if not arg1 or not arg2 or not arg3 then
+ return nil
+ end
+
+ if arg1 > arg2 then
+ return nil
+ end
+
+ local domainMin = arg1
+ local h = arg2-arg1
+ local domainMax = arg3
+
+ -- round to the nearest integer (using +0.5, should be ok)
+ local samples = math.floor((domainMax - domainMin)/h + 0.5) + 1
+
+ return domainMin, domainMax, samples
+ end
+ end
+end
+
+-- This is the code which attempts to transfer control from `\addplot expression' to LUA.
+--
+-- If it succeeds, the entire plot stream and the entire survey phase has been done in LUA.
+--
+-- generates TeX output '1' on success and '0' on failure
+-- @param debugMode one of a couple of strings: "off", "verbose", or "compileerror"
+function texAddplotExpressionCoordinateGenerator(
+ is3d,
+ xExpr, yExpr, zExpr,
+ sampleLine,
+ domainxmin, domainxmax,
+ domainymin, domainymax,
+ samplesx, samplesy,
+ variablex, variabley,
+ samplesAt,
+ debugMode
+)
+ local plothandler = gca.currentPlotHandler
+ local coordoutputstream = SurveyCoordOutputStream.new(plothandler)
+
+ if samplesAt and string.len(samplesAt) >0 then
+ -- "samples at" has higher priority than domain.
+ -- Use it!
+
+ domainxmin, domainxmax, samplesx = samplesAtToDomain(samplesAt)
+ if not domainxmin then
+ -- FAILURE: could not convert "samples at".
+ -- Fall back to a TeX based survey.
+ log("log", "LUA survey failed: The value of 'samples at= " .. tostring(samplesAt) .. "' is unsupported by the LUA backend (currently, only 'samples at={a,b,...,c}' is supported).\n")
+ tex.sprint("0")
+ return
+ end
+
+ else
+ domainxmin= pgftonumber(domainxmin)
+ domainxmax= pgftonumber(domainxmax)
+ samplesx= pgftonumber(samplesx)
+ end
+
+ local expressions
+ local domainMin
+ local domainMax
+ local samples
+ local variableNames
+
+ -- allow both, even if sampleLine=1. We may want to assign a dummy value to y.
+ variableNames = { variablex, variabley }
+
+ if sampleLine==1 then
+ domainMin = { domainxmin }
+ domainMax = { domainxmax }
+ samples = { samplesx }
+ else
+ local domainymin = pgftonumber(domainymin)
+ local domainymax = pgftonumber(domainymax)
+ local samplesy = pgftonumber(samplesy)
+
+ domainMin = { domainxmin, domainymin }
+ domainMax = { domainxmax, domainymax }
+ samples = { samplesx, samplesy }
+ end
+ if is3d then
+ expressions = {xExpr, yExpr, zExpr}
+ else
+ expressions = {xExpr, yExpr}
+ end
+ removeSurroundingBraces(expressions)
+
+ local generator = AddplotExpressionCoordinateGenerator.new(
+ coordoutputstream,
+ expressions,
+ domainMin, domainMax,
+ samples,
+ variableNames)
+
+ local messageOnFailure
+ local compileErrorOnFailure
+ if debugMode == "compileerror" then
+ compileErrorOnFailure = true
+ messageOnFailure = true
+ elseif debugMode == "off" or debugMode == "verbose" then
+ messageOnFailure = true
+ compileErrorOnFailure = false
+ elseif debugMode == "off and silent" then
+ messageOnFailure = false
+ compileErrorOnFailure = false
+ else
+ error("Got unknown debugMode = " .. debugMode )
+ end
+
+ local success
+ if compileErrorOnFailure then
+ success = generator:generateCoords()
+ else
+ local resultOfGenerator
+ success, resultOfGenerator = pcall(generator.generateCoords, generator)
+ if success then
+ -- AH: "pcall" returned 'true'. In this case, 'success' is the boolean returned by generator
+ success = resultOfGenerator
+ end
+
+ if messageOnFailure and not success and type(resultOfGenerator) ~= "boolean" then
+ log("log", "LUA survey failed: " .. resultOfGenerator .. ". Use \\pgfplotsset{lua debug} to see more.\n")
+ end
+ end
+
+ if not type(success) == 'boolean' then error("Illegal state: expected boolean result") end
+
+ if success then
+ tex.sprint("1")
+ else
+ tex.sprint("0")
+ end
+end
+
+-- Creates the default plot visualizer factory. It simply applies data scale trafos.
+function defaultPlotVisualizerFactory(plothandler)
+ return PlotVisualizer.new(plothandler)
+end
+
+end
diff --git a/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/pgfplotsutil.lua b/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/pgfplotsutil.lua
new file mode 100644
index 00000000000..b3b09fea688
--- /dev/null
+++ b/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/pgfplotsutil.lua
@@ -0,0 +1,139 @@
+local math=math
+local string=string
+local type=type
+local tostring = tostring
+local tonumber = tonumber
+local setmetatable = setmetatable
+local getmetatable = getmetatable
+local print=print
+local pairs = pairs
+local table=table
+local texio=texio
+
+do
+local _ENV = pgfplots
+---------------------------------------
+--
+
+log=texio.write_nl
+
+local stringfind = string.find
+local stringsub = string.sub
+local tableinsert = table.insert
+
+-- Splits 'str' at delimiter and returns a table of strings
+function stringsplit( str, delimiter )
+ if not str or not delimiter then error("arguments must not be nil") end
+ local result = { }
+ local start = 1
+ local findStart, findEnd = stringfind( str, delimiter, start )
+ while findStart do
+ tableinsert( result, stringsub( str, start, findStart-1 ) )
+ start = findEnd + 1
+ findStart, findEnd = stringfind( str, delimiter, start )
+ end
+ tableinsert( result, stringsub( str, start ) )
+ return result
+end
+
+function stringOrDefault(str, default)
+ if str == nil or type(str) == 'string' and string.len(str) == 0 then
+ return default
+ end
+ return tostring(str)
+end
+
+
+pgfplotsmath = {}
+
+function pgfplotsmath.isfinite(x)
+ if pgfplotsmath.isnan(x) or x == pgfplotsmath.infty or x == -pgfplotsmath.infty then
+ return false
+ end
+ return true
+end
+
+local isnan = function(x)
+ return x ~= x
+end
+
+pgfplotsmath.isnan = isnan
+
+local infty = 1/0
+pgfplotsmath.infty = infty
+
+local nan = math.sqrt(-1)
+pgfplotsmath.nan = nan
+
+---------------------------------------
+--
+
+
+-- Creates and returns a new class object.
+--
+-- Usage:
+-- complexclass = newClass()
+-- function complexclass:constructor()
+-- self.re = 0
+-- self.im = 0
+-- end
+--
+-- instance = complexclass.new()
+--
+function newClass()
+ local result = {}
+
+ -- we need this such that *instances* (which will have 'result' as meta table)
+ -- will "inherit" the class'es methods.
+ result.__index = result
+ local allocator= function (...)
+ local self = setmetatable({}, result)
+ self:constructor(...)
+ return self
+ end
+ result.new = allocator
+ return result
+end
+
+
+
+-- Create a new class that inherits from a base class
+--
+-- base = pgfplots.newClass()
+-- function base:constructor()
+-- self.variable= 'a'
+-- end
+--
+-- sub = pgfplots.newClassExtends(base)
+-- function sub:constructor()
+-- -- call super constructor.
+-- -- it is ABSOLUTELY CRUCIAL to use <baseclass>.constructor here - not :constructor!
+-- base.constructor(self)
+-- end
+--
+-- instance = base.new()
+--
+-- instance2 = sub.new()
+--
+-- @see newClass
+function newClassExtends( baseClass )
+ if not baseClass then error "baseClass must not be nil" end
+
+ local new_class = newClass()
+
+ -- The following is the key to implementing inheritance:
+
+ -- The __index member of the new class's metatable references the
+ -- base class. This implies that all methods of the base class will
+ -- be exposed to the sub-class, and that the sub-class can override
+ -- any of these methods.
+ --
+ local mt = {} -- getmetatable(new_class)
+ mt.__index = baseClass
+ setmetatable(new_class,mt)
+
+ return new_class
+end
+
+
+end
diff --git a/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/plothandler.lua b/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/plothandler.lua
new file mode 100644
index 00000000000..e3fc089e25d
--- /dev/null
+++ b/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/plothandler.lua
@@ -0,0 +1,962 @@
+--
+-- This file contains parts of pgfplotscoordprocessing.code.tex and pgfplotsplothandlers.code.tex .
+--
+-- It contains
+--
+-- pgfplots.Axis
+-- pgfplots.Coord
+-- pgfplots.Plothandler
+--
+-- and some related classes.
+
+local math=math
+local pgfplotsmath = pgfplots.pgfplotsmath
+local type=type
+local tostring=tostring
+local error=error
+local table=table
+local pgfmathparse = pgfplots.pgfluamathparser.pgfmathparse
+
+do
+-- all globals will be read from/defined in pgfplots:
+local _ENV = pgfplots
+-----------------------------------
+
+local pgftonumber =pgfluamathfunctions.tonumber
+
+Coord = newClass()
+
+function Coord:constructor()
+ self.x = { nil, nil, nil }
+ self.unboundedDir = nil
+ self.meta= nil
+ self.metatransformed = nil -- assigned during vis phase only
+ self.unfiltered = nil
+ self.pgfXY = nil -- assigned during visphase only
+ return self
+end
+
+function Coord:copy(other)
+ for i = 1,#other.x do self.x[i] = other.x[i] end
+ self.meta = other.meta
+ self.metatransformed = other.metatransformed
+ self.unfiltered = nil -- not needed
+end
+
+function Coord:__tostring()
+ local result = '(' .. stringOrDefault(self.x[1], "--") ..
+ ',' .. stringOrDefault(self.x[2], "--") ..
+ ',' .. stringOrDefault(self.x[3], "--") ..
+ ') [' .. stringOrDefault(self.meta, "--") .. ']'
+
+ if not self.x[1] and self.unfiltered then
+ result = result .. "(was " .. tostring(self.unfiltered) .. ")"
+ end
+ return result
+end
+
+local stringToFunctionMap = pgfluamathfunctions.stringToFunctionMap
+
+-- a reference to a Coord which is returned by math expressions involving 'x', 'y', or 'z'
+-- see surveystart()
+local pseudoconstant_pt = nil
+local function pseudoconstant_x() return pseudoconstant_pt.x[1] end
+local function pseudoconstant_y() return pseudoconstant_pt.x[2] end
+local function pseudoconstant_z() return pseudoconstant_pt.x[3] end
+local function pseudoconstant_rawx() return pgftonumber(pseudoconstant_pt.unfiltered.x[1]) end
+local function pseudoconstant_rawy() return pgftonumber(pseudoconstant_pt.unfiltered.x[2]) end
+local function pseudoconstant_rawz() return pgftonumber(pseudoconstant_pt.unfiltered.x[3]) end
+local function pseudoconstant_meta() return pseudoconstant_pt.meta end
+
+-- @return the old value
+local function updatePseudoConstants(pt)
+ local old = pseudoconstant_pt
+ pseudoconstant_pt = pt
+ return old
+end
+
+-------------------------------------------------------
+
+LinearMap = newClass()
+
+-- A map such that
+-- [inMin,inMax] is mapped linearly to [outMin,outMax]
+--
+function LinearMap:constructor(inMin, inMax, outMin, outMax)
+ if not inMin or not inMax or not outMin or not outMax then error("arguments must not be nil") end
+ if inMin == inMax then
+ self.map = function (x) return inMin end
+ else
+ if inMin > inMax then error("linear map received invalid input domain " .. tostring(inMin) .. ":" .. tostring(inMax)) end
+ self.offset = outMin - (outMax-outMin)*inMin/(inMax-inMin)
+ self.scale = (outMax-outMin)/(inMax-inMin)
+ end
+end
+
+function LinearMap:map(x)
+ return x*self.scale + self.offset
+end
+
+PointMetaMap = newClass()
+
+function PointMetaMap:constructor(inMin,inMax, warnForfilterDiscards)
+ if not inMin or not inMax or warnForfilterDiscards == nil then error("arguments must not be nil") end
+ self._mapper = LinearMap.new(inMin,inMax, 0, 1000)
+ self.warnForfilterDiscards = warnForfilterDiscards
+end
+
+function PointMetaMap:map(meta)
+ if pgfplotsmath.isfinite(meta) then
+ local result = self._mapper:map(meta)
+ result = math.max(0, result)
+ result = math.min(1000, result)
+ return result
+ else
+ if self.warnForfilterDiscards then
+ log("The per point meta data '" .. tostring(meta) .. " (and probably others as well) is unbounded - using the minimum value instead.\n")
+ self.warnForfilterDiscards=false
+ end
+ return 0
+ end
+end
+
+
+
+-------------------------------------------------------
+
+-- Abstract base class of all plot handlers.
+-- It offers basic functionality for the survey phase.
+Plothandler = newClass()
+
+-- @param name the plot handler's name (a string)
+-- @param axis the parent axis
+-- @param pointmetainputhandler an instance of PointMetaHandler or nil if there is none
+function Plothandler:constructor(name, axis, pointmetainputhandler)
+ if not name or not axis then
+ error("arguments must not be nil")
+ end
+ self.axis = axis
+ self.config = PlothandlerConfig.new()
+ self.name = name
+ self.coordindex = 0
+ self.metamin = math.huge
+ self.metamax = -math.huge
+ self.autocomputeMetaMin = true
+ self.autocomputeMetaMax = true
+ self.coords = {}
+ self.pointmetainputhandler = pointmetainputhandler
+ self.pointmetamap = nil -- will be set later
+ self.filteredCoordsAway = false
+ self.plotHasJumps = false
+ -- will be set before the visualization phase starts. At least.
+ self.plotIs3d = false
+ return self
+end
+
+function Plothandler:__tostring()
+ return 'plot handler ' .. self.name
+end
+
+-- @see \pgfplotsplothandlersurveybeforesetpointmeta
+function Plothandler:surveyBeforeSetPointMeta()
+end
+
+-- @see \pgfplotsplothandlersurveyaftersetpointmeta
+function Plothandler:surveyAfterSetPointMeta()
+end
+
+-- PRIVATE
+--
+-- appends a fully surveyed point
+function Plothandler:addSurveyedPoint(pt)
+ table.insert(self.coords, pt)
+ -- log("addSurveyedPoint(" .. tostring(pt) .. ") ...\n")
+end
+
+-- PRIVATE
+--
+-- assigns the point meta value by means of the PointMetaHandler
+function Plothandler:setperpointmeta(pt)
+ if pt.meta == nil and self.pointmetainputhandler ~= nil then
+ self.pointmetainputhandler:assign(pt)
+ end
+end
+
+-- PRIVATE
+--
+-- updates point meta limits
+function Plothandler:setperpointmetalimits(pt)
+ if pt.meta ~= nil then
+ if not type(pt.meta) == 'number' then error("got unparsed input "..tostring(pt)) end
+ if self.autocomputeMetaMin then
+ self.metamin = math.min(self.metamin, pt.meta )
+ end
+
+ if self.autocomputeMetaMax then
+ self.metamax = math.max(self.metamax, pt.meta )
+ end
+ end
+end
+
+-- @see \pgfplotsplothandlersurveystart
+function Plothandler:surveystart()
+ stringToFunctionMap["x"] = pseudoconstant_x
+ stringToFunctionMap["y"] = pseudoconstant_y
+ stringToFunctionMap["z"] = pseudoconstant_z
+ stringToFunctionMap["rawx"] = pseudoconstant_rawx
+ stringToFunctionMap["rawy"] = pseudoconstant_rawy
+ stringToFunctionMap["rawz"] = pseudoconstant_rawz
+ stringToFunctionMap["meta"] = pseudoconstant_meta
+end
+
+-- @see \pgfplotsplothandlersurveyend
+-- returns executable TeX code to communicate return values.
+function Plothandler:surveyend()
+ -- empty by default.
+ return ""
+end
+
+-- @see \pgfplotsplothandlersurveypoint
+function Plothandler:surveypoint(pt)
+ updatePseudoConstants(nil)
+
+ local updateLimits = self.config.updateLimits
+ local current = self.axis:parsecoordinate(pt, self.config.filterExpressionByDir)
+
+ -- this here defines the math functions for x, y, or z.
+ -- FIXME: are there any hidden callers which rely on these constants in parsecoordinate!?
+ updatePseudoConstants(current)
+
+ if current.x[1] ~= nil then
+ current = self.axis:preparecoordinate(current)
+ if updateLimits then
+ self.axis:updatelimitsforcoordinate(current)
+ end
+ end
+ self.axis:datapointsurveyed(current, self)
+
+ self.coordindex = self.coordindex + 1;
+end
+
+-- PUBLIC
+--
+-- @return a string containing all surveyed coordinates in the format which is accepted \pgfplotsaxisdeserializedatapointfrom
+function Plothandler:surveyedCoordsToPgfplots(axis)
+ return self:getCoordsInTeXFormat(axis, self.coords)
+end
+
+-- PUBLIC
+--
+-- @return a string containing all coordinates in the format which is accepted \pgfplotsaxisdeserializedatapointfrom
+-- @param extraSerializer a function which takes an instance of Coord and returns a string. can be nil.
+function Plothandler:getCoordsInTeXFormat(axis, coords, extraSerializer)
+ if not axis then error("arguments must not be nil") end
+ local result = {}
+ for i = 1,#coords,1 do
+ local pt = coords[i]
+ local ptstr = self:serializeCoordToPgfplots(pt)
+ local axisPrivate = axis:serializeCoordToPgfplotsPrivate(pt)
+ if extraSerializer then
+ axisPrivate = extraSerializer(pt) .. "{" .. axisPrivate .. "}"
+ end
+ local serialized = "{" .. axisPrivate .. ";" .. ptstr .. "}"
+ table.insert(result, serialized)
+ end
+ return table.concat(result)
+end
+
+-- PRIVATE
+--
+-- does the same as \pgfplotsplothandlerserializepointto
+function Plothandler:serializeCoordToPgfplots(pt)
+ return
+ toTeXstring(pt.x[1]) .. "," ..
+ toTeXstring(pt.x[2]) .. "," ..
+ toTeXstring(pt.x[3])
+end
+
+function Plothandler:visualizationPhaseInit()
+ if self.pointmetainputhandler ~=nil then
+ local rangeMin
+ local rangeMax
+ if self.config.pointmetarel == PointMetaRel.axiswide then
+ rangeMin = self.axis.axiswidemetamin
+ rangeMax = self.axis.axiswidemetamax
+ else
+ rangeMin = self.metamin
+ rangeMax = self.metamax
+ end
+ self.pointmetamap = PointMetaMap.new(rangeMin, rangeMax, self.config.warnForfilterDiscards)
+ end
+end
+
+-- PRECONDITION: visualizationPhaseInit() has been called some time before.
+function Plothandler:visualizationTransformMeta(meta)
+ if meta == nil then
+ log("could not access the 'point meta' (used for example by scatter plots and color maps). Maybe you need to add '\\addplot[point meta=y]' or something like that?\n")
+ return 1
+ else
+ return self.pointmetamap:map(meta)
+ end
+end
+
+-- Modifies coords inplace.
+-- @return nothing.
+-- see \pgfplots@apply@zbuffer@sort@coordinates
+function Plothandler:sortCoordinatesByViewDepth()
+ local coords = self.coords
+
+ local axis = self.axis
+ local viewdir = axis.viewdir
+
+ -- Step 1: compute view depth for every coordinate
+ local getVertexDepth = axis.getVertexDepth
+ for i=1,#coords do
+ local vertexDepth = getVertexDepth(axis,coords[i])
+ coords[i].vertexDepth = vertexDepth
+ end
+
+ -- Step 2: sort (inplace)
+ local comparator = function(ptA, ptB)
+ return ptA.vertexDepth > ptB.vertexDepth
+ end
+ table.sort(coords, comparator)
+
+ -- Step 3: cleanup: do not leave 'vertexDepth' inside of the array
+ for i=1,#coords do
+ coords[i].vertexDepth = nil
+ end
+end
+
+-------------------------------------------------------
+-- Generic plot handler: one which has the default survey phase
+-- It is actually the same as Plothandler...
+
+GenericPlothandler = newClassExtends(Plothandler)
+
+function GenericPlothandler:constructor(name, axis, pointmetainputhandler)
+ Plothandler.constructor(self,name, axis, pointmetainputhandler)
+end
+
+
+-------------------------------------------------------
+
+UnboundedCoords = { discard="d", jump="j" }
+
+PointMetaRel = { axiswide = 0, perplot =1 }
+
+
+-- contains static configuration entities.
+PlothandlerConfig = newClass()
+
+function PlothandlerConfig:constructor()
+ self.unboundedCoords = UnboundedCoords.discard
+ self.warnForfilterDiscards=true
+ self.pointmetarel = PointMetaRel.axiswide
+ self.updateLimits = true
+ self.filterExpressionByDir = {"", "", ""}
+ return self
+end
+
+-------------------------------------------------------
+-- a PlotVisualizer takes an input Plothandler and visualizes its results.
+--
+-- "Visualize" can mean
+-- * apply the plot handler's default visualization phase
+-- * visualize just plot marks at each of the collected coordinates
+-- * visualize just error bars at each collected coordinate
+-- * ...
+--
+
+-- this class offers basic visualization support. "Basic" means that it will merely transform and finalize input coordinates.
+PlotVisualizer = newClass()
+-- @param sourcePlotHandler an instance of Plothandler
+function PlotVisualizer:constructor(sourcePlotHandler)
+ if not sourcePlotHandler then error("arguments must not be nil") end
+ self.axis = sourcePlotHandler.axis
+ self.sourcePlotHandler=sourcePlotHandler
+ if sourcePlotHandler.plotIs3d then
+ self.qpointxyz = self.axis.qpointxyz
+ else
+ self.qpointxyz = self.axis.qpointxy
+ end
+end
+
+-- Visualizes the results.
+--
+-- @return any results. The format of the results is currently a list of Coord, but I am unsure of whether it will stay this way.
+--
+-- Note that a PlotVisualizer does _not_ modify self.sourcePlotHandler.coords
+function PlotVisualizer:getVisualizationOutput()
+ local result = {}
+ local coords = self.sourcePlotHandler.coords
+
+ -- standard z buffer choices (not mesh + sort) is currently handled in TeX
+ -- as well as other preparations
+
+ -- FIXME : stacked plots?
+ -- FIXME : error bars?
+
+ for i = 1,#coords do
+ local result_i
+ local result_i = Coord.new()
+ result_i:copy(coords[i])
+
+ if result_i.x[1] ~= nil then
+ self:visphasegetpoint(result_i)
+ else
+ self:notifyJump(result_i)
+ end
+
+ result[i] = result_i
+ end
+
+ return result
+end
+
+-- PROTECTED
+-- resembles \pgfplotsplothandlervisualizejump -- or at least that part which can be done in LUA.
+-- It does not visualize anything, but it can be used to modify the coordinate
+function PlotVisualizer:notifyJump(pt)
+ -- do nothing.
+end
+
+function PlotVisualizer:visphasegetpoint(pt)
+ pt.untransformed = {}
+ for j = 1,#pt.x do
+ pt.untransformed[j] = pt.x[j]
+ end
+
+ self.axis:visphasetransformcoordinate(pt)
+
+ -- FIXME : prepare data point (only for stacked)
+
+ pt.pgfXY = self.qpointxyz(pt.x)
+end
+
+
+
+-------------------------------------------------------
+
+-- An abstract base class for a handler of point meta.
+-- @see \pgfplotsdeclarepointmetasource
+PointMetaHandler = newClass()
+
+-- @param isSymbolic
+-- expands to either '1' or '0'
+-- A numeric source will be processed numerically in float
+-- arithmetics. Thus, the output of the @assign routine should be
+-- a macro \pgfplots@current@point@meta in float format.
+--
+-- The output of a numeric point meta source will result in meta
+-- limit updates and the final map to [0,1000] will be
+-- initialised automatically.
+--
+-- A symbolic input routine won't be processed.
+-- Default is '0'
+--
+-- @param explicitInput
+-- expands to either
+-- '1' or '0'. In case '1', it expects explicit input from the
+-- coordinate input routines. For example, 'plot file' will look for
+-- further input after the x,y,z coordinates.
+-- Default is '0'
+--
+function PointMetaHandler:constructor(isSymbolic, explicitInput)
+ self.isSymbolic =isSymbolic
+ self.explicitInput = explicitInput
+ return self
+end
+
+-- During the survey phase, this macro is expected to assign
+-- \pgfplots@current@point@meta
+-- if it is a numeric input method, it should return a
+-- floating point number.
+-- It is allowed to return an empty string to say "there is no point
+-- meta".
+-- PRECONDITION for '@assign':
+-- - the coordinate input method has already assigned its
+-- '\pgfplots@current@point@meta' (probably as raw input string).
+-- - the other input coordinates are already read.
+-- POSTCONDITION for '@assign':
+-- - \pgfplots@current@point@meta is ready for use:
+-- - EITHER a parsed floating point number
+-- - OR an empty string,
+-- - OR a symbolic string (if the issymbolic boolean is true)
+-- The default implementation is
+-- \let\pgfplots@current@point@meta=\pgfutil@empty
+--
+function PointMetaHandler.assign(pt)
+ error("This instance of PointMetaHandler is not implemented")
+end
+
+
+-- A PointMetaHandler which merely acquires values of either x,y, or z.
+CoordAssignmentPointMetaHandler = newClassExtends( PointMetaHandler )
+function CoordAssignmentPointMetaHandler:constructor(dir)
+ PointMetaHandler.constructor(self, false,false)
+ if not dir then error "nil argument for 'dir' is unsupported." end
+ self.dir=dir
+end
+
+function CoordAssignmentPointMetaHandler:assign(pt)
+ if not pt then error("arguments must not be nil") end
+ pt.meta = pgftonumber(pt.x[self.dir])
+end
+
+XcoordAssignmentPointMetaHandler = CoordAssignmentPointMetaHandler.new(1)
+YcoordAssignmentPointMetaHandler = CoordAssignmentPointMetaHandler.new(2)
+ZcoordAssignmentPointMetaHandler = CoordAssignmentPointMetaHandler.new(3)
+
+-- A class of PointMetaHandler which takes the 'Coord.meta' as input
+ExplicitPointMetaHandler = newClassExtends( PointMetaHandler )
+function ExplicitPointMetaHandler:constructor()
+ PointMetaHandler.constructor(self, false,true)
+end
+
+function ExplicitPointMetaHandler:assign(pt)
+ if pt.unfiltered ~= nil and pt.unfiltered.meta ~= nil then
+ pt.meta = pgftonumber(pt.unfiltered.meta)
+ end
+end
+
+-- a point meta handler which evaluates a math expression.
+-- ATTENTION: the expression cannot depend on TeX macro values
+ExpressionPointMetaHandler = newClassExtends( PointMetaHandler )
+
+-- @param expression an expression. It can rely on functions which are only available in plot context (in plot expression, x and y are typically defined)
+function ExpressionPointMetaHandler:constructor(expression)
+ PointMetaHandler.constructor(self, false,false)
+ self.expression = expression
+end
+
+function ExpressionPointMetaHandler:assign(pt)
+ pt.meta = pgfmathparse(self.expression)
+ if not pt.meta then
+ error("point meta=" .. self.expression .. ": expression has been rejected.")
+ end
+end
+
+
+-------------------------------------------------------
+
+DatascaleTrafo = newClass()
+
+function DatascaleTrafo:constructor(exponent, shift)
+ self.exponent=exponent
+ self.shift=shift
+ self.scale = math.pow(10, exponent)
+end
+
+function DatascaleTrafo:map(x)
+ return self.scale * x - self.shift
+end
+
+
+-------------------------------------------------------
+
+-- An axis.
+Axis = newClass()
+
+function Axis:constructor()
+ self.is3d = false
+ self.clipLimits = true
+ self.autocomputeAllLimits = true -- FIXME : redundant!?
+ self.autocomputeMin = { true, true, true }
+ self.autocomputeMax = { true, true, true }
+ self.isLinear = { true, true, true }
+ self.min = { math.huge, math.huge, math.huge }
+ self.max = { -math.huge, -math.huge, -math.huge }
+ self.datamin = { math.huge, math.huge, math.huge }
+ self.datamax = { -math.huge, -math.huge, -math.huge }
+ self.axiswidemetamin = { math.huge, math.huge }
+ self.axiswidemetamax = { -math.huge, -math.huge }
+ -- will be populated by the TeX code:
+ self.plothandlers = {}
+ -- needed during visualization phase:
+ self.datascaleTrafo={}
+ -- needed during visualization phase: a vector of 3 elements, each is a vector of 2 elements.
+ -- self.unitvectors[1] is (\pgf@xx,\pgf@xy)
+ self.unitvectors={}
+ -- needed during visualization phase -- but only for 3d!
+ self.viewdir = {}
+ return self
+end
+
+function Axis:getVertexDepth(pt)
+ local vertexDepth = 0
+ local vertex = pt.x
+ local viewdir = self.viewdir
+ if vertex[1] == nil then
+ -- an empty coordinate. Get rid of it.
+ return 0
+ end
+
+ if #vertex ~=3 then
+ error("Cannot compute vertex depth of " .. tostring(pt) .. ": expected a 3d point but got " .. tostring(#vertex))
+ end
+ if not viewdir or #viewdir~=3 then error("got unexpected view dir " ..tostring(viewdir) ) end
+
+ for k = 1,3 do
+ local component = vertex[k]
+ vertexDepth = vertexDepth + component*viewdir[k]
+ end
+
+ return vertexDepth
+end
+
+function Axis:setunitvectors(unitvectors)
+ if not unitvectors or #unitvectors ~= 3 then error("got illegal arguments " .. tostring(unitvectors)) end
+ self.unitvectors = unitvectors
+
+ local pgfxx = unitvectors[1][1]
+ local pgfxy = unitvectors[1][2]
+ local pgfyx = unitvectors[2][1]
+ local pgfyy = unitvectors[2][2]
+ local pgfzx = unitvectors[3][1]
+ local pgfzy = unitvectors[3][2]
+
+ self.qpointxyz = function(xyz)
+ local result = {}
+ result[1] = xyz[1] * pgfxx + xyz[2] * pgfyx + xyz[3] * pgfzx
+ result[2] = xyz[1] * pgfxy + xyz[2] * pgfyy + xyz[3] * pgfzy
+ return result
+ end
+
+ if pgfxy==0 and pgfyx ==0 then
+ self.qpointxy = function(xy)
+ local result = {}
+ result[1] = xy[1] * pgfxx
+ result[2] = xy[2] * pgfyy
+ return result
+ end
+ else
+ self.qpointxy = function(xyz)
+ local result = {}
+ result[1] = xyz[1] * pgfxx + xyz[2] * pgfyx
+ result[2] = xyz[1] * pgfxy + xyz[2] * pgfyy
+ return result
+ end
+ end
+end
+
+-- PRIVATE
+--
+-- applies user transformations and logs
+--
+-- @see \pgfplots@prepared@xcoord
+function Axis:preparecoord(dir, value)
+ -- FIXME : user trafos, logs (switches off LUA backend)
+ return value
+end
+
+function Axis:filtercoord(dir, ptCoords, filterExpressionByDir)
+ if not dir or not ptCoords or not filterExpressionByDir then error("Arguments must not be nil") end
+ local result = ptCoords.x[dir]
+ if filterExpressionByDir[dir]:len() > 0 then
+
+ for j = 1,#ptCoords.x do
+ ptCoords.x[j] = pgftonumber(ptCoords.x[j])
+ end
+ local old = updatePseudoConstants(ptCoords)
+
+ result = pgfmathparse(filterExpressionByDir[dir])
+
+ updatePseudoConstants(old)
+ end
+ return result
+end
+
+-- PRIVATE
+-- @see \pgfplotsaxisserializedatapoint@private
+function Axis:serializeCoordToPgfplotsPrivate(pt)
+ return toTeXstring(pt.meta)
+end
+
+
+-- PRIVATE
+--
+function Axis:validatecoord(dir, point)
+ if not dir or not point then error("arguments must not be nil") end
+ local result = pgftonumber(point.x[dir])
+
+ if result == nil then
+ result = nil
+ elseif result == pgfplotsmath.infty or result == -pgfplotsmath.infty or pgfplotsmath.isnan(result) then
+ result = nil
+ point.unboundedDir = dir
+ end
+
+ point.x[dir] = result
+end
+
+-- PRIVATE
+--
+-- @see \pgfplotsaxisparsecoordinate
+function Axis:parsecoordinate(pt, filterExpressionByDir)
+ -- replace empty strings by 'nil':
+ for i = 1,3 do
+ pt.x[i] = stringOrDefault(pt.x[i], nil)
+ end
+ pt.meta = stringOrDefault(pt.meta)
+
+ if pt.x[3] ~= nil then
+ self.is3d = true
+ end
+
+ local result = Coord.new()
+
+ local unfiltered = Coord.new()
+ unfiltered.x = {}
+ unfiltered.meta = pt.meta
+ for i = 1,3 do
+ unfiltered.x[i] = pt.x[i]
+ end
+ result.unfiltered = unfiltered
+
+ -- copy values such that filtercoord can access them in the same order as the TeX impl.
+ for i = 1,self:loopMax() do
+ result.x[i] = pt.x[i]
+ end
+
+ -- FIXME : self:prefilter(pt[i])
+ for i = 1,self:loopMax() do
+ result.x[i] = self:preparecoord(i, pt.x[i])
+ result.x[i] = self:filtercoord(i, result, filterExpressionByDir)
+ end
+ -- FIXME : result.x = self:xyzfilter(result.x)
+
+ for i = 1,self:loopMax() do
+ self:validatecoord(i, result)
+ end
+
+ local resultIsBounded = true
+ for i = 1,self:loopMax() do
+ if result.x[i] == nil then
+ resultIsBounded = false
+ end
+ end
+
+ if not resultIsBounded then
+ result.x = { nil, nil, nil}
+ end
+
+ return result
+end
+
+-- PROTECTED
+--
+-- @see \pgfplotsaxispreparecoordinate
+function Axis:preparecoordinate(pt)
+ -- the default "preparation" is to return it as is (no number parsing)
+ --
+ -- FIXME : data cs! Stacking!
+ return pt
+end
+
+-- PRIVATE
+--
+-- returns either 2 if this axis is 2d or 3 otherwise
+--
+-- FIXME : shouldn't this depend on the current plot handler!?
+function Axis:loopMax()
+ if self.is3d then return 3 else return 2 end
+end
+
+-- PRIVATE:
+--
+-- updates axis limits for pt
+-- @param pt an instance of Coord
+function Axis:updatelimitsforcoordinate(pt)
+ local isClipped = false
+ if self.clipLimits then
+ for i = 1,self:loopMax(),1 do
+ if not self.autocomputeMin[i] then
+ isClipped = isClipped or pt.x[i] < self.min[i]
+ end
+ if not self.autocomputeMax[i] then
+ isClipped = isClipped or pt.x[i] > self.max[i]
+ end
+ end
+ end
+
+ if not isClipped then
+ for i = 1,self:loopMax(),1 do
+ if self.autocomputeMin[i] then
+ self.min[i] = math.min(pt.x[i], self.min[i])
+ end
+
+ if self.autocomputeMax[i] then
+ self.max[i] = math.max(pt.x[i], self.max[i])
+ end
+ end
+ end
+
+ -- Compute data range:
+ if self.autocomputeAllLimits then
+ -- the data range will be acquired simply from the axis
+ -- range, see below!
+ else
+ for i = 1,self:loopMax(),1 do
+ self.datamin[i] = math.min(pt.x[i], self.min[i])
+ self.datamax[i] = math.max(pt.x[i], self.max[i])
+ end
+ end
+end
+
+-- PRIVATE
+--
+-- unfinished, see its fixmes
+function Axis:addVisualizationDependencies(pt)
+ -- FIXME : 'visualization depends on'
+ -- FIXME : 'execute for finished point'
+ return pt
+end
+
+-- PROTECTED
+--
+-- indicates that a data point has been surveyed by the axis and that it can be consumed
+function Axis:datapointsurveyed(pt, plothandler)
+ if not pt or not plothandler then error("arguments must not be nil") end
+ if pt.x[1] ~= nil then
+ plothandler:surveyBeforeSetPointMeta()
+ plothandler:setperpointmeta(pt)
+ plothandler:setperpointmetalimits(pt)
+ plothandler:surveyAfterSetPointMeta()
+
+ -- FIXME : error bars
+ -- FIXME: collect first plot as tick
+
+ -- note that that TeX code would also remember the first/last coordinate in a stream.
+ -- This is unnecessary here.
+
+ local serialized = self:addVisualizationDependencies(pt)
+ plothandler:addSurveyedPoint(serialized)
+ else
+ -- COORDINATE HAS BEEN FILTERED AWAY
+ if plothandler.config.unboundedCoords == UnboundedCoords.discard then
+ plothandler.filteredCoordsAway = true
+ if plothandler.config.warnForfilterDiscards then
+ local reason
+ if pt.unboundedDir == nil then
+ reason = "of a coordinate filter."
+ else
+ reason = "it is unbounding (in " .. tostring(pt.unboundedDir) .. ")."
+ end
+ log("NOTE: coordinate " .. tostring(pt) .. " has been dropped because " .. reason .. "\n")
+ end
+ elseif plothandler.config.unboundedCoords == UnboundedCoords.jump then
+ if pt.unboundedDir == nil then
+ plothandler.filteredCoordsAway = true
+ if plothandler.config.warnForfilterDiscards then
+ local reason = "of a coordinate filter."
+ log("NOTE: coordinate " .. tostring(pt) .. " has been dropped because " .. reason .. "\n")
+ end
+ else
+ plothandler.plotHasJumps = true
+
+ local serialized = self:addVisualizationDependencies(pt)
+ plothandler:addSurveyedPoint(serialized)
+ end
+ end
+ end
+
+ -- note that the TeX variant would increase the coord index here.
+ -- We do it it surveypoint.
+end
+
+local function axisLimitToTeXString(name, value)
+ if value == math.huge or value == -math.huge then
+ return ""
+ end
+ return "\\gdef" .. name .. "{" .. toTeXstring(value) .. "}"
+end
+
+local function toTeXxyzCoord(namePrefix, pt )
+ local x = toTeXstring(pt.x[1])
+ local y = toTeXstring(pt.x[2])
+ local z = toTeXstring(pt.x[3])
+ return
+ "\\gdef" .. namePrefix .. "@x{" .. x .. "}" ..
+ "\\gdef" .. namePrefix .. "@y{" .. y .. "}" ..
+ "\\gdef" .. namePrefix .. "@z{" .. z .. "}";
+end
+
+local function findFirstValidCoord(coords)
+ for i=1,#coords do
+ local pt = coords[i]
+ if pt.x[1] ~=nil then
+ return pt
+ end
+ end
+ return nil
+end
+
+local function findLastValidCoord(coords)
+ for i=#coords,1,-1 do
+ local pt = coords[i]
+ if pt.x[1] ~=nil then
+ return pt
+ end
+ end
+ return nil
+end
+
+-- PUBLIC
+--
+-- @return a set of (private) key-value pairs such that the TeX code of pgfplots can
+-- access survey results of the provided plot handler
+--
+-- @param plothandler an instance of Plothandler
+function Axis:surveyToPgfplots(plothandler)
+ local plothandlerResult = plothandler:surveyend()
+ local firstCoord = findFirstValidCoord(plothandler.coords) or Coord.new()
+ local lastCoord = findLastValidCoord(plothandler.coords) or Coord.new()
+
+ local result =
+ plothandlerResult ..
+ toTeXxyzCoord("\\pgfplots@currentplot@firstcoord", firstCoord) ..
+ toTeXxyzCoord("\\pgfplots@currentplot@lastcoord", lastCoord) ..
+ axisLimitToTeXString("\\pgfplots@metamin", plothandler.metamin) ..
+ axisLimitToTeXString("\\pgfplots@metamax", plothandler.metamax) ..
+ "\\c@pgfplots@coordindex=" .. tostring(plothandler.coordindex) .. " " ..
+ axisLimitToTeXString("\\pgfplots@xmin", self.min[1]) ..
+ axisLimitToTeXString("\\pgfplots@ymin", self.min[2]) ..
+ axisLimitToTeXString("\\pgfplots@xmax", self.max[1]) ..
+ axisLimitToTeXString("\\pgfplots@ymax", self.max[2]);
+ if self.is3d then
+ result = result ..
+ axisLimitToTeXString("\\pgfplots@zmin", self.min[3]) ..
+ axisLimitToTeXString("\\pgfplots@zmax", self.max[3]) ..
+ "\\global\\pgfplots@threedimtrue ";
+ end
+ if plothandler.plotHasJumps then
+ result = result ..
+ "\\def\\pgfplotsaxisplothasjumps{1}"
+ else
+ result = result ..
+ "\\def\\pgfplotsaxisplothasjumps{0}"
+ end
+ if plothandler.filteredCoordsAway then
+ result = result ..
+ "\\def\\pgfplotsaxisfilteredcoordsaway{1}"
+ else
+ result = result ..
+ "\\def\\pgfplotsaxisfilteredcoordsaway{0}"
+ end
+
+ return result
+end
+
+-- resembles \pgfplotsaxisvisphasetransformcoordinate
+function Axis:visphasetransformcoordinate(pt)
+ for i = 1,#pt.x do
+ pt.x[i] = self.datascaleTrafo[i]:map( pt.x[i] )
+ end
+end
+
+-- will be set by TeX code (in \begin{axis})
+gca = nil
+
+
+end
diff --git a/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/statistics.lua b/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/statistics.lua
new file mode 100644
index 00000000000..57350254b30
--- /dev/null
+++ b/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/statistics.lua
@@ -0,0 +1,400 @@
+-- This file has dependencies to BOTH, the TeX part of pgfplots and the LUA part.
+-- It is the only LUA component with this property.
+--
+-- Its purpose is to encapsulate the communication between TeX and LUA in a central LUA file
+
+local pgfplotsmath = pgfplots.pgfplotsmath
+local error=error
+local table=table
+local string=string
+local tostring=tostring
+local type=type
+local io=io
+local mathfloor=math.floor
+local mathceil=math.ceil
+local pgfmathparse = pgfplots.pgfluamathparser.pgfmathparse
+
+do
+-- all globals will be read from/defined in pgfplots:
+local _ENV = pgfplots
+
+local pgftonumber =pgfluamathfunctions.tonumber
+
+function texBoxPlotSurveyPoint(data)
+ gca.currentPlotHandler:semiSurveyedValue(data)
+end
+
+-------------------------------------------------------
+
+PercentileEstimator = newClass()
+
+function PercentileEstimator:constructor()
+end
+
+function PercentileEstimator:getIndex(data, i)
+ local idx = i
+ if idx < 1 then idx = 1 end
+ if idx > #data then idx = #data end
+
+ local result = data[idx]
+ if not result then
+ error("Box plot percentile estimator '" .. tostring(self) .." accessed illegal array index " .. tostring(idx) .. " in array of length " .. tostring(#data))
+ end
+ return result
+end
+
+
+-- @param percentile the requested percentile. Use 0.5 for the median, 0.25 for the first quartile, 0.95 for the 95% percentile etc.
+function PercentileEstimator:getValue(percentile, data)
+ error("Use implementation of PercentileEstimator, not interface")
+end
+
+-- LegacyPgfplotsPercentileEstimator is a minimally repaired percentile estimator as it has been shipped with pgfplots.10 .
+-- I decided to mark it as deprecated because it is non-standard and not comparable with other programs.
+LegacyPgfplotsPercentileEstimator = newClassExtends(PercentileEstimator)
+function LegacyPgfplotsPercentileEstimator:constructor()
+end
+function LegacyPgfplotsPercentileEstimator:__tostring()
+ return "estimator=legacy";
+end
+function LegacyPgfplotsPercentileEstimator:getValue(percentile, data)
+ if not percentile or not data then error("Arguments must not be nil") end
+ local numCoords = #data
+ local h = numCoords * percentile
+
+ local offset_low = mathfloor(h)
+ local isInt = ( h==offset_low )
+
+ local offset_high = offset_low+1
+
+ local x_low = self:getIndex(data, offset_low)
+ local x_up = self:getIndex(data, offset_high)
+ local res = x_low
+ if not isInt then
+ res = 0.5 * (res + x_up)
+ end
+ return res
+end
+
+-- LegacyBadPgfplotsPercentileEstimator is _the_ percentile estimator as it has been shipped with pgfplots 1.10.
+-- It has bugs and is non-standard. Don't use it.
+LegacyBadPgfplotsPercentileEstimator = newClassExtends(PercentileEstimator)
+function LegacyBadPgfplotsPercentileEstimator:constructor()
+end
+function LegacyBadPgfplotsPercentileEstimator:__tostring()
+ return "estimator=legacy*";
+end
+function LegacyBadPgfplotsPercentileEstimator:getValue(percentile, data)
+ if not percentile or not data then error("Arguments must not be nil") end
+ local numCoords = #data
+ local h = (numCoords-1) * percentile
+
+ local offset_low = mathfloor(h)
+ local isInt = ( h==offset_low )
+
+ local offset_high = offset_low+1
+
+ local x_low = self:getIndex(data, offset_low+1)
+ local x_up = self:getIndex(data, offset_high+1)
+ local res = x_low
+ if not isInt then
+ res = 0.5 * (res + x_up)
+ end
+ return res
+end
+----------------
+
+ParameterizedPercentileEstimator = newClassExtends(PercentileEstimator)
+
+function ParameterizedPercentileEstimator:__tostring()
+ return "estimator=" .. tostring(self.typeFlag) ;
+end
+
+function ParameterizedPercentileEstimator:constructor( typeFlag )
+ -- http://en.wikipedia.org/wiki/Quantile
+ self.typeFlag = typeFlag
+
+ local getIndex = self.getIndex
+
+ local stdLookup = function(data, h )
+ local h_low = mathfloor(h)
+ local x_low = getIndex(self, data, h_low )
+ local x_up = getIndex(self, data, h_low +1 )
+ return x_low + (h - h_low) * (x_up - x_low)
+ end
+
+ if typeFlag == 1 then
+ -- R1
+ self.getValue = function(self, percentile, data)
+ local h= #data * percentile
+ return getIndex(self, data, mathceil(h) )
+ end
+ elseif typeFlag == 2 then
+ -- R2
+ self.getValue = function(self, percentile, data)
+ local h= #data * percentile + 0.5
+ return 0.5*(getIndex(self, data, mathceil(h-0.5)) + getIndex(self, data, mathfloor(h+0.5) ) )
+ end
+ elseif typeFlag == 3 then
+ -- R3
+ self.getValue = function(self, percentile, data)
+ local h= #data * percentile
+ return getIndex(self, data, pgfluamathfunctions.round(h) )
+ end
+ elseif typeFlag == 4 then
+ -- R4
+ self.getValue = function(self, percentile, data)
+ local h= #data * percentile
+ return stdLookup(data,h)
+ end
+ elseif typeFlag == 5 then
+ -- R5
+ self.getValue = function(self, percentile, data)
+ local h= #data * percentile + 0.5
+ return stdLookup(data,h)
+ end
+ elseif typeFlag == 6 then
+ -- R6
+ self.getValue = function(self, percentile, data)
+ local h= (#data +1) * percentile
+ return stdLookup(data,h)
+ end
+ elseif typeFlag == 7 then
+ -- R7 (Excel)
+ self.getValue = function(self, percentile, data)
+ local h= (#data -1) * percentile + 1
+ return stdLookup(data,h)
+ end
+ elseif typeFlag == 8 then
+ -- R8
+ self.getValue = function(self, percentile, data)
+ local h= (#data + 1/3) * percentile + 1/3
+ return stdLookup(data,h)
+ end
+ elseif typeFlag == 9 then
+ -- R9
+ self.getValue = function(self, percentile, data)
+ local h= (#data + 1/4) * percentile + 3/8
+ return stdLookup(data,h)
+ end
+ else
+ error("Got unsupported type '" .. tostring(typeFlag) .. "'")
+ end
+end
+
+
+getPercentileEstimator = function(estimatorName)
+ if estimatorName == "legacy" then
+ return LegacyPgfplotsPercentileEstimator.new()
+ elseif estimatorName == "legacy*" then
+ return LegacyBadPgfplotsPercentileEstimator.new()
+ elseif estimatorName == "R1" then
+ return ParameterizedPercentileEstimator.new(1)
+ elseif estimatorName == "R2" then
+ return ParameterizedPercentileEstimator.new(2)
+ elseif estimatorName == "R3" then
+ return ParameterizedPercentileEstimator.new(3)
+ elseif estimatorName == "R4" then
+ return ParameterizedPercentileEstimator.new(4)
+ elseif estimatorName == "R5" then
+ return ParameterizedPercentileEstimator.new(5)
+ elseif estimatorName == "R6" then
+ return ParameterizedPercentileEstimator.new(6)
+ elseif estimatorName == "R7" then
+ return ParameterizedPercentileEstimator.new(7)
+ elseif estimatorName == "R8" then
+ return ParameterizedPercentileEstimator.new(8)
+ elseif estimatorName == "R9" then
+ return ParameterizedPercentileEstimator.new(9)
+ end
+
+ error("Unknown estimator '" .. tostring(estimatorName) .. "'")
+end
+
+BoxPlotRequest = newClass()
+
+-- @param lowerQuartialPercent: typically 0.25
+-- @param upperQuartialPercent: typically 0.75
+-- @param whiskerRange: typically 1.5
+-- @param estimator: an instance of PercentileEstimator
+-- @param morePercentiles: either nil or an array of percentiles to compute
+function BoxPlotRequest:constructor(lowerQuartialPercent, upperQuartialPercent, whiskerRange, estimator, morePercentiles)
+ if not lowerQuartialPercent or not upperQuartialPercent or not whiskerRange or not estimator then error("Arguments must not be nil") end
+ self.lowerQuartialPercent = pgftonumber(lowerQuartialPercent)
+ self.upperQuartialPercent = pgftonumber(upperQuartialPercent)
+ self.whiskerRange = pgftonumber(whiskerRange)
+ self.estimator = estimator
+ if not morePercentiles then
+ self.morePercentiles = {}
+ else
+ self.morePercentiles = morePercentiles
+ end
+end
+
+-------------------------------------------------------
+
+BoxPlotResponse = newClass()
+
+function BoxPlotResponse:constructor()
+ self.lowerWhisker = nil
+ self.lowerQuartile = nil
+ self.median = nil
+ self.upperQuartile = nil
+ self.upperWhisker = nil
+ self.average = nil
+ self.morePercentiles = {}
+ self.outliers = {}
+end
+
+-- @param boxPlotRequest an instance of BoxPlotRequest
+-- @param data an indexed array with float values
+-- @return an instance of BoxPlotResponse
+function boxPlotCompute(boxPlotRequest, data)
+ if not boxPlotRequest or not data then error("Arguments must not be nil") end
+
+ for i = 1,#data do
+ local data_i = data[i]
+ if data_i == nil or type(data_i) ~= "number" then
+ error("Illegal input array at index " .. tostring(i) .. ": " .. tostring(data_i))
+ end
+ end
+
+ table.sort(data)
+
+ local sum = 0
+ for i = 1,#data do
+ sum = sum + data[i]
+ end
+
+ local numCoords = #data
+
+ local lowerWhisker
+ local lowerQuartile = boxPlotRequest.estimator:getValue(boxPlotRequest.lowerQuartialPercent, data)
+ local median = boxPlotRequest.estimator:getValue(0.5, data)
+ local upperQuartile = boxPlotRequest.estimator:getValue(boxPlotRequest.upperQuartialPercent, data)
+
+ local morePercentileValues = {}
+ for i = 1,#boxPlotRequest.morePercentiles do
+ morePercentileValues[i] = boxPlotRequest.estimator:getValue(boxPlotRequest.morePercentiles[i], data)
+ end
+
+ local upperWhisker
+ local average = sum / numCoords
+
+ local whiskerRange = boxPlotRequest.whiskerRange
+ local whiskerWidth = whiskerRange*(upperQuartile - lowerQuartile)
+ local upperWhiskerValue = upperQuartile + whiskerWidth
+ local lowerWhiskerValue = lowerQuartile - whiskerWidth
+
+ local outliers = {}
+ for i = 1,numCoords do
+ local current = data[i]
+ if current < lowerWhiskerValue then
+ table.insert(outliers, current)
+ else
+ lowerWhisker = current
+ break
+ end
+ end
+
+ for i = numCoords,1,-1 do
+ local current = data[i]
+ if upperWhiskerValue < current then
+ table.insert(outliers, current)
+ else
+ upperWhisker = current
+ break
+ end
+ end
+
+ local result = BoxPlotResponse.new()
+ result.lowerWhisker = lowerWhisker
+ result.lowerQuartile = lowerQuartile
+ result.median = median
+ result.upperQuartile = upperQuartile
+ result.upperWhisker = upperWhisker
+ result.average = average
+ result.morePercentiles = morePercentileValues
+ result.outliers = outliers
+
+ return result
+end
+
+-------------------------------------------------------
+-- Replicates the survey phase of \pgfplotsplothandlerboxplot
+BoxPlotPlothandler = newClassExtends(Plothandler)
+
+-- drawDirection : either "x" or "y".
+function BoxPlotPlothandler:constructor(boxPlotRequest, drawDirection, drawPosition, axis, pointmetainputhandler)
+ if not boxPlotRequest or not drawDirection or not drawPosition then error("Arguments must not be nil") end
+ Plothandler.constructor(self,"boxplot", axis, pointmetainputhandler)
+ self.boxPlotRequest = boxPlotRequest
+
+ local function evaluateDrawPosition()
+ local result = pgfmathparse(drawPosition)
+ return result
+ end
+
+ if drawDirection == "x" then
+ self.boxplotsetxy = function (a,b) return a,evaluateDrawPosition() + b end
+ elseif drawDirection == "y" then
+ self.boxplotsetxy = function (a,b) return evaluateDrawPosition() + b,a end
+ else
+ error("Illegal argument drawDirection="..tostring(drawDirection) )
+ end
+end
+
+function BoxPlotPlothandler:surveystart()
+ self.boxplotInput = {}
+ self.boxplotSurveyMode = true
+end
+
+
+function BoxPlotPlothandler:surveyend()
+ self.boxplotSurveyMode = false
+
+ local computed = boxPlotCompute( self.boxPlotRequest, self.boxplotInput )
+
+ local texResult =
+ "\\pgfplotsplothandlersurveyend@boxplot@set{lower whisker}{" .. toTeXstring(computed.lowerWhisker) .. "}" ..
+ "\\pgfplotsplothandlersurveyend@boxplot@set{lower quartile}{" .. toTeXstring(computed.lowerQuartile) .. "}" ..
+ "\\pgfplotsplothandlersurveyend@boxplot@set{median}{" .. toTeXstring(computed.median) .. "}" ..
+ "\\pgfplotsplothandlersurveyend@boxplot@set{upper quartile}{" .. toTeXstring(computed.upperQuartile) .. "}" ..
+ "\\pgfplotsplothandlersurveyend@boxplot@set{upper whisker}{" .. toTeXstring(computed.upperWhisker) .. "}" ..
+ "\\pgfplotsplothandlersurveyend@boxplot@set{sample size}{" .. toTeXstring(# self.boxplotInput) .. "}"
+
+ self.boxplotInput = nil
+ Plothandler.surveystart(self)
+
+ local outliers = computed.outliers
+ for i =1,#outliers do
+ local outlier = outliers[i]
+ local pt = Coord.new()
+ -- this here resembles \pgfplotsplothandlersurveypoint@boxplot@prepared when it is invoked during boxplot:
+ local X,Y = self.boxplotsetxy(outlier, 0)
+ pt.x = { X, Y, nil }
+ Plothandler.surveypoint(self,pt)
+ end
+ Plothandler.surveyend(self)
+
+ return texResult
+end
+
+function BoxPlotPlothandler:semiSurveyedValue(data)
+ local result = pgftonumber(data)
+ if result then
+ table.insert( self.boxplotInput, result )
+ end
+end
+
+function BoxPlotPlothandler:surveypoint(pt)
+ if self.boxplotSurveyMode then
+ error("Unsupported Operation encountered: box plot survey in LUA are only in PARTIAL mode (i.e. only if almost all has been prepared in TeX. Use 'lua backend=false' to get around this.")
+ else
+ Plothandler.surveypoint(self,pt)
+ end
+end
+
+-------------------------------------------------------
+
+end
diff --git a/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/streamer.lua b/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/streamer.lua
new file mode 100644
index 00000000000..ab5b605d6d1
--- /dev/null
+++ b/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplots/streamer.lua
@@ -0,0 +1,193 @@
+-- Contains coordinate streamers, i.e. classes which generate coordinates and stream them to some output stream
+
+local math=math
+local pgfplotsmath = pgfplots.pgfplotsmath
+local type=type
+local tostring=tostring
+local error=error
+local table=table
+
+do
+-- all globals will be read from/defined in pgfplots:
+local _ENV = pgfplots
+-----------------------------------
+
+CoordOutputStream = newClass()
+
+function CoordOutputStream:constructor()
+end
+
+-- @param pt an instance of Coord
+function CoordOutputStream:coord(pt)
+end
+
+-----------------------------------
+
+SurveyCoordOutputStream = newClassExtends(CoordOutputStream)
+
+function SurveyCoordOutputStream:constructor(targetPlothandler)
+ if not targetPlothandler then error("arguments must not be nil") end
+ self.plothandler= targetPlothandler
+end
+
+function SurveyCoordOutputStream:coord(pt)
+ self.plothandler:surveypoint(pt)
+end
+
+-------------
+-- This is a partial reimplementation of \addplot expression: it samples points -- but entirely in LUA. Only the results are serialized back to TeX.
+
+AddplotExpressionCoordinateGenerator = newClass()
+
+function AddplotExpressionCoordinateGenerator:constructor(coordoutputstream, expressionsByDimension, domainMin, domainMax, samples, variableNames)
+ if not coordoutputstream or not expressionsByDimension or not domainMin or not domainMax or not samples or not variableNames then error("arguments must not be nil") end
+ if #variableNames ~= 2 then error("Expected 2 variableNames") end
+ self.coordoutputstream = coordoutputstream
+ self.is3d = #expressionsByDimension == 3
+ self.expressions = expressionsByDimension
+ self.domainMin = domainMin
+ self.domainMax = domainMax
+ self.samples = samples
+ self.variableNames = variableNames
+
+ -- log("initialized " .. tostring(self) .. "\n")
+end
+
+-- @return true on success or false if the operation cannot be carried out.
+-- this method is a replicate of \pgfplots@addplotimpl@expression@@
+function AddplotExpressionCoordinateGenerator:generateCoords()
+ local stringToFunctionMap = pgfluamathfunctions.stringToFunctionMap
+ -- create a backup of the 'x' and 'y' math expressions which
+ -- have been defined in \pgfplots@coord@stream@start:
+ local old_global_function_x = stringToFunctionMap["x"]
+ local old_global_function_y = stringToFunctionMap["y"]
+
+ local coordoutputstream = self.coordoutputstream
+ local is3d = self.is3d
+ local expressions = self.expressions
+ local xExpr = expressions[1]
+ local yExpr = expressions[2]
+ local zExpr = expressions[3]
+
+ local domainMin = self.domainMin
+ local domainMax = self.domainMax
+ local samples = self.samples
+ local h = {}
+ for i = 1,#domainMin do
+ h[i] = (domainMax[i] - domainMin[i]) / (samples[i]-1)
+ end
+
+ local variableNames = self.variableNames
+
+ local x,y
+ local sampleLine = #samples==1
+
+ local function pseudoconstantx() return x end
+ local pseudoconstanty
+ if sampleLine then
+ if yExpr ~= variableNames[2] then
+ -- suppress the warning - we want to allow (x,y,x^2) in this case.
+ pseudoconstanty = function() return 0 end
+ else
+ local didWarn = false
+ pseudoconstanty = function()
+ if not didWarn then
+ log("Sorry, you can't use 'y' in this context. PGFPlots expected to sample a line, not a mesh. Please use the [mesh] option combined with [samples y>0] and [domain y!=0:0] to indicate a twodimensional input domain\n")
+ didWarn = true
+ end
+ return 0
+ end
+ end
+ else
+ pseudoconstanty = function() return y end
+ end
+
+ local pgfmathparse = pgfluamathparser.pgfmathparse
+ local prepareX
+ if xExpr == variableNames[1] then
+ prepareX = function() return x end
+ else
+ prepareX = function() return pgfmathparse(xExpr) end
+ end
+
+ local prepareY
+ if yExpr == variableNames[2] then
+ prepareY = function() return y end
+ else
+ prepareY = function() return pgfmathparse(yExpr) end
+ end
+
+ local function computeXYZ()
+ stringToFunctionMap[variableNames[1]] = pseudoconstantx
+ stringToFunctionMap[variableNames[2]] = pseudoconstanty
+ local X = prepareX()
+ local Y = prepareY()
+ local Z = nil
+ if is3d then
+ Z = pgfmathparse(zExpr)
+ end
+
+ local pt = Coord.new()
+ pt.x = { X, Y, Z}
+
+ -- restore 'x' and 'y'
+ -- FIXME : defining the resulting x/y coordinates as 'x' and 'y' constants was a really really bad idea in the first place :-(
+ stringToFunctionMap["x"] = old_global_function_x
+ stringToFunctionMap["y"] = old_global_function_y
+
+ coordoutputstream:coord(pt)
+ end
+
+ if not sampleLine then
+ local xmin = domainMin[1]
+ local ymin = domainMin[2]
+ local hx = h[1]
+ local hy = h[2]
+ local max_i = samples[1]-1
+ local max_j = samples[2]-1
+ -- samples twodimensionally (a lattice):
+ for j = 0,max_j do
+ -- FIXME : pgfplots@plot@data@notify@next@y
+ y = ymin + j*hy
+ -- log("" .. j .. "\n")
+ for i = 0,max_i do
+ -- FIXME : pgfplots@plot@data@notify@next@x
+ x = xmin + i*hx
+ computeXYZ()
+ end
+ -- FIXME : \pgfplotsplothandlernotifyscanlinecomplete
+ end
+ else
+ local xmin = domainMin[1]
+ local hx = h[1]
+ local max_i = samples[1]-1
+ for i = 0,max_i do
+ -- FIXME : pgfplots@plot@data@notify@next@x
+ x = xmin + i*hx
+ computeXYZ()
+ end
+ end
+
+ stringToFunctionMap[variableNames[1]] = nil
+ stringToFunctionMap[variableNames[2]] = nil
+ return true
+end
+
+function AddplotExpressionCoordinateGenerator:__tostring()
+ local result = "AddplotExpressionCoordinateGenerator[\n"
+ result = result .. "\n variable(s)=" .. self.variableNames[1] .. " " .. self.variableNames[2]
+ result = result .. "\n expressions="
+ for i = 1,#self.expressions do
+ result = result .. self.expressions[i] ..", "
+ end
+ result = result .. "\n domain=" .. self.domainMin[1] .. ":" .. self.domainMax[1]
+ result = result .. "\n samples=" .. self.samples[1]
+ if #self.domainMin == 2 then
+ result = result .. "\n domain y=" .. self.domainMin[2] .. ":" .. self.domainMax[2]
+ result = result .. "\n samples y=" .. self.samples[2]
+ end
+ result = result .. "]"
+ return result
+end
+
+end
diff --git a/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplotsoldpgfsupp/luamath/functions.lua b/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplotsoldpgfsupp/luamath/functions.lua
new file mode 100644
index 00000000000..e5573f26f64
--- /dev/null
+++ b/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplotsoldpgfsupp/luamath/functions.lua
@@ -0,0 +1,649 @@
+--------------------------------------------------------------------------------------------------
+------ This file is a copy of some part of PGF/Tikz.
+------ It has been copied here to provide :
+------ - compatibility with older PGF versions
+------ - availability of PGF contributions by Christian Feuersaenger
+------ which are necessary or helpful for pgfplots.
+------
+------ For reasons of simplicity, I have copied the whole file, including own contributions AND
+------ PGF parts. The copyrights are as they appear in PGF.
+------
+------ Note that pgfplots has compatible licenses.
+------
+------ This copy has been modified in the following ways:
+------ - nested \input commands have been updated
+------
+--
+-- Support for the contents of this file will NOT be done by the PGF/TikZ team.
+-- Please contact the author and/or maintainer of pgfplots (Christian Feuersaenger) if you need assistance in conjunction
+-- with the deployment of this patch or partial content of PGF. Note that the author and/or maintainer of pgfplots has no obligation to fix anything:
+-- This file comes without any warranty as the rest of pgfplots; there is no obligation for help.
+----------------------------------------------------------------------------------------------------
+-- Date of this copy: Mi 14. Jan 21:15:32 CET 2015 ---
+
+
+
+-- Copyright 2011 by Christophe Jorssen
+--
+-- This file may be distributed and/or modified
+--
+-- 1. under the LaTeX Project Public License and/or
+-- 2. under the GNU Public License.
+--
+-- See the file doc/generic/pgf/licenses/LICENSE for more details.
+--
+-- $Id: functions.lua,v 1.2 2015/01/14 20:15:13 cfeuersaenger Exp $
+--
+
+local pgfluamathfunctions = pgfluamathfunctions or {}
+
+-- Maps function names to their function.
+--
+-- Note that this allows to register functions which are not in pgfluamathfunctions.
+--
+-- Note that the string keys are not necessarily the same as the function
+-- names. In particular, the math expression "not(1,1)" will execute notPGF(1,1)
+--
+-- Note that each function which is added to pgfluamathfunctions will _automatically_ be inserted into this map, see __newindex.
+-- (I fear it will not be erased directly...)
+pgfluamathfunctions.stringToFunctionMap = {}
+
+local newFunctionAllocatedCallback = function(table,key,value)
+ local keyName = tostring(key):gsub("PGF","")
+ if not value then
+ stringToFunctionMap[keyName] = nil
+ elseif type(value) == 'function' then
+ -- remember it, and strip PGF suffix (i.e. remember 'not' instead of 'notPGF')
+ pgfluamathfunctions.stringToFunctionMap[keyName] = value
+ end
+ rawset(table,key,value)
+end
+
+setmetatable(pgfluamathfunctions, { __newindex = newFunctionAllocatedCallback })
+
+local mathabs, mathacos, mathasin = math.abs, math.acos, math.asin
+local mathatan, mathceil = math.atan, math.ceil
+local mathcos, mathdeg = math.cos, math.deg
+local mathexp, mathfloor, mathfmod = math.exp, math.floor, math.fmod
+local mathlog, mathmax = math.log, math.max
+local mathmin, mathpi = math.min, math.pi
+local mathrad, mathrandom = math.rad, math.random
+local mathrandomseed, mathsin = math.randomseed, math.sin
+local mathsqrt = math.sqrt
+local mathtan = math.tan
+
+local trigFormatToRadians = mathrad
+
+local radiansToTrigFormat = mathdeg
+
+pgfluamathfunctions.TrigFormat = { 'deg', 'rad' }
+pgfluamathfunctions.stringToFunctionMap["TrigFormat"] = nil
+
+-- choice is one of the valid choices in TrigFormat.
+function pgfluamathfunctions.setTrigFormat(choice)
+ if choice == 'deg' then
+ trigFormatToRadians = mathrad
+ radiansToTrigFormat = mathdeg
+ elseif choice == 'rad' then
+ local identity = function(x) return x end
+ trigFormatToRadians = identity
+ radiansToTrigFormat = identity
+ else
+ error("The argument '" .. tostring(choice) .. "' is no valid choice for setTrigFormat.")
+ end
+end
+pgfluamathfunctions.stringToFunctionMap["setTrigFormat"] = nil
+
+pgfluamathfunctions.setRandomSeed = mathrandomseed
+pgfluamathfunctions.stringToFunctionMap["setRandomSeed"] = nil
+
+-------------------------------------------
+
+function pgfluamathfunctions.add(x,y)
+ return x+y
+end
+
+function pgfluamathfunctions.subtract(x,y)
+ return x-y
+end
+
+function pgfluamathfunctions.neg(x)
+ return -x
+end
+
+function pgfluamathfunctions.multiply(x,y)
+ return x*y
+end
+
+function pgfluamathfunctions.veclen(x,y)
+ return mathsqrt(x*x+y*y)
+end
+
+function pgfluamathfunctions.divide(x,y)
+ return x/y
+end
+
+function pgfluamathfunctions.div(x,y)
+ return mathfloor(x/y)
+end
+
+function pgfluamathfunctions.pow(x,y)
+ -- do not use math.pow -- it is deprecated as of LUA 5.3
+ return x^y
+end
+
+function pgfluamathfunctions.factorial(x)
+-- TODO: x must be an integer
+ if x == 0 then
+ return 1
+ else
+ return x * pgfluamathfunctions.factorial(x-1)
+ end
+end
+
+function pgfluamathfunctions.ifthenelse(x,y,z)
+ if x~= 0 then
+ return y
+ else
+ return z
+ end
+end
+
+function pgfluamathfunctions.equal(x,y)
+ if x == y then
+ return 1
+ else
+ return 0
+ end
+end
+
+function pgfluamathfunctions.greater(x,y)
+ if x > y then
+ return 1
+ else
+ return 0
+ end
+end
+
+function pgfluamathfunctions.less(x,y)
+ if x < y then
+ return 1
+ else
+ return 0
+ end
+end
+
+function pgfluamathfunctions.min(x,y)
+ return mathmin(x,y)
+end
+
+function pgfluamathfunctions.max(x,y)
+ return mathmax(x,y)
+end
+
+function pgfluamathfunctions.notequal(x,y)
+ if x ~= y then
+ return 1
+ else
+ return 0
+ end
+end
+
+function pgfluamathfunctions.notless(x,y)
+ if x >= y then
+ return 1
+ else
+ return 0
+ end
+end
+
+function pgfluamathfunctions.notgreater(x,y)
+ if x <= y then
+ return 1
+ else
+ return 0
+ end
+end
+
+function pgfluamathfunctions.andPGF(x,y)
+ if (x ~= 0) and (y ~= 0) then
+ return 1
+ else
+ return 0
+ end
+end
+
+function pgfluamathfunctions.orPGF(x,y)
+ if (x ~= 0) or (y ~= 0) then
+ return 1
+ else
+ return 0
+ end
+end
+
+function pgfluamathfunctions.notPGF(x)
+ if x == 0 then
+ return 1
+ else
+ return 0
+ end
+end
+
+function pgfluamathfunctions.pi()
+ return mathpi
+end
+
+function pgfluamathfunctions.e()
+ return mathexp(1)
+end
+
+function pgfluamathfunctions.abs(x)
+ return mathabs(x)
+end
+
+function pgfluamathfunctions.floor(x)
+ return mathfloor(x)
+end
+
+function pgfluamathfunctions.ceil(x)
+ return mathceil(x)
+end
+
+function pgfluamathfunctions.exp(x)
+ return mathexp(x)
+end
+
+function pgfluamathfunctions.ln(x)
+ return mathlog(x)
+end
+
+local logOf10 = mathlog(10)
+function pgfluamathfunctions.log10(x)
+ return mathlog(x) / logOf10
+end
+
+local logOf2 = mathlog(2)
+function pgfluamathfunctions.log2(x)
+ return mathlog(x) / logOf2
+end
+
+function pgfluamathfunctions.sqrt(x)
+ return mathsqrt(x)
+end
+
+function pgfluamathfunctions.sign(x)
+ if x < 0 then
+ return -1.0
+ elseif x == 0 then
+ return 0.0
+ else
+ return 1.0
+ end
+end
+function pgfluamathfunctions.real(x)
+ -- "ensure that x contains a decimal point" is kind of a no-op here, isn't it!?
+ return x
+end
+
+function pgfluamathfunctions.rnd()
+ return mathrandom()
+end
+
+function pgfluamathfunctions.rand()
+ return -1 + mathrandom() *2
+end
+
+function pgfluamathfunctions.random(x,y)
+ if x == nil and y == nil then
+ return mathrandom()
+ elseif y == nil then
+ return mathrandom(x)
+ else
+ return mathrandom(x,y)
+ end
+end
+
+function pgfluamathfunctions.deg(x)
+ return mathdeg(x)
+end
+
+function pgfluamathfunctions.rad(x)
+ return mathrad(x)
+end
+
+function pgfluamathfunctions.round(x)
+ if x<0 then
+ return -mathfloor(mathabs(x)+0.5)
+ else
+ return mathfloor(x + 0.5)
+ end
+end
+
+function pgfluamathfunctions.gcd(a, b)
+ if b == 0 then
+ return a
+ else
+ return pgfluamathfunctions.gcd(b, a%b)
+ end
+end
+
+function pgfluamathfunctions.isprime(a)
+ local ifisprime = true
+ if a == 1 then
+ ifisprime = false
+ elseif a == 2 then
+ ifisprime = true
+-- if a > 2 then
+ else
+ local i, imax = 2, mathceil(mathsqrt(a)) + 1
+ while ifisprime and (i < imax) do
+ if pgfluamathfunctions.gcd(a,i) ~= 1 then
+ ifisprime = false
+ end
+ i = i + 1
+ end
+ end
+ if ifisprime then
+ return 1
+ else
+ return 0
+ end
+end
+
+
+function pgfluamathfunctions.split_braces_to_explist(s)
+ -- (Thanks to mpg and zappathustra from fctt)
+ -- Make unpack available whatever lua version is used
+ -- (unpack in lua 5.1 table.unpack in lua 5.2)
+ local unpack = table.unpack or unpack
+ local t = {}
+ for i in s:gmatch('%b{}') do
+ table.insert(t, tonumber(i:sub(2, -2)))
+ end
+ return unpack(t)
+end
+
+function pgfluamathfunctions.split_braces_to_table(s)
+ local t = {}
+ for i in s:gmatch('%b{}') do
+ table.insert(t, tonumber(i:sub(2, -2)))
+ end
+ return t
+end
+
+function pgfluamathfunctions.mathtrue()
+ return 1.0
+end
+pgfluamathfunctions.stringToFunctionMap["true"] = pgfluamathfunctions.mathtrue
+
+function pgfluamathfunctions.mathfalse()
+ return 0.0
+end
+pgfluamathfunctions.stringToFunctionMap["false"] = pgfluamathfunctions.mathfalse
+
+function pgfluamathfunctions.frac(a)
+ -- should be positive, apparently
+ return mathabs(a - pgfluamathfunctions.int(a))
+end
+
+function pgfluamathfunctions.int(a)
+ if a < 0 then
+ return -mathfloor(mathabs(a))
+ else
+ return mathfloor(a)
+ end
+end
+
+function pgfluamathfunctions.iseven(a)
+ if (a % 2) == 0 then
+ return 1.0
+ else
+ return 0.0
+ end
+end
+
+function pgfluamathfunctions.isodd(a)
+ if (a % 2) == 0 then
+ return 0.0
+ else
+ return 1.0
+ end
+end
+
+function pgfluamathfunctions.mod(x,y)
+ if x/y < 0 then
+ return -(mathabs(x)%mathabs(y))
+ else
+ return mathabs(x)%mathabs(y)
+ end
+end
+
+function pgfluamathfunctions.Mod(x,y)
+ local tmp = pgfluamathfunctions.mod(x,y)
+ if tmp < 0 then
+ tmp = tmp + y
+ end
+ return tmp
+end
+
+function pgfluamathfunctions.Sin(x)
+ return mathsin(trigFormatToRadians(x))
+end
+pgfluamathfunctions.sin=pgfluamathfunctions.Sin
+
+
+function pgfluamathfunctions.cosh(x)
+ -- math.cosh is deprecated as of LUA 5.3 . reimplement it:
+ return 0.5* (mathexp(x) + mathexp(-x))
+end
+function pgfluamathfunctions.sinh(x)
+ -- math.sinh is deprecated as of LUA 5.3 . reimplement it:
+ return 0.5* (mathexp(x) - mathexp(-x))
+end
+
+local sinh = pgfluamathfunctions.sinh
+local cosh = pgfluamathfunctions.cosh
+function pgfluamathfunctions.tanh(x)
+ -- math.tanh is deprecated as of LUA 5.3 . reimplement it:
+ return sinh(x)/cosh(x)
+end
+
+function pgfluamathfunctions.Cos(x)
+ return mathcos(trigFormatToRadians(x))
+end
+pgfluamathfunctions.cos=pgfluamathfunctions.Cos
+
+function pgfluamathfunctions.Tan(x)
+ return mathtan(trigFormatToRadians(x))
+end
+pgfluamathfunctions.tan=pgfluamathfunctions.Tan
+
+function pgfluamathfunctions.aSin(x)
+ return radiansToTrigFormat(mathasin(x))
+end
+pgfluamathfunctions.asin=pgfluamathfunctions.aSin
+
+function pgfluamathfunctions.aCos(x)
+ return radiansToTrigFormat(mathacos(x))
+end
+pgfluamathfunctions.acos=pgfluamathfunctions.aCos
+
+function pgfluamathfunctions.aTan(x)
+ return radiansToTrigFormat(mathatan(x))
+end
+pgfluamathfunctions.atan=pgfluamathfunctions.aTan
+
+local mathatan2
+if math.atan2 == nil then
+ -- math.atan2 has been deprecated since LUA 5.3
+ mathatan2 = function (y,x) return mathatan(y,x) end
+else
+ mathatan2 = math.atan2
+end
+
+function pgfluamathfunctions.aTan2(y,x)
+ return radiansToTrigFormat(mathatan2(y,x))
+end
+pgfluamathfunctions.atan2=pgfluamathfunctions.aTan2
+pgfluamathfunctions.atantwo=pgfluamathfunctions.aTan2
+
+function pgfluamathfunctions.cot(x)
+ return pgfluamathfunctions.cos(x) / pgfluamathfunctions.sin(x)
+end
+function pgfluamathfunctions.sec(x)
+ return 1 / pgfluamathfunctions.cos(x)
+end
+function pgfluamathfunctions.cosec(x)
+ return 1 / pgfluamathfunctions.sin(x)
+end
+
+function pgfluamathfunctions.pointnormalised (pgfx, pgfy)
+ local pgfx_normalised, pgfy_normalised
+ if pgfx == 0. and pgfy == 0. then
+ -- Orginal pgf macro gives this result
+ tex.dimen['pgf@x'] = "0pt"
+ tex.dimen['pgf@y'] = "1pt"
+ else
+ pgfx_normalised = pgfx/math.sqrt(pgfx^2 + pgfy^2)
+ pgfx_normalised = pgfx_normalised - pgfx_normalised%0.00001
+ pgfy_normalised = pgfy/math.sqrt(pgfx^2 + pgfy^2)
+ pgfy_normalised = pgfy_normalised - pgfy_normalised%0.00001
+ tex.dimen['pgf@x'] = tostring(pgfx_normalised) .. "pt"
+ tex.dimen['pgf@y'] = tostring(pgfy_normalised) .. "pt"
+ end
+ return nil
+end
+
+local isnan = function(x)
+ return x ~= x
+end
+
+pgfluamathfunctions.isnan = isnan
+
+local infty = 1/0
+pgfluamathfunctions.infty = infty
+
+local nan = math.sqrt(-1)
+pgfluamathfunctions.nan = nan
+
+local stringlen = string.len
+local globaltonumber = tonumber
+local stringsub=string.sub
+local stringformat = string.format
+local stringsub = string.sub
+
+-- like tonumber(x), but it also accepts nan, inf, infty, and the TeX FPU format
+function pgfluamathfunctions.tonumber(x)
+ if type(x) == 'number' then return x end
+ if not x then return x end
+
+ local len = stringlen(x)
+ local result = globaltonumber(x)
+ if not result then
+ if len >2 and stringsub(x,2,2) == 'Y' and stringsub(x,len,len) == ']' then
+ -- Ah - some TeX FPU input of the form 1Y1.0e3] . OK. transform it
+ local flag = stringsub(x,1,1)
+ if flag == '0' then
+ -- ah, 0.0
+ result = 0.0
+ elseif flag == '1' then
+ result = globaltonumber(stringsub(x,3, len-1))
+ elseif flag == '2' then
+ result = globaltonumber("-" .. stringsub(x,3, len-1))
+ elseif flag == '3' then
+ result = nan
+ elseif flag == '4' then
+ result = infty
+ elseif flag == '5' then
+ result = -infty
+ end
+ else
+ local lower = x:lower()
+ if lower == 'nan' then
+ result = nan
+ elseif lower == 'inf' or lower == 'infty' then
+ result = infty
+ elseif lower == '-inf' or lower == '-infty' then
+ result = -infty
+ end
+ end
+ end
+
+ return result
+end
+
+local stringlen = string.len
+local globaltonumber = tonumber
+local stringformat = string.format
+local stringsub = string.sub
+local stringfind = string.find
+local stringbyte = string.byte
+local NULL_CHAR = string.byte("0",1)
+
+local function discardTrailingZeros(x)
+ local result = x
+ -- printf is too stupid: I would like to have
+ -- 1. a fast method
+ -- 2. a reliable method
+ -- 3. full precision of x
+ -- 4. a fixed point representation
+ -- the 'f' modifier has trailing zeros (stupid!)
+ -- the 'g' modified can switch to scientific notation (no-go!)
+ local periodOff = stringfind(result, '.',1,true)
+ if periodOff ~= nil then
+ -- strip trailing zeros
+ local chars = { stringbyte(result,1,#result) };
+ local lastNonZero = #chars
+ for i = #chars, periodOff, -1 do
+ if chars[i] ~= NULL_CHAR then lastNonZero=i; break; end
+ end
+ if lastNonZero ~= #chars then
+ -- Ah: we had at least one trailing zero.
+ -- discard all but the last.
+ lastNonZero = mathmax(periodOff+1,lastNonZero)
+ end
+ result = stringsub(result, 1, lastNonZero)
+ end
+ return result;
+end
+
+local function discardTrailingZerosFromMantissa(x)
+ local mantissaStart = stringfind(x, "e")
+
+ local mantissa = stringsub(x,1,mantissaStart-1)
+ local exponent = stringsub(x,mantissaStart)
+
+ return discardTrailingZeros(mantissa) .. exponent
+end
+
+
+-- a helper function which has no catcode issues when communicating with TeX:
+function pgfluamathfunctions.tostringfixed(x)
+ if x == nil then
+ return ""
+ end
+
+ return discardTrailingZeros(stringformat("%f", x))
+end
+
+-- converts an input number to a string which is accepted by the TeX FPU
+function pgfluamathfunctions.toTeXstring(x)
+ local result = ""
+ if x ~= nil then
+ if x == infty then result = "4Y0.0e0]"
+ elseif x == -infty then result = "5Y0.0e0]"
+ elseif isnan(x) then result = "3Y0.0e0]"
+ elseif x == 0 then result = "0Y0.0e0]"
+ else
+ result = discardTrailingZerosFromMantissa(stringformat("%.10e", x))
+ if x > 0 then
+ result = "1Y" .. result .. "]"
+ else
+ result = "2Y" .. stringsub(result,2) .. "]"
+ end
+ end
+ end
+ return result
+end
+
+return pgfluamathfunctions
diff --git a/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplotsoldpgfsupp/luamath/parser.lua b/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplotsoldpgfsupp/luamath/parser.lua
new file mode 100644
index 00000000000..cc9663f145f
--- /dev/null
+++ b/Master/texmf-dist/tex/generic/pgfplots/lua/pgfplotsoldpgfsupp/luamath/parser.lua
@@ -0,0 +1,495 @@
+--------------------------------------------------------------------------------------------------
+------ This file is a copy of some part of PGF/Tikz.
+------ It has been copied here to provide :
+------ - compatibility with older PGF versions
+------ - availability of PGF contributions by Christian Feuersaenger
+------ which are necessary or helpful for pgfplots.
+------
+------ For reasons of simplicity, I have copied the whole file, including own contributions AND
+------ PGF parts. The copyrights are as they appear in PGF.
+------
+------ Note that pgfplots has compatible licenses.
+------
+------ This copy has been modified in the following ways:
+------ - nested \input commands have been updated
+------
+--
+-- Support for the contents of this file will NOT be done by the PGF/TikZ team.
+-- Please contact the author and/or maintainer of pgfplots (Christian Feuersaenger) if you need assistance in conjunction
+-- with the deployment of this patch or partial content of PGF. Note that the author and/or maintainer of pgfplots has no obligation to fix anything:
+-- This file comes without any warranty as the rest of pgfplots; there is no obligation for help.
+----------------------------------------------------------------------------------------------------
+-- Date of this copy: Mi 14. Jan 21:15:32 CET 2015 ---
+
+
+
+-- Copyright 2011 by Christophe Jorssen and Mark Wibrow
+-- Copyright 2014 by Christian Feuersaenger
+--
+-- This file may be distributed and/or modified
+--
+-- 1. under the LaTeX Project Public License and/or
+-- 2. under the GNU Public License.
+--
+-- See the file doc/generic/pgf/licenses/LICENSE for more details.
+--
+-- $Id: parser.lua,v 1.1 2014/12/27 14:11:49 cfeuersaenger Exp $
+--
+-- usage:
+--
+-- pgfluamathparser = require("pgfplotsoldpgfsupp.luamath.parser")
+--
+-- local result = pgfluamathparser.pgfmathparse("1+ 2*4^2")
+--
+-- This LUA class has a direct backend in \pgfuselibrary{luamath}, see the documentation of that TeX package.
+
+local pgfluamathparser = pgfluamathparser or {}
+
+pgfluamathfunctions = require("pgfplotsoldpgfsupp.luamath.functions")
+
+-- lpeg is always present in luatex
+local lpeg = require("lpeg")
+
+local S, P, R = lpeg.S, lpeg.P, lpeg.R
+local C, Cc, Ct = lpeg.C, lpeg.Cc, lpeg.Ct
+local Cf, Cg, Cs = lpeg.Cf, lpeg.Cg, lpeg.Cs
+local V = lpeg.V
+local match = lpeg.match
+
+local space_pattern = S(" \n\r\t")^0
+local tex_unit =
+ P('pt') + P('mm') + P('cm') + P('in') +
+ -- while valid units, the font-depending ones need special attention... move them to the TeX side. For now.
+ -- P('ex') + P('em') +
+ P('bp') + P('pc') +
+ P('dd') + P('cc') + P('sp');
+
+local one_digit_pattern = R("09")
+local positive_integer_pattern = one_digit_pattern^1
+-- FIXME : it might be a better idea to remove '-' from all number_patterns! Instead, rely on the prefix operator 'neg' to implement negative numbers.
+-- Is that wise? It is certainly less efficient...
+local integer_pattern = S("+-")^-1 * positive_integer_pattern
+-- Valid positive decimals are |xxx.xxx|, |.xxx| and |xxx.|
+local positive_integer_or_decimal_pattern = positive_integer_pattern * ( P(".") * one_digit_pattern^0)^-1 +
+ (P(".") * one_digit_pattern^1)
+local integer_or_decimal_pattern = S("+-")^-1 * positive_integer_or_decimal_pattern
+local fpu_pattern = R"15" * P"Y" * positive_integer_or_decimal_pattern * P"e" * P("-")^-1 * R("09")^1 * P"]"
+local unbounded_pattern = P"inf" + P"INF" + P"nan" + P"NaN" + P"Inf"
+local number_pattern = C(unbounded_pattern + fpu_pattern + integer_or_decimal_pattern * (S"eE" * integer_pattern + C(tex_unit))^-1)
+
+local underscore_pattern = P("_")
+
+local letter_pattern = R("az","AZ")
+local alphanum__pattern = letter_pattern + one_digit_pattern + underscore_pattern
+
+local identifier_pattern = letter_pattern^1 * alphanum__pattern^0
+
+local openparen_pattern = P("(") * space_pattern
+local closeparen_pattern = P(")")
+local opencurlybrace_pattern = P("{")
+local closecurlybrace_pattern = P("}")
+local openbrace_pattern = P("[")
+local closebrace_pattern = P("]")
+
+-- hm. what about '\\' or '\%' ?
+-- accept \pgf@x, \count0, \dimen42, \c@pgf@counta, \wd0, \ht0, \dp 0
+local controlsequence_pattern = P"\\" * C( (R("az","AZ") + P"@")^1) * space_pattern* C( R"09"^0 )
+
+-- local string = P('"') * C((1 - P('"'))^0) * P('"')
+
+local comma_pattern = P(",") * space_pattern
+
+
+----------------
+local TermOp = C(S("+-")) * space_pattern
+local RelationalOp = C( P"==" + P"!=" + P"<=" + P">=" + P"<" + P">" ) * space_pattern
+local FactorOp = C(S("*/")) * space_pattern
+
+-- Grammar
+local Exp, Term, Factor = V"Exp", V"Term", V"Factor"
+local Prefix = V"Prefix"
+local Postfix = V"Postfix"
+
+
+
+local function eval (v1, op, v2)
+ if (op == "+") then return v1 + v2
+ elseif (op == "-") then return v1 - v2
+ elseif (op == "*") then return v1 * v2
+ elseif (op == "/") then return v1 / v2
+ else
+ error("This function must not be invoked for operator "..op)
+ end
+end
+
+local pgfStringToFunctionMap = pgfluamathfunctions.stringToFunctionMap
+local function function_eval(name, ... )
+ local f = pgfStringToFunctionMap[name]
+ if not f then
+ error("Function '" .. name .. "' is undefined (did not find pgfluamathfunctions."..name .." (looked into pgfluamathfunctions.stringToFunctionMap))")
+ end
+ -- FIXME: validate signature
+ return f(...)
+end
+
+
+local func =
+ (C(identifier_pattern) * space_pattern * openparen_pattern * Exp * (comma_pattern * Exp)^0 * closeparen_pattern) / function_eval;
+
+local functionWithoutArg = identifier_pattern / function_eval
+
+-- this is what can occur as exponent after '^'.
+-- I have the impression that the priorities could be implemented in a better way than this... but it seems to work.
+local pow_exponent =
+ -- allows 2^-4, 2^1e4, 2^2
+ -- FIXME : why not 2^1e2 ?
+ Cg(C(integer_or_decimal_pattern)
+ -- 2^pi, 2^multiply(2,2)
+ + Cg(func+functionWithoutArg)
+ -- 2^(2+2)
+ + openparen_pattern * Exp * closeparen_pattern )
+
+local function prefix_eval(op, x)
+ if op == "-" then
+ return pgfluamathfunctions.neg(x)
+ elseif op == "!" then
+ return pgfluamathfunctions.notPGF(x)
+ else
+ error("This function must not be invoked for operator "..op)
+ end
+end
+
+
+local prefix_operator = C( S"-!" )
+local prefix_operator_pattern = (prefix_operator * space_pattern * Cg(Prefix) ) / prefix_eval
+
+-- apparently, we need to distinghuish between <expr> ! and <expr> != <expr2>:
+local postfix_operator = C( S"r!" - P"!=" ) + C(P"^") * space_pattern * pow_exponent
+
+
+local ternary_eval = pgfluamathfunctions.ifthenelse
+
+local factorial_eval = pgfluamathfunctions.factorial
+local deg = pgfluamathfunctions.deg
+local pow_eval = pgfluamathfunctions.pow
+
+-- @param prefix the argument before the postfix operator.
+-- @param op either nil or the postfix operator
+-- @param arg either nil or the (mandatory) argument for 'op'
+local function postfix_eval(prefix, op, arg)
+ local result
+ if op == nil then
+ result = prefix
+ elseif op == "r" then
+ if arg then error("parser setup error: expected nil argument") end
+ result = deg(prefix)
+ elseif op == "!" then
+ if arg then error("parser setup error: expected nil argument") end
+ result = factorial_eval(prefix)
+ elseif op == "^" then
+ if not arg then error("parser setup error: ^ with its argument") end
+ result = pow_eval(prefix, arg)
+ else
+ error("Parser setup error: " .. tostring(op) .. " unexpected in this context")
+ end
+ return result
+end
+
+local function relational_eval(v1, op, v2)
+ local fct
+ if (op == "==") then fct = pgfluamathfunctions.equal
+ elseif (op == "!=") then fct = pgfluamathfunctions.notequal
+ elseif (op == "<") then fct = pgfluamathfunctions.less
+ elseif (op == ">") then fct = pgfluamathfunctions.greater
+ elseif (op == ">=") then fct = pgfluamathfunctions.notless
+ elseif (op == "<=") then fct = pgfluamathfunctions.notgreater
+ else
+ error("This function must not be invoked for operator "..op)
+ end
+ return fct(v1,v2)
+end
+
+-- @return either the box property or nil
+-- @param cs "wd", "ht", or "dp"
+-- @param intSuffix some integer
+local function get_tex_box(cs, intSuffix)
+ -- assume get_tex_box is only called when a dimension is required.
+ local result
+ pgfluamathparser.units_declared = true
+ local box =tex.box[tonumber(intSuffix)]
+ if not box then error("There is no box " .. intSuffix) end
+ if cs == "wd" then
+ result = box.width / 65536
+ elseif cs == "ht" then
+ result = box.height / 65536
+ elseif cs == "dp" then
+ result = box.depth / 65536
+ else
+ result = nil
+ end
+ return result
+end
+
+
+local function controlsequence_eval(cs, intSuffix)
+ local result
+ if intSuffix and #intSuffix >0 then
+ if cs == "count" then
+ result= pgfluamathparser.get_tex_count(intSuffix)
+ elseif cs == "dimen" then
+ result= pgfluamathparser.get_tex_dimen(intSuffix)
+ else
+ result = get_tex_box(cs,intSuffix)
+ if not result then
+ -- this can happen - we cannot expand \chardef'ed boxes here.
+ -- this will be done by the TeX part
+ error('I do not know/support the TeX register "\\' .. cs .. '"')
+ end
+ end
+ else
+ result = pgfluamathparser.get_tex_register(cs)
+ end
+ return result
+end
+
+pgfluamathparser.units_declared = false
+function pgfluamathparser.get_tex_register(register)
+ -- register is a string which could be a count or a dimen.
+ if pcall(tex.getcount, register) then
+ return tex.count[register]
+ elseif pcall(tex.getdimen, register) then
+ pgfluamathparser.units_declared = true
+ return tex.dimen[register] / 65536 -- return in points.
+ else
+ error('I do not know the TeX register "' .. register .. '"')
+ return nil
+ end
+
+end
+
+function pgfluamathparser.get_tex_count(count)
+ -- count is expected to be a number
+ return tex.count[tonumber(count)]
+end
+
+function pgfluamathparser.get_tex_dimen(dimen)
+ -- dimen is expected to be a number
+ pgfluamathparser.units_declared = true
+ return tex.dimen[tonumber(dimen)] / 65536
+end
+
+function pgfluamathparser.get_tex_sp(dimension)
+ -- dimension should be a string
+ pgfluamathparser.units_declared = true
+ return tex.sp(dimension) / 65536
+end
+
+
+local initialRule = V"initial"
+
+local Summand = V"Summand"
+local Relational = V"Relational"
+local LogicalOr = V"LogicalOr"
+local LogicalAnd = V"LogicalAnd"
+
+local pgftonumber = pgfluamathfunctions.tonumber
+local tonumber_withunit = pgfluamathparser.get_tex_sp
+local function number_optional_units_eval(x, unit)
+ if not unit then
+ return pgftonumber(x)
+ else
+ return tonumber_withunit(x)
+ end
+end
+
+-- @param scale the number.
+-- @param controlsequence either nil in which case just the number must be returned or a control sequence
+-- @see controlsequence_eval
+local function scaled_controlsequence_eval(scale, controlsequence, intSuffix)
+ if controlsequence==nil then
+ return scale
+ else
+ return scale * controlsequence_eval(controlsequence, intSuffix)
+ end
+end
+
+-- Grammar
+--
+-- for me:
+-- - use '/' to evaluate all expressions which contain a _constant_ number of captures.
+-- - use Cf to evaluate expressions which contain a _dynamic_ number of captures
+--
+-- see unittest_luamathparser.tex for tons of examples
+local G = P{ "initialRule",
+ initialRule = space_pattern* Exp * -1;
+ -- ternary operator (or chained ternary operators):
+ -- FIXME : is this chaining a good idea!?
+ Exp = Cf( Relational * Cg(P"?" * space_pattern * Relational * P":" *space_pattern * Relational )^0, ternary_eval) ;
+ -- FIXME : do we really allow something like " 1 == 1 != 2" ? I would prefer (1==1) != 2 !?
+ Relational = Cf(LogicalOr * Cg(RelationalOp * LogicalOr)^0, relational_eval);
+ LogicalOr = Cf(LogicalAnd * (P"||" * space_pattern * LogicalAnd)^0, pgfluamathfunctions.orPGF);
+ LogicalAnd = Cf(Summand * (P"&&" * space_pattern * Summand)^0, pgfluamathfunctions.andPGF);
+ Summand = Cf(Term * Cg(TermOp * Term)^0, eval) ;
+ Term = Cf(Prefix * Cg(FactorOp * Prefix)^0, eval);
+ Prefix = prefix_operator_pattern + Postfix;
+ -- this calls 'postfix_eval' with nil arguments if it is no postfix operation.. but that does not hurt (right?)
+ Postfix = Factor * (postfix_operator * space_pattern)^-1 / postfix_eval;
+ Factor =
+ (
+ number_pattern / number_optional_units_eval *
+ -- this construction will evaluate number_pattern with 'number_optional_units_eval' FIRST.
+ -- also accept '0.5 \pgf@x' here:
+ space_pattern *controlsequence_pattern^-1 / scaled_controlsequence_eval
+ + func
+ + functionWithoutArg
+ + openparen_pattern * Exp * closeparen_pattern
+ + controlsequence_pattern / controlsequence_eval
+ ) *space_pattern
+ ;
+}
+
+-- does not reset units_declared.
+local function pgfmathparseinternal(str)
+ local result = match(G,str)
+ if result == nil then
+ error("The string '" .. str .. "' is no valid PGF math expression. Please check for syntax errors.")
+ end
+ return result
+end
+
+
+-- This is the math parser function in this module.
+--
+-- @param str a string like "1+1" which is accepted by the PGF math language
+-- @return the result of the expression.
+--
+-- Throws an error if the string is no valid expression.
+function pgfluamathparser.pgfmathparse(str)
+ pgfluamathparser.units_declared = false
+
+ return pgfmathparseinternal(str)
+end
+
+local pgfmathparse = pgfluamathparser.pgfmathparse
+local tostringfixed = pgfluamathfunctions.tostringfixed
+local tostringfpu = pgfluamathfunctions.toTeXstring
+
+local tmpFunctionArgumentPrefix = "tmpVar"
+local stackOfLocalFunctions = {}
+
+-- This is a backend for PGF's 'declare function'.
+-- \tikzset{declare function={mu(\x,\i)=\x^\i;}}
+-- will boil down to
+-- pgfluamathparser.declareExpressionFunction("mu", 2, "#1^#2")
+--
+-- The local function will be pushed on a stack of known local functions and is
+-- available until popLocalExpressionFunction() is called. TeX will call this using
+-- \aftergroup.
+--
+-- @param name the name of the new function
+-- @param numArgs the number of arguments
+-- @param expression an expression containing #1, ... #n where n is numArgs
+--
+-- ATTENTION: local functions behave DIFFERENTLY in LUA!
+-- In LUA, local variables are not expanded whereas TeX expands them.
+-- The difference is
+--
+-- declare function={mu1(\x,\i)=\x^\i;}
+-- \pgfmathparse{mu1(-5,2)} --> -25
+-- \pgfluamathparse{mu1(-5,2)} --> 25
+--
+-- x = -5
+-- \pgfmathparse{mu1(x,2)} --> 25
+-- \pgfluamathparse{mu1(x,2)} --> 25
+--
+-- In an early prototype, I simulated TeX's expansion to fix the first case (successfully).
+-- BUT: that "simulated expansion" broke the second case because LUA will evaluate "x" and hand -5 to the local function.
+-- I decided to keep it as is. Perhaps we should fix PGF's expansion approach in TeX (which is ugly anyway)
+function pgfluamathparser.pushLocalExpressionFunction(name, numArgs, expression)
+ -- now we have "tmpVar1^tmpVar2" instead of "#1^#2"
+ local normalizedExpr = expression:gsub("#", tmpFunctionArgumentPrefix)
+ local restores = {}
+ local tmpVars = {}
+ for i=1,numArgs do
+ local tmpVar = tmpFunctionArgumentPrefix .. tostring(i)
+ tmpVars[i] = tmpVar
+ end
+
+ local newFunction = function(...)
+ local args = table.pack(...)
+
+ -- define "tmpVar1" ... "tmpVarN" to return args[i].
+ -- Of course, we need to restore "tmpVar<i>" after we return!
+ for i=1,numArgs do
+ local tmpVar = tmpVars[i]
+ local value = args[i]
+ restores[i] = pgfStringToFunctionMap[tmpVar]
+ pgfStringToFunctionMap[tmpVar] = function () return value end
+ end
+
+ -- parse our expression.
+
+ -- FIXME : this here is an attempt to mess around with "units_declared".
+ -- It would be better to call pgfmathparse and introduce some
+ -- semaphore to check if pgfmathparse is a nested call-- in this case, it should
+ -- not reset units_declared. But there is no "finally" block and pcall is crap (looses stack trace).
+ local success,result = pcall(pgfmathparseinternal, normalizedExpr)
+
+ -- remove 'tmpVar1', ... from the function table:
+ for i=1,numArgs do
+ local tmpVar = tmpVars[i]
+ pgfStringToFunctionMap[tmpVar] = restores[i]
+ end
+
+ if success==false then error(result) end
+ return result
+ end
+ table.insert(stackOfLocalFunctions, name)
+ pgfStringToFunctionMap[name] = newFunction
+end
+
+function pgfluamathparser.popLocalExpressionFunction()
+ local name = stackOfLocalFunctions[#stackOfLocalFunctions]
+ pgfStringToFunctionMap[name] = nil
+ -- this removes the last element:
+ table.remove(stackOfLocalFunctions)
+end
+
+
+-- A Utility function which simplifies the interaction with the TeX code
+-- @param expression the input expression (string)
+-- @param outputFormatChoice 0 if the result should be a fixed point number, 1 if it should be in FPU format
+-- @param showErrorMessage (boolean) true if any error should be displayed, false if errors should simply result in an invocation of TeX's parser (the default)
+--
+-- it defines \pgfmathresult and \ifpgfmathunitsdeclared
+function pgfluamathparser.texCallParser(expression, outputFormatChoice, showErrorMessage)
+ local success, result
+ if showErrorMessage then
+ result = pgfmathparse(expression)
+ success = true
+ else
+ success, result = pcall(pgfmathparse, expression)
+ end
+
+ if success and result then
+ local result_str
+ if outputFormatChoice == 0 then
+ -- luamath/output format=fixed
+ result_str = tostringfixed(result)
+ else
+ -- luamath/output format=fixed
+ result_str = tostringfpu(result)
+ end
+ tex.sprint("\\def\\pgfmathresult{" .. result_str .. "}")
+ if pgfluamathparser.units_declared then
+ tex.sprint("\\pgfmathunitsdeclaredtrue")
+ else
+ tex.sprint("\\pgfmathunitsdeclaredfalse")
+ end
+ else
+ tex.sprint("\\def\\pgfmathresult{}")
+ tex.sprint("\\pgfmathunitsdeclaredfalse")
+ end
+end
+
+return pgfluamathparser