diff options
author | Karl Berry <karl@freefriends.org> | 2018-06-10 20:51:02 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2018-06-10 20:51:02 +0000 |
commit | 4273e1b07b28f73dfa7d6b2440d8cdf803440fa1 (patch) | |
tree | eba93c24a4f30a2743e04c1622e224f6d87c41c3 /Master/texmf-dist/tex/lualatex/bezierplot | |
parent | bc7020ed4219d8d04e324299e468fb0b0b706222 (diff) |
bezierplot (10jun18)
git-svn-id: svn://tug.org/texlive/trunk@47977 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/lualatex/bezierplot')
-rwxr-xr-x | Master/texmf-dist/tex/lualatex/bezierplot/bezierplot.lua | 476 | ||||
-rw-r--r-- | Master/texmf-dist/tex/lualatex/bezierplot/bezierplot.sty | 10 |
2 files changed, 308 insertions, 178 deletions
diff --git a/Master/texmf-dist/tex/lualatex/bezierplot/bezierplot.lua b/Master/texmf-dist/tex/lualatex/bezierplot/bezierplot.lua index da702f987fc..8cc414f278d 100755 --- a/Master/texmf-dist/tex/lualatex/bezierplot/bezierplot.lua +++ b/Master/texmf-dist/tex/lualatex/bezierplot/bezierplot.lua @@ -1,6 +1,6 @@ #!/usr/bin/env lua -- Linus Romer, published 2018 under LPPL Version 1.3c --- version 1.0 2018-04-12 +-- version 1.1 2018-06-10 abs = math.abs acos = math.acos asin = math.asin @@ -33,7 +33,7 @@ function sgn(x) end end -function round(num, decimals) +local function round(num, decimals) local result = tonumber(string.format("%." .. (decimals or 0) .. "f", num)) if abs(result) == 0 then return 0 @@ -45,77 +45,86 @@ end -- 5-stencil method -- return from a graph from f in the form {{x,y},...} -- the derivatives in form {{x,y,dy/dx,ddy/ddx},...} -function diffgraph(func,graph,h) +local function diffgraph(func,graph,h) local dgraph = {} - local yh = func(graph[1][1]-h) - local yhh = func(graph[1][1]-2*h) - if yhh > -math.huge and yhh < math.huge -- if defined at all - and yh > -math.huge and yh < math.huge then - dgraph[1] = {graph[1][1],graph[1][2], - (yhh-8*yh+8*graph[2][2]-graph[3][2])/(12*h), - (-yhh+16*yh-30*graph[1][2]+16*graph[2][2]-graph[3][2]) - /(12*h^2)} - dgraph[2] = {graph[2][1],graph[2][2], - (yh-8*graph[1][2]+8*graph[3][2]-graph[4][2])/(12*h), - (-yh+16*graph[1][2]-30*graph[2][2]+16*graph[3][2]-graph[4][2]) - /(12*h^2)} - else -- take neighbour values - dgraph[1] = {graph[1][1],graph[1][2], - (graph[1][2]-8*graph[2][2]+8*graph[4][2]-graph[5][2])/(12*h), - (-graph[1][2]+16*graph[2][2]-30*graph[3][2] - +16*graph[4][2]-graph[5][2])/(12*h^2)} - dgraph[2] = {graph[2][1],graph[2][2], - (graph[1][2]-8*graph[2][2]+8*graph[4][2]-graph[5][2])/(12*h), - (-graph[1][2]+16*graph[2][2]-30*graph[3][2] - +16*graph[4][2]-graph[5][2])/(12*h^2)} - end local l = #graph - for i = 3, l-2 do - table.insert(dgraph,{graph[i][1],graph[i][2], - (graph[i-2][2]-8*graph[i-1][2]+8*graph[i+1][2]-graph[i+2][2]) - /(12*h), - (-graph[i-2][2]+16*graph[i-1][2]-30*graph[i][2] - +16*graph[i+1][2]-graph[i+2][2]) - /(12*h^2)}) - end - yh = func(graph[l][1]+h) - yhh = func(graph[l][1]+2*h) - if yhh > -math.huge and yhh < math.huge -- if defined at all - and yh > -math.huge and yh < math.huge then - dgraph[l-1] = {graph[l-1][1],graph[l-1][2], - (graph[l-3][2]-8*graph[l-2][2]+8*graph[l][2]-yh)/(12*h), - (-graph[l-3][2]+16*graph[l-2][2]-30*graph[l-1][2] - +16*graph[l][2]-yh)/(12*h^2)} - dgraph[l] = {graph[l][1],graph[l][2], - (graph[l-2][2]-8*graph[l-1][2]+8*yh-yhh)/(12*h), - (-graph[l-2][2]+16*graph[l-1][2]-30*graph[l][2] - +16*yh-yhh)/(12*h^2)} - else - -- take neighbour values - dgraph[l] = {graph[l][1],graph[l][2], - (graph[l-4][2]-8*graph[l-3][2]+8*graph[l-1][2]-graph[l][2]) - /(12*h), - (-graph[l-4][2]+16*graph[l-3][2]-30*graph[l-2][2] - +16*graph[l-1][2]-graph[l][2])/(12*h^2)} - dgraph[l-1] = {graph[l-1][1],graph[l-2][2], - (graph[l-4][2]-8*graph[l-3][2]+8*graph[l-1][2]-graph[l][2]) - /(12*h), - (-graph[l-4][2]+16*graph[l-3][2]-30*graph[l-2][2] - +16*graph[l-1][2]-graph[l][2])/(12*h^2)} - end - -- add information about being extremum / inflection point (true/false) - for i = 1, l do - dgraph[i][5] = false -- dy/dx == 0 ? default, may change later - dgraph[i][6] = false -- ddy/ddx == 0 ? default, may change later - end - for i = 1, l-1 do - -- if no gap is inbetween - if not (dgraph[i+1][1] - dgraph[i][1] > 1.5*h) then - -- check for dy/dx == 0 - -- if not already determined as near dy/dx=0 - if not dgraph[i][5] then + if l < 4 then -- this is not worth the pain... + for i = 1, l do + table.insert(dgraph,{graph[i][1],graph[i][2],0,0}) + end + else + local yh = func(graph[1][1]-h) + local yhh = func(graph[1][1]-2*h) + if yhh > -math.huge and yhh < math.huge -- if defined at all + and yh > -math.huge and yh < math.huge then + dgraph[1] = {graph[1][1],graph[1][2], + (yhh-8*yh+8*graph[2][2]-graph[3][2])/(12*h), + (-yhh+16*yh-30*graph[1][2]+16*graph[2][2]-graph[3][2]) + /(12*h^2)} + dgraph[2] = {graph[2][1],graph[2][2], + (yh-8*graph[1][2]+8*graph[3][2]-graph[4][2])/(12*h), + (-yh+16*graph[1][2]-30*graph[2][2]+16*graph[3][2]-graph[4][2]) + /(12*h^2)} + else -- take neighbour values + dgraph[1] = {graph[1][1],graph[1][2], + (graph[1][2]-8*graph[2][2]+8*graph[4][2]-graph[5][2])/(12*h), + (-graph[1][2]+16*graph[2][2]-30*graph[3][2] + +16*graph[4][2]-graph[5][2])/(12*h^2)} + dgraph[2] = {graph[2][1],graph[2][2], + (graph[1][2]-8*graph[2][2]+8*graph[4][2]-graph[5][2])/(12*h), + (-graph[1][2]+16*graph[2][2]-30*graph[3][2] + +16*graph[4][2]-graph[5][2])/(12*h^2)} + end + for i = 3, l-2 do + table.insert(dgraph,{graph[i][1],graph[i][2], + (graph[i-2][2]-8*graph[i-1][2]+8*graph[i+1][2]-graph[i+2][2]) + /(12*h), + (-graph[i-2][2]+16*graph[i-1][2]-30*graph[i][2] + +16*graph[i+1][2]-graph[i+2][2]) + /(12*h^2)}) + end + yh = func(graph[l][1]+h) + yhh = func(graph[l][1]+2*h) + if yhh > -math.huge and yhh < math.huge -- if defined at all + and yh > -math.huge and yh < math.huge then + dgraph[l-1] = {graph[l-1][1],graph[l-1][2], + (graph[l-3][2]-8*graph[l-2][2]+8*graph[l][2]-yh)/(12*h), + (-graph[l-3][2]+16*graph[l-2][2]-30*graph[l-1][2] + +16*graph[l][2]-yh)/(12*h^2)} + dgraph[l] = {graph[l][1],graph[l][2], + (graph[l-2][2]-8*graph[l-1][2]+8*yh-yhh)/(12*h), + (-graph[l-2][2]+16*graph[l-1][2]-30*graph[l][2] + +16*yh-yhh)/(12*h^2)} + else + -- take neighbour values + dgraph[l] = {graph[l][1],graph[l][2], + (graph[l-4][2]-8*graph[l-3][2]+8*graph[l-1][2]-graph[l][2]) + /(12*h), + (-graph[l-4][2]+16*graph[l-3][2]-30*graph[l-2][2] + +16*graph[l-1][2]-graph[l][2])/(12*h^2)} + dgraph[l-1] = {graph[l-1][1],graph[l-2][2], + (graph[l-4][2]-8*graph[l-3][2]+8*graph[l-1][2]-graph[l][2]) + /(12*h), + (-graph[l-4][2]+16*graph[l-3][2]-30*graph[l-2][2] + +16*graph[l-1][2]-graph[l][2])/(12*h^2)} + end + -- add information about being extremum / inflection point (true/false) + for i = 1, l do + dgraph[i][5] = false -- dy/dx == 0 ? default, may change later + dgraph[i][6] = false -- ddy/ddx == 0 ? default, may change later + end + for i = 1, l-1 do + -- if no gap is inbetween + if not (dgraph[i+1][1] - dgraph[i][1] > 1.5*h) then + -- check for dy/dx == 0 + -- if not already determined as near dy/dx=0 if dgraph[i][3] == 0 then - dgraph[i][5] = true + if dgraph[i+1][3] == 0 then --take the later + dgraph[i+1][5] = true + dgraph[i][5] = false + else + dgraph[i][5] = true + end elseif abs(dgraph[i][3]*dgraph[i+1][3]) ~= dgraph[i][3]*dgraph[i+1][3] then -- this must be near if abs(dgraph[i][4]) <= abs(dgraph[i+1][4]) then @@ -124,12 +133,10 @@ function diffgraph(func,graph,h) dgraph[i+1][5] = true end end - end - -- check for ddy/ddx == 0 - -- if not already determined as near ddy/ddx=0 - if not dgraph[i][6] then - if abs(dgraph[i][4]*dgraph[i+1][4]) - ~= dgraph[i][4]*dgraph[i+1][4] then -- this must be near + -- check for ddy/ddx == 0 + -- if not already determined as near ddy/ddx=0 + if (not dgraph[i][6]) and (abs(dgraph[i][4]*dgraph[i+1][4]) + ~= dgraph[i][4]*dgraph[i+1][4]) then -- this must be near if abs(dgraph[i][4]) <= abs(dgraph[i+1][4]) then dgraph[i][6] = true else @@ -146,7 +153,11 @@ end -- fits the graph g (up to maxerror) after filling in -- the parameters a, b, c, d -- if the graph is inverted, then isinverse has to be set true -function do_parameters_fit(a,b,c,d,funcstring,funcgraph,maxerror,isinverse) +local function do_parameters_fit(a,b,c,d,funcstring,funcgraph,maxerror,isinverse) + if not (a > -math.huge and a < math.huge and b > -math.huge and b < math.huge and + c > -math.huge and c < math.huge and d > -math.huge and d < math.huge) then + return false + end local funcx = string.gsub(funcstring, "a", a) local funcx = string.gsub(funcx, "b", b) local funcx = string.gsub(funcx, "c", c) @@ -169,7 +180,7 @@ function do_parameters_fit(a,b,c,d,funcstring,funcgraph,maxerror,isinverse) end -- f(x)=a*x^3+b*x+c -function parameters_cubic(xp,yp,xq,yq,xr,yr,xs,ys) +local function parameters_cubic(xp,yp,xq,yq,xr,yr,xs,ys) local a = (((xp^2 * xq) * yr) - ((xp^2 * xq) * ys) - ((xp^2 * xr) * yq) + ((xp^2 * xr) * ys) + ((xp^2 * xs) * yq) - ((xp^2 * xs) * yr) - ((xp * xq^2) * yr) + ((xp * xq^2) * ys) @@ -266,7 +277,7 @@ function parameters_cubic(xp,yp,xq,yq,xr,yr,xs,ys) end -- f(x)=a*x+b -function parameters_affine(xp,yp,xq,yq) +local function parameters_affine(xp,yp,xq,yq) local a = (yp - yq) / (xp - xq) local b = ((xp * yq) - (xq * yp)) / (xp - xq) return a, b @@ -274,21 +285,25 @@ end -- returns true iff the function is of type f(x)=a*x^3+b*x^2+c*x+d -- a, b, c, d being real numbers -function is_cubic(graph,maxerror) +local function is_cubic(graph,maxerror) local l = #graph - local a, b, c, d = parameters_cubic(graph[1][1],graph[1][2], - graph[math.floor(l/3)][1],graph[math.floor(l/3)][2], - graph[math.floor(2*l/3)][1],graph[math.floor(2*l/3)][2], - graph[l][1],graph[l][2]) - return do_parameters_fit(a,b,c,d,"a*x^3+b*x^2+c*x+d",graph, - maxerror,false) + if l < 2 then + return false + else + local a, b, c, d = parameters_cubic(graph[1][1],graph[1][2], + graph[math.floor(l/3)][1],graph[math.floor(l/3)][2], + graph[math.floor(2*l/3)][1],graph[math.floor(2*l/3)][2], + graph[l][1],graph[l][2]) + return do_parameters_fit(a,b,c,d,"a*x^3+b*x^2+c*x+d",graph, + maxerror,false) + end end -- returns true iff the function is of type f(x)=a*x^3+b*x^2+c*x+d -- a, b, c, d being real numbers -- this takes several graph parts -- the idea is to have a possibility to avoid tan(x) -function are_cubic(graphs,maxerror) +local function are_cubic(graphs,maxerror) if is_cubic(graphs[1],maxerror) then if #graphs < 2 then return true @@ -310,21 +325,25 @@ end -- returns true iff the inverse function is of type -- f(x)=a*x^3+b*x^2+c*x+d -- a, b, c, d being real numbers -function is_cuberoot(graph,maxerror) +local function is_cuberoot(graph,maxerror) local l = #graph - local a, b, c, d = parameters_cubic(graph[1][2],graph[1][1], - graph[math.floor(l/3)][2],graph[math.floor(l/3)][1], - graph[math.floor(2*l/3)][2],graph[math.floor(2*l/3)][1], - graph[l][2],graph[l][1]) - return do_parameters_fit(a,b,c,d,"a*x^3+b*x^2+c*x+d",graph, - maxerror,true) + if l < 2 then + return false + else + local a, b, c, d = parameters_cubic(graph[1][2],graph[1][1], + graph[math.floor(l/3)][2],graph[math.floor(l/3)][1], + graph[math.floor(2*l/3)][2],graph[math.floor(2*l/3)][1], + graph[l][2],graph[l][1]) + return do_parameters_fit(a,b,c,d,"a*x^3+b*x^2+c*x+d",graph, + maxerror,true) + end end -- returns true iff the function is of type f(x)=a*x^3+b*x^2+c*x+d -- a, b, c, d being real numbers -- this takes several graph parts -- the idea is to have a possibility to avoid tan(x) -function are_cuberoot(graphs,maxerror) +local function are_cuberoot(graphs,maxerror) if is_cuberoot(graphs[1],maxerror) then if #graphs < 2 then return true @@ -345,7 +364,7 @@ end -- returns true iff function is of type f(x)=a*x+b -- a, b being real numbers -function is_affine(graph,maxerror) +local function is_affine(graph,maxerror) l = #graph local a, b = parameters_affine(graph[1][1],graph[1][2], graph[l][1],graph[l][2]) @@ -357,7 +376,7 @@ end -- p.. control q and r .. s -- with the graph g from index starti to endi -- (looking at the points at roughly t=.33 and t=.67) -function squareerror(f,g,starti,endi,qx,qy,rx,ry) +local function squareerror(f,g,starti,endi,qx,qy,rx,ry) local result = 0 for t = .33, .7, .34 do x = (1-t)^3*g[starti][1]+3*t*(1-t)^2*qx+3*t^2*(1-t)*rx+t^3*g[endi][1] @@ -367,13 +386,105 @@ function squareerror(f,g,starti,endi,qx,qy,rx,ry) return result end -function pointstobezier(qx,qy,rx,ry,sx,sy,rndx,rndy) - return " .. controls (" .. round(qx,rndx) .. "," - .. round(qy,rndy) ..") and (" - .. round(rx,rndx) .. "," - .. round(ry,rndy) .. ") .. (" - .. round(sx,rndx) .. "," - .. round(sy,rndy)..")" +-- converts a table with bezier point information +-- to a string with rounded values +-- the path is reversed, if rev is true +-- e.g. if b = {{0,1},{2,3,4,5,6,7},{8,9,10,11,12,13}} +-- then +-- (0,1) .. controls (2,3) and (4,5) .. (6,7) .. controls +-- (8,9) and (10,11) .. (12,13) +-- will be returned +-- the notation "pgfplots" will change the notation to +-- YES: \addplot coordinates {(0,1) (6,7) (2,3) (4,5) (6,7) (12,13) (8,9) (10,11)} +-- NO: 0 1 \\ 6 7 \\ 2 3 \\ 4 5 \\ \\ 6 7 \\ 12 13 \\ 8 9 \\ 10 11 \\ +-- As pgfplots does not connect the bezier segments +-- reverse paths are not implemented +local function beziertabletostring(b,rndx,rndy,rev,notation) + local bezierstring = "" + if #b > 1 then -- if not empty or single point + if #b == 2 and #b[2] == 2 then -- straight line + if rev then + bezierstring = "(" .. round(b[2][1],rndx) .. "," + .. round(b[2][2],rndy) ..")" + .. " -- (" .. round(b[1][1],rndx) .. "," + .. round(b[1][2],rndy) ..")" + else + if notation == "pgfplots" then + bezierstring = "\\addplot coordinates {(" + .. round(b[1][1],rndx) .. "," + .. round(b[1][2],rndy) .. ") (" + .. round(b[2][1],rndx) .. "," + .. round(b[2][2],rndy) .. ") (" + .. round(b[1][1],rndx) .. "," + .. round(b[1][2],rndy) .. ") (" + .. round(b[2][1],rndx) .. "," + .. round(b[2][2],rndy) .. ") }" + else -- notation = tikz + bezierstring = "(" .. round(b[1][1],rndx) .. "," + .. round(b[1][2],rndy) ..")" + .. " -- (" .. round(b[2][1],rndx) .. "," + .. round(b[2][2],rndy) ..")" + end + end + else + if rev then + bezierstring = "(" .. round(b[#b][#b[#b]-1],rndx) .. "," + .. round(b[#b][#b[#b]],rndy) ..")" -- initial point + for i = #b, 2, -1 do + if #b[i] >= 6 then -- cubic bezier spline + bezierstring = bezierstring .. " .. controls (" + .. round(b[i][3],rndx) .. "," + .. round(b[i][4],rndy) ..") and (" + .. round(b[i][1],rndx) .. "," + .. round(b[i][2],rndy) .. ") .. (" + .. round(b[i-1][#b[i-1]-1],rndx) .. "," + .. round(b[i-1][#b[i-1]],rndy)..")" + else + bezierstring = bezierstring .. " (" + .. round(b[i-1][#b[i-1]-1],rndx) .. "," + .. round(b[i-1][#b[i-1]],rndy) ..")" + end + end + else + if notation == "pgfplots" then + bezierstring = "\\addplot coordinates {" + for i = 1, #b-1 do + if #b[i+1] >= 6 then -- cubic bezier spline + bezierstring = bezierstring .. "(" + .. round(b[i][#b[i]-1],rndx) .. "," + .. round(b[i][#b[i]],rndy) .. ") (" + .. round(b[i+1][5],rndx) .. "," + .. round(b[i+1][6],rndy) .. ") (" + .. round(b[i+1][1],rndx) .. "," + .. round(b[i+1][2],rndy) .. ") (" + .. round(b[i+1][3],rndx) .. "," + .. round(b[i+1][4],rndy) .. ") " + end + end + bezierstring = bezierstring .. "}" + else -- notation = tikz + bezierstring = "(" .. round(b[1][1],rndx) .. "," + .. round(b[1][2],rndy) ..")" -- initial point + for i = 2, #b do + if #b[i] >= 6 then -- cubic bezier spline + bezierstring = bezierstring .. " .. controls (" + .. round(b[i][1],rndx) .. "," + .. round(b[i][2],rndy) ..") and (" + .. round(b[i][3],rndx) .. "," + .. round(b[i][4],rndy) .. ") .. (" + .. round(b[i][5],rndx) .. "," + .. round(b[i][6],rndy)..")" + else + bezierstring = bezierstring .. " (" + .. round(b[i][1],rndx) .. "," + .. round(b[i][2],rndy) ..")" + end + end + end + end + end + end + return bezierstring end -- take end points of a graph g of the function f @@ -381,7 +492,7 @@ end -- without extrema or inflection points inbetween -- and try to approximate it with a cubic bezier curve -- (round to rndx and rndy when printing) -function graphtobezierapprox(f,g,starti,endi,rndx,rndy,maxerror) +local function graphtobezierapprox(f,g,starti,endi,maxerror) local px = g[starti][1] local py = g[starti][2] local dp = g[starti][3] @@ -427,7 +538,7 @@ function graphtobezierapprox(f,g,starti,endi,rndx,rndy,maxerror) end end if err <= maxerror then - return pointstobezier(qx,qy,rx,ry,sx,sy,rndx,rndy) + return {qx,qy,rx,ry,sx,sy} else -- search for an intermediate point where the graph has the same -- slope as the line from the start point to the end point: @@ -440,14 +551,18 @@ function graphtobezierapprox(f,g,starti,endi,rndx,rndy,maxerror) interindex = i end end - return graphtobezierapprox(f,g,starti,interindex,rndx,rndy,maxerror) - .. graphtobezierapprox(f,g,interindex,endi,rndx,rndy,maxerror) + local left = graphtobezierapprox(f,g,starti,interindex,maxerror) + local right = graphtobezierapprox(f,g,interindex,endi,maxerror) + for i=1, #right do --now append the right to the left: + left[#left+1] = right[i] + end + return left end end -- like above but exact for quadratic and cubic (if not inverse) -- resp. exact for squareroot and cuberoot (if inverse) -function graphtobezier(g,starti,endi,rndx,rndy,isinverse) +local function graphtobezier(g,starti,endi,isinverse) local px = g[starti][1] local py = g[starti][2] local dp = g[starti][3] @@ -459,48 +574,24 @@ function graphtobezier(g,starti,endi,rndx,rndy,isinverse) local qy = py+(qx-px)*dp local ry = sy+(rx-sx)*ds if isinverse then - return pointstobezier(qy,qx,ry,rx,sy,sx,rndy,rndx) + return {qy,qx,ry,rx,sy,sx} else - return pointstobezier(qx,qy,rx,ry,sx,sy,rndx,rndy) + return {qx,qy,rx,ry,sx,sy} end end --- reverses a path p e.g. when p = "(0,1) -- (2,3)" --- it returns "(2,3) -- (0,1)" --- or when p = "(0,1) .. controls (2,3) and (4,5) .. (6,7)" --- it returns "(6,7) .. controls (4,5) and (2,3) .. (0,1)" -function reversepath(p) - local r = "" -- will become the reverse path - local temppoint ="" -- will store temporal points like "(0,1)" - local tempbetween = "" -- will store things like " .. controls " - for i = 1, #p do - local c = string.sub(p,i,i) - if c == "(" then - if tempbetween == " .. " then - r = " .. controls " .. r - elseif tempbetween == " .. controls " then - r = " .. " .. r - else - r = tempbetween .. r - end - tempbetween = "" - temppoint = "(" - elseif c == ")" then - r = temppoint .. ")" .. r - temppoint = "" - else - if temppoint == "" then -- not reading a point - tempbetween = tempbetween .. c - else - temppoint = temppoint .. c - end +-- just for debugging: +local function printtable(t) + for i = 1,#t do + for j = 1, #t[i] do + io.write(t[i][j].." ") end + io.write("\n") end - return r end -- main function -function bezierplot(functionstring,xmin,xmax,ymin,ymax) +function bezierplot(functionstring,xmin,xmax,ymin,ymax,notation) local fstringreplaced = string.gsub(functionstring, "%*%*", "^") local f = assert(load("local x = ...; return " .. fstringreplaced)) local isreverse = false @@ -539,11 +630,19 @@ function bezierplot(functionstring,xmin,xmax,ymin,ymax) end local functiontype = "unknown" - local bezierstring = "" + local bezierpoints = {} + -- the bezier path (0,1) .. controls + -- (2,3) and (4,5) .. (6,7) .. controls + -- (8,9) and (10,11) .. (12,13) + -- will be stored as + -- bezierpoints={{0,1},{2,3,4,5,6,7},{8,9,10,11,12,13}} -- go through the connected parts for part = 1, #graphs do local d = diffgraph(f,graphs[part],xstep) + --for i = 1, #d do -- just for debugging + -- print(d[i][1],d[i][2],d[i][3],d[i][4],d[i][5],d[i][6]) + --end -- check for type of function (only for the first part) if part == 1 then if is_affine(d,yerror) then @@ -555,20 +654,20 @@ function bezierplot(functionstring,xmin,xmax,ymin,ymax) end end if functiontype ~= "cuberoot" then -- start with initial point - bezierstring = bezierstring .. "(" .. round(d[1][1],rndx) - .. "," .. round(d[1][2],rndy) .. ")" + bezierpoints[#bezierpoints+1] = {round(d[1][1],rndx), + round(d[1][2],rndy)} end if functiontype == "affine" then - bezierstring = bezierstring .. " -- (" .. round(d[#d][1], - rndx) .. "," .. round(d[#d][2],rndy) ..")" + bezierpoints[#bezierpoints+1] = {round(d[#d][1],rndx), + round(d[#d][2],rndy)} elseif functiontype == "cubic" then local startindex = 1 local extremainbetween = false for k = 2, #d do if d[k][5] then -- extrema extremainbetween = true - bezierstring = bezierstring - .. graphtobezier(d,startindex,k,rndx,rndy,false) + bezierpoints[#bezierpoints+1] = graphtobezier(d, + startindex,k,false) startindex = k end end @@ -584,16 +683,16 @@ function bezierplot(functionstring,xmin,xmax,ymin,ymax) local ry = d[#d][2]+(rx-d[#d][1])*d[#d][3] if math.max(qy,ry) > ymax or math.min(qy,ry) < ymin then - bezierstring = bezierstring ..graphtobezier( - d,startindex,k,rndx,rndy,false) + bezierpoints[#bezierpoints+1] = graphtobezier( + d,startindex,k,false) startindex = k end end end end if startindex ~= #d then -- if no special points inbetween - bezierstring = bezierstring - .. graphtobezier(d,startindex,#d,rndx,rndy,false) + bezierpoints[#bezierpoints+1] = graphtobezier(d, + startindex,#d,false) end elseif functiontype == "cuberoot" then -- we determine a, b, c, d and then @@ -619,8 +718,7 @@ function bezierplot(functionstring,xmin,xmax,ymin,ymax) end end d = diffgraph(finverse,graphinverse,xstep) - bezierstring = bezierstring .. "(" .. round(d[1][2],rndy) - .. "," .. round(d[1][1],rndx) .. ")" -- initial point + bezierpoints[#bezierpoints+1] = {d[1][2],d[1][1]} -- initial point local startindex = 1 for k = 2, #d do if d[k][6] then -- inflection point @@ -633,37 +731,56 @@ function bezierplot(functionstring,xmin,xmax,ymin,ymax) local ry = d[#d][2]+(rx-d[#d][1])*d[#d][3] if math.max(qy,ry) > xmax or math.min(qy,ry) < xmin then - bezierstring = bezierstring..graphtobezier( - d,startindex,k,rndx,rndy,true) + bezierpoints[#bezierpoints+1] = graphtobezier( + d,startindex,k,true) startindex = k end end end if startindex ~= #d then -- if no special points inbetween - bezierstring = bezierstring - .. graphtobezier(d,startindex,#d,rndx,rndy,true) + bezierpoints[#bezierpoints+1] = graphtobezier(d, + startindex,#d,true) end else -- standard case (nothing special) local startindex = 1 for k = 2, #d do if d[k][5] or d[k][6] then -- extrema and inflection points - bezierstring = bezierstring .. graphtobezierapprox( - f,d,startindex,k,rndx,rndy,(ymax-ymin)/(0.5*10^rndy)) + local tobeadded = graphtobezierapprox( + f,d,startindex,k,(ymax-ymin)/(0.5*10^rndy)) + -- tobeadded may contain a multiple of 6 entries + -- e.g. {1,2,3,4,5,6,7,8,9,10,11,12} + for i = 1, math.floor(#tobeadded/6) do + bezierpoints[#bezierpoints+1] = {} + for j = 1, 6 do + bezierpoints[#bezierpoints][j] = tobeadded[(i-1)*6+j] + end + end startindex = k end end if startindex ~= #d then -- if no special points inbetween - bezierstring = bezierstring .. graphtobezierapprox(f,d, - startindex,#d,rndx,rndy,(ymax-ymin)/(0.5*10^rndy)) + local tobeadded = graphtobezierapprox(f,d, + startindex,#d,(ymax-ymin)/(0.5*10^rndy)) + -- tobeadded may contain a multiple of 6 entries + -- e.g. {1,2,3,4,5,6,7,8,9,10,11,12} + for i = 1, math.floor(#tobeadded/6) do + bezierpoints[#bezierpoints+1] = {} + for j = 1, 6 do + bezierpoints[#bezierpoints][j] = tobeadded[(i-1)*6+j] + end + end end end end - if isreverse then - return reversepath(bezierstring) - else - return bezierstring - end + -- only for debugging: + --for i = 1, #bezierpoints do + -- for j = 1, #bezierpoints[i] do + -- print(bezierpoints[i][j]) + -- end + ---print("\n") + --end + return beziertabletostring(bezierpoints,rndx,rndy,isreverse,notation) end -- main program -- @@ -672,25 +789,38 @@ if not pcall(debug.getlocal, 4, 1) then if #arg >= 1 then local xmin = -5 local xmax = 5 - if #arg >= 2 then xmin = arg[2] end + if #arg >= 2 then + local tempfunc = assert(load("return " .. arg[2])) + xmin = tempfunc() + end if #arg >= 3 then if arg[3] == arg[2] then xmax = xmin + 10 else - xmax = arg[3] + local tempfunc = assert(load("return " .. arg[3])) + xmax = tempfunc() end end local ymin = -5 local ymax = 5 - if #arg >= 4 then ymin = arg[4] end + if #arg >= 4 then + local tempfunc = assert(load("return " .. arg[4])) + ymin = tempfunc() + end if #arg >= 5 then if arg[5] == arg[4] then ymax = ymin + 10 else - ymax = arg[5] + local tempfunc = assert(load("return " .. arg[5])) + ymax = tempfunc() end end - print(bezierplot(arg[1],xmin,xmax,ymin,ymax)) + if #arg >= 6 then + notation = arg[6] + else + notation = "tikz" + end + print(bezierplot(arg[1],xmin,xmax,ymin,ymax,notation)) end end diff --git a/Master/texmf-dist/tex/lualatex/bezierplot/bezierplot.sty b/Master/texmf-dist/tex/lualatex/bezierplot/bezierplot.sty index 66f404dfd34..26e76f0e25b 100644 --- a/Master/texmf-dist/tex/lualatex/bezierplot/bezierplot.sty +++ b/Master/texmf-dist/tex/lualatex/bezierplot/bezierplot.sty @@ -1,16 +1,16 @@ \NeedsTeXFormat{LaTeX2e} -\ProvidesPackage{bezierplot}[2018/04/12 bezierplot] +\ProvidesPackage{bezierplot}[2018/06/10 bezierplot] \RequirePackage{xparse} \RequirePackage{iftex} \ifLuaTeX \directlua{require("bezierplot")} - \DeclareExpandableDocumentCommand{\xbezierplot}{O{-5} O{5} O{-5} O{5} m}{% - \directlua{tex.sprint(bezierplot("#5",#1,#2,#3,#4))} + \DeclareExpandableDocumentCommand{\xbezierplot}{O{-5} O{5} O{-5} O{5} O{tikz} m}{% + \directlua{tex.sprint(bezierplot("#6",#1,#2,#3,#4,"#5"))} } \else \let\xpandblinpt\@@input - \DeclareExpandableDocumentCommand{\xbezierplot}{O{-5} O{5} O{-5} O{5} m}{% - \xpandblinpt|"bezierplot '#5' #1 #2 #3 #4" + \DeclareExpandableDocumentCommand{\xbezierplot}{O{-5} O{5} O{-5} O{5} O{tikz} m}{% + \xpandblinpt|"bezierplot '#6' #1 #2 #3 #4 '#5'" } \fi \providecommand\bezierplot{\romannumeral`\^^@\xbezierplot} |