モジュール:Plotter

local p={}
function pick(a,n)
return an+1
end
function loadColorSet(page)
if not(page) then page="" end
if mw.ustring.sub(page,1,7) ~= "Module:" then page="Module:Plotter/DefaultColors" end
local ct=mw.loadData(page)
if not ct then ct=mw.loadData("Module:Plotter/DefaultColors") end
local x=0
local color={}
local name={}
repeat
x=x+1
local n=ctx*2-1
local c=ctx*2
if not (n and c) then break end
table.insert(color,c)
table.insert(name,n)
until false
return color, name
end

function piechartslice(color,percent,radius,link)
radius=radius or 100
local quadrant=math.floor(percent/25)
local sin=math.floor(radius*math.sin(percent*math.pi/50))
local cos=math.floor(radius*math.cos(percent*math.pi/50))
local tan25=math.floor(-1*radius*math.cos(percent*math.pi/50)/math.sin(percent*math.pi/50))
local output,lr,lrv,tv,bw1,bw2,bw3,bw4,bd,lrB,bw2B
local a={} -- throwaway array to make value matrix more apparent
-- quadrant 1 is upper left, quadrant 2 is lower left
lr=pick({'left','right','right','left','left'},quadrant)
lrv=pick({radius,radius,radius,radius,0},quadrant)
tv=pick({radius-sin,0,radius,radius,0},quadrant)
-- border width:bw1 (top) bw2 (right) bw3 (bottom) bw4 (left)
bw1=pick({0,0,-1*sin,radius,0},quadrant)
bw2=pick({0,tan25,-1*cos,0,2*radius},quadrant)
bw3=pick({sin,radius,0,0,2*radius},quadrant)
bw4=pick({cos,0,0,tan25,0},quadrant)
bd=pick({'bottom-','right-','top-','left-',},quadrant)
lrB=pick({'n/a','right','left','left','n/a'},quadrant)
-- right border for second div (the bottom border is radius and others are zero)
bw2B=pick({'n/a',radius,2*radius,2*radius,'n/a'},quadrant)
local output=
if quadrant==1 or quadrant==2 or quadrant==3 then
output=output..
if quadrant==3 then
output=output.. 
end
end
return output
end

function p.piechart(frame)
local parent=frame.getParent(frame) or {}
local color=loadColorSet(frame.args.colorset or parent.args.colorset) or {'red','green','blue','yellow','fuchsia','aqua','brown','orange','purple','sienna'}
local value={}
local label={}
local link={}
local slicecount=0
local thumb,nowiki,radius
if parent.args then
thumb=parent.args.thumb
nowik=parent.args.nowiki
radius=parent.args.radius
end
thumb=frame.args.thumb or thumb
nowiki=frame.args.nowiki or nowiki
radius=frame.args.radius or radius or 100
radius=tonumber(radius)
if radiusslicecount then slicecount=k end -- not using #value to avoid randomness if some values are left out
else k=tonumber(mw.ustring.match(i,"label(%d*)"))
if k then labelk=j
end
end
end
end
--- innermost absolute div around circle, then a second thumbcaption div around legend.  Note (/div)(div) at core between circle and legend.  The rest are accreted around this center.
output='link=    '
for i,j in pairs(frame.args or {}) do -- supersede parent.args values
local k=tonumber(mw.ustring.match(i,"color(%d*)"))
if k then colork=j or ""
else k=tonumber(mw.ustring.match(i,"value(%d*)"))
if k then
valuek=tonumber(j)
if k>slicecount then slicecount=k end -- not using #value to avoid randomness if some values are left out
else k=tonumber(mw.ustring.match(i,"label(%d*)"))
if k then labelk=j or ""
else k=tonumber(mw.ustring.match(i,"link(%d*)"))
if k then linkk=j or ""
end
end
end
end
end
local valuesum=0 -- sum of all slices
local imgmap="" -- beginning of a polygon specification for 
for slice=1,slicecount do
if valueslice then
if linkslice then
-- center of the circle, NOTE coords are relative to 600 px image before scaling NOT the radius
imgmap=imgmap.."poly 300 300" 
for x=valuesum,valuesum+valueslice do
local sin=math.floor(300*math.sin(x*math.pi/50))
local cos=math.floor(300*math.cos(x*math.pi/50))
imgmap=imgmap.." "..300+cos.." "..300-sin
end
imgmap=imgmap.." "..linkslice.."\n"
end
valuesum=valuesum+valueslice
output=piechartslice(colorslice,valuesum,radius)..output.."legend|"..(colorslice or "").."|"..(labelslice or "").." ("..valuesum.."%)"
end
end
--- imagemap has its own absolute div to position with a separate transparent image
imgmap='\n\nFile:transparent600.gif|'..2*radius..'px\n'..imgmap..'desc none\n'
if #link==0 then imgmap="" end -- make sure imgmap is blank if no links
--- outer thumb tleft/tright is float/clear left or right
--- thumbinner encapsulates the graph
--- third relative div container ends in the middle of ..output..
--- next third div style "thumbcaption" begins in ..output..
--- all three end at end
output='   '..output..'Other ('..tostring(math.floor((100-valuesum)*1000000)/1000000)..'%)'
output=mw.ustring.gsub(output,"", imgmap)
if nowiki then return frame.preprocess(frame,"") else return frame.preprocess(frame,output) end
end

function p.main(frame)
local args=frame.args
local parent=frame.getParent(frame)
local pargs=parent.args or {}
local icon=args.icon or pargs.icon
local iconradius=args.iconradius or pargs.iconradius or 10
local lineicon=args.lineicon or pargs.lineicon or "•"
local lineiconradius=args.lineiconradius or pargs.lineiconradius or 5
local linefix=iconradius-lineiconradius
local plotsizex = args.plotsizex or pargs.plotsizex or 100
local plotsizey = args.plotsizey or pargs.plotsizey or 100
local plotstep = args.plotstep or pargs.plotstep or 10
local output = 
if (args2 or pargs2) ~= nil then
local x=(args1 or pargs1)+0
local y=(args2 or pargs2)+0
local xmin = x
local xmax = x
local ymin = y
local ymax = y
local index = 3
while (argsindex+1 or pargsindex+1) ~= nil do
local x=(argsindex+0 or pargsindex+0)
local y=(argsindex+1+0 or pargsindex+1+0)
if (x  xmax) then xmax = x end
if (y  ymax) then ymax = y end
index = index + 2
end
local lastx=0
local lasty=0
if args2 ~= nil then
local x=(args1 or pargs1)+0
local y=(args2 or pargs2)+0
local plotx=math.floor(plotsizex*(x-xmin)/(xmax-xmin))
local ploty=math.floor((plotsizey-plotsizey*(y-ymin)/(ymax-ymin)))
output = output ..  .. icon .. ""
lastx = plotx
lasty = ploty
end
index = 3
while (argsindex+1 or pargsindex+1) ~= nil do
local x=(argsindex or pargsindex)+0
local y=(argsindex+1 or pargsindex+1)+0
local plotx=math.floor(plotsizex*(x-xmin)/(xmax-xmin))
local ploty=math.floor((plotsizey-plotsizey*(y-ymin)/(ymax-ymin)))
if plotstep+0 ~= 0 then
local delx=plotx-lastx
local dely=ploty-lasty
plotdist=math.sqrt(delx*delx+dely*dely)
plotparm=plotdist-iconradius-plotstep/2
while plotparm>iconradius+lineiconradius+plotstep/2 do
output = output ..  .. lineicon .. ""
plotparm = plotparm - plotstep
end
lastx = plotx
lasty = ploty
end
output = output ..  .. icon .. ""
index = index + 2
end
else output = "error"
end
output = output .. ""
return output
end
-- data structure is
-- datayx.value
-- maxyvaly
-- datay.color
-- datay.legend
-- data.legendx
function p.bar(frame)
local debuglog=""
local args=frame.args
local parent=frame.getParent(frame)
local pargs=parent.args or {}
local delimiter = args.delimiter or pargs.delimiter or ","
local width = args.width or pargs.width or 200
local height = args.height or pargs.height or 200

---- Set up the table of "norms".  Series 1 to N normalize to (%d+)
local normalize = args.normalize or pargs.normalize or ""
local prowl=mw.ustring.gmatch(normalize,"(%d+)")
norm={}
local ngroup={} -- ngroupyseries identifies an index for ymax
local nngroup=0 -- the current maximum ngroup assigned
repeat
local t=prowl()
if not(t) then break end
t=tonumber(t)
table.insert(norm,t)
until false

--- import the actual data in group1 .. groupN
local yseries=0;local x=0
local data={} -- main data storage array
local maxy=0;local maxx=0; local maxyval={}; local minyval={} -- keeping these out of the data array after being driven half mad giving them cutesy names in the array!
repeat
yseries=yseries+1
datayseries={}
--- pull in the "groupN" data (delimited) --> text
local text=args"group"..yseries -- each _group_ is a group of x-values in a y-series
if not (text) then maxy=yseries-1 break end
---- pull in the originN=some number
datayseries.origin=args"origin"..yseries or 0
datayseries.origin=tonumber(datayseries.origin)
datayseries.max=datayseries.origin;datayseries.min=datayseries.origin
debuglog=debuglog.."I"..yseries..tostring(normyseries)
--- set ngroupyseries to whatever its norm points at, or new
if normyseries
then if ngroupnormyseries
then ngroupyseries=ngroupnormyseries
else nngroup=nngroup+1
ngroupyseries=nngroup
end
else ngroupyseries=1 -- if no norm specified, just dump to the first series group
end
---- pull in the actual values
prowl=mw.ustring.gmatch(text,"(^" .. delimiter .. "+)")
x=0
repeat
x=x+1
datayseriesx={}
datayseriesx.value=prowl()
debuglog=debuglog.."V"..x..yseries..tostring(datayseriesx.value)
if not(datayseriesx.value) then if x>maxx then maxx = x-1 end; break end
datayseriesx.value=tonumber(datayseriesx.value)
if datayseries.max then if datayseriesx.value>datayseries.max then datayseries.max=datayseriesx.value end else datayseries.max=datayseriesx.value end
if datayseries.min then if datayseriesx.value"..tostring(datayseries.max)
if maxyvalngroupyseries
then if datayseries.max>maxyvalngroupyseries
then maxyvalngroupyseries=datayseries.max
end
else maxyvalngroupyseries=datayseries.max;debuglog=debuglog.."A"..tostring(datayseries.max)..tostring(datayseries.min)
end
if minyvalngroupyseries
then if datayseries.min
local output=
local topreserve=20*(maxy)
local bottomreserve=20
local leftreserve=20
local rightreserve=20
local reducedheight=height-topreserve-bottomreserve
local reducedwidth=width-leftreserve-rightreserve
local ew=math.floor(reducedwidth/((maxx)*(maxy+1)))
for y = 1,maxy do
for x = 1, maxx do
debuglog=debuglog..y..x..tostring(ngroupy)..tostring(datayx) .. tostring(maxyvalngroupy)..tostring(minyvalngroupy)
if datayx and maxyvalngroupy
then local pw=(datayx.value-datay.origin)/(maxyvalngroupy-minyvalngroupy) -- proportion of value to the max value for that y-series
local po=(datay.origin-minyvalngroupy)/(maxyvalngroupy-minyvalngroupy)
local eh=math.floor(pw*reducedheight)
local et=topreserve+math.floor(reducedheight - eh - po*reducedheight)
if eh'
end -- if datayx and maxvalngroupy
end -- for x = 1, maxx
end -- for y=1,maxy
---- draw the ylegends
for x = 1,maxx do
output=output .. ..data.legendx..
end
for y = 1,maxy do
output=output .. .. (datay.legend or "") ..
local point={minyvalngroupy,datay.origin,maxyvalngroupy}
for i,j in ipairs(point) do
local po=(j-minyvalngroupy)/(maxyvalngroupy-minyvalngroupy)
local et=topreserve+math.floor((1-po)*reducedheight)
debuglog=debuglog.."pass" .. y .. ngroupy
if tonumber(ngroupy)==1
then debuglog=debuglog.."left";output=output .. .. j .. 
else debuglog=debuglog.."right";output=output .. .. j .. 
end
end
end
debuglog=debuglog..tostring(maxyval1)..tostring(maxyval2)..tostring(maxyval3)..tostring(data1.max)..tostring(data2.max)..tostring(data3.max)..tostring(minyval1)..tostring(minyval2)..tostring(minyval3)..tostring(data1.min)..tostring(data2.min)..tostring(data3.min)..data.legend1..data.legend2..data.legend3
output = output .. "\n"
if (args.debug or pargs.debug) then output=output..debuglog end
return output
end
return p