Module:Sandbox/Con-struct (t)/Test1/svg-graphs

From Wikimedia Commons, the free media repository
Jump to navigation Jump to search
Lua
CodeDiscussionEditHistoryLinksLink count Subpages:DocumentationTestsResultsSandboxLive code All modules

Documentation for this module may be created at Module:Sandbox/Con-struct (t)/Test1/svg-graphs/doc

Code

-- GraphLineMarker (), create style sheets for line and marker --
--[[
1: graph number
2: sign, if line required
3: sign, if marker required
4: color of border
5: fill color of graph, if available
6: color inside of marker; 'x' means nothing
  ]]
local function GraphLineMarkerStyle (iGraph, bShowLine, bShowMarker, sColor, sGraphColorFill, sMarkerColorFill)
    local sReturn = ''
    local sGraphColor, sFill, sFillMarker, sMarker
    
    if bShowLine then
        sGraphColor = sColor
    else
        sGraphColor = 'none'
    end

    if sGraphColorFill then 
    	sFill = '    fill:            ' .. sGraphColorFill .. ';\n' 
    else 
        sFill = '' 
    end

    if bShowMarker then sMarker = 
        '    marker-start:url(#graph' .. iGraph .. 'markerstretch); marker-mid:url(#graph' .. iGraph .. 'markerstretch); marker-end:url(#graph' .. iGraph .. 'markerstretch);\n' 
    else 
        sMarker = '' 
    end

    sReturn = sReturn ..
        '  .graph' .. iGraph .. 'lineblank { /*-- look of graph ' .. iGraph .. ' --*/\n' ..
        '    stroke:          ' .. sGraphColor .. ';\n' ..
        '  }\n' ..
        '  .graph' .. iGraph .. 'line {\n' ..
        '    stroke:          ' .. sGraphColor .. ';\n' ..
        sFill ..
        sMarker ..
        '  }\n'

    if bShowMarker then 
   	    if not sMarkerColorFill then 
            sMarkerColorFill = sColor
        end

    	sReturn = sReturn ..
        '  .graph' .. iGraph .. 'marker {    /*-- look of marker ' .. iGraph .. ' --*/\n' ..
        '    stroke:          ' .. sColor .. ';\n' ..
        '    fill:            ' .. sMarkerColorFill .. ';\n' ..
        '  }\n'
    end
 
    return sReturn
end


---- GraphUseLine () workaround for constant line thickness on stretched graphs, one line: ----
--[[1: id number, text
	2: delta, px
	3: direction
	4: href of graph
	5: extra attributes
	]]
local function GraphUseLine (sID, dDelta, sDirection, sHref, sExtraAttributes)
    local sTranslate
	
    if sDirection == "y" then 
        sTranslate = '0, ' .. dDelta 
    else 
        sTranslate = dDelta .. ', 0' 
    end
    
    if sExtraAttributes  then 
        sExtraAttributes = ' ' .. sExtraAttributes 
    else 
        sExtraAttributes = "" 
    end
    
    gsDebug = gsDebug .. 'svg-line.GraphUseLine(): sID: ' .. sID .. ', dDelta: ' .. dDelta .. ', sDirection: ' .. sDirection .. '\n'
    return 
	    '  <use id="graphuse' .. sID .. '-line' ..
	    '" transform="translate(' .. sTranslate .. ')"' .. sExtraAttributes .. 
	    ' xlink:href="#' .. sHref .. '"/>\n'
end

---- GraphUseLines (), workaround for constant line thickness on stretched graphs, many lines for one graph ----
--[[1: line number
    2: defined line thickness (dlt), px, e. g. 0.6
    3: required width (rw), %, e. g. 180% means two required lines;  ToDo: remove?
    4: relation of line thickness width/height or height/width, <= 1
    5: direction "x" or "y"
    6: href of graph
    - the line thickness must be reduced (rlt) before to: rlt = dlt / rw * 100, e. g. 0.33
    - the line must be placed
      - at outside of real line thickness, = real line width / 2
      - minus real line thickness / 2
        -> delta = rlt * (1 - rw/100) / 2
    - for lapping: 0.49 but not 0.50
	]]
local function GraphUseLines (iID, dLineThickness, dWidthRequired, dThicknessRelation, sDirection, sHref)
	local sReturn = ""
	local sExtraAttribute = 'class&#61;"graph' .. iID
	
	if not dLineThickness     then dLineThickness     = 1 end
	if not dThicknessRelation then dThicknessRelation = 1 end
	if not sHref              then sHref              = 'graphs' end
	
	if dThicknessRelation > 1.3 then
		sReturn =
		GraphUseLine (iID .. 'p2', round ( 0.49 * dLineThickness * (1 - 1/dThicknessRelation), 3), sDirection, sHref, sExtraAttribute .. 'lineblank"') ..
		GraphUseLine (iID .. 'm2', round (-0.49 * dLineThickness * (1 - 1/dThicknessRelation), 3), sDirection, sHref, sExtraAttribute .. 'lineblank"')
	end
	
	if dThicknessRelation > 2.3 then
		sReturn = sReturn ..
		GraphUseLine (iID .. 'p3', round ( 0.48 * dLineThickness * (1 - 3/dThicknessRelation), 3), sDirection, sHref, sExtraAttribute .. 'lineblank"') ..
		GraphUseLine (iID .. 'm3', round (-0.48 * dLineThickness * (1 - 3/dThicknessRelation), 3), sDirection, sHref, sExtraAttribute .. 'lineblank"')
	end
	
	sReturn = sReturn .. 
	    GraphUseLine (iID .. '-1', 0,                                                              'x',        sHref, sExtraAttribute .. 'line"')       -- none movement of one line
	
    gsDebug = gsDebug .. 'svg-line.GraphUseLines(): iID: ' .. iID .. ', dLineThickness: ' .. dLineThickness .. ', dThicknessRelation: ' .. dThicknessRelation .. '\n'
	return sReturn
end


-------- global functions --------

---- svgChartValuePair (), create a pair of values ----
function svgChartValuePair (sX, sY, iXLen, iXLenInteger, iYLenInteger)
   	if iserror () then return '' end
	local iLen
	
	if sX == nil or sY == nil then return nil end
	
	iLen = iXLenInteger - lenInteger (sX)
--gsDebug = gsDebug .. "   lenInteger: " .. lenInteger (sX) .. ", iLen: " .. iLen .. ", string.sub. " .. string.sub ("abcdefg", 1, iLen) .. ", " .. sX .. ", \n"
	sX = string.sub ("        ", 1, iLen) .. sX                       -- first formatting spaces for the first value
    sX = string.sub (sX .. "        ", 1, iXLen)   -- following formatting spaces for the first value, left side of the string

    iLen = iYLenInteger - lenInteger (sY)
	sY = string.sub ("       ", 1, iLen) .. sY                        -- first formatting spaces for the 2nd value
	
	return '    ' .. sX .. ' ' .. sY .. '\n'
end

-------- public SVG Chart functions --------
function LineMarkerStyles (dChartElementSize, dGraphWidth, dGraphStretchWidth, dGraphStretchHeight, tsGraphValues, tsGraphLine, tsGraphMarker, tsGraphColor, tsGraphFill, tsGraphMarkerFill)
   	if iserror () then return '' end
    local sReturn, i

    sReturn = 
        '  .graphgeneral {         /*-- general look of graphs and markers, e.g. in legend --*/\n' ..
        '    stroke-width:    ' .. LineWidth (dChartElementSize, dGraphWidth) .. ';\n' ..
        '    fill:            none;\n' ..
        '    stroke-linejoin: round;\n' ..
        '    stroke-linecap:  round;\n' ..
        '  }\n' ..
        '  .graphgeneralstretch {  /*-- general look of graphs and markers on a stretched chart --*/\n' ..      -- because vector-effect="non-scaling-stroke" don't work currently
        '    stroke-width:    ' .. LineWidth (dChartElementSize, dGraphWidth, dGraphStretchWidth, dGraphStretchHeight) .. ';\n' ..
        '    fill:            none;\n' ..
        '    stroke-linejoin: round;\n' ..
        '    stroke-linecap:  round;\n' ..
        '  }\n'
 
    for i = 1, MAXCHARTNUMBER do
        if tsGraphValues[i] or tsGraphMarker[i] then sReturn = sReturn ..
            GraphLineMarkerStyle (i, tsGraphLine[i] ~= 'none', tsGraphMarker[i], tsGraphColor[i], tsGraphFill[i], tsGraphMarkerFill[i])
        end
    end
    
	return sReturn
end

---- svgGraphLine (), code for the graph lines ----
function svgGraphLine (iGraphNumber, sColor)
   	if iserror () then return '' end
    return 
        '  &lt;!-- graph ' .. iGraphNumber .. ' -->\n' .. 
        '  <use id="graphuse' .. iGraphNumber .. '" transform="translate(0, 0)" class="graph' .. iGraphNumber .. 'line" xlink:href="#graph' .. iGraphNumber .. '-line"/>\n'
end

---- svgGraphFill (), code for filled graph areas ----
function svgGraphFill (iGraphNumber, sColor)
   	if iserror () then return '' end
    return 
        '  &lt;!-- graphfill ' .. iGraphNumber .. ' -->\n' .. 
        '  <use id="graphfilluse' .. iGraphNumber .. '" transform="translate(0, 0)" class="graph' .. iGraphNumber .. 'line" xlink:href="#graph' .. iGraphNumber .. '-fill"/>\n'
end

---- svgChartValues (), formatted output of the pairs of values for a chart, 2 values per line ----
function svgChartValues (iChartNumber, tsX, tsY, iXLenMax, iXIntegerLen, iYIntegerLen)
   	if iserror () then return '' end
--	local sX, sY
	local sReturn = ""
	local i, sPair
	
	i = 1
--gsDebug = gsDebug .. " svgChartValues (), tiXIntegerLen: " .. tiXIntegerLen[iChartNumber] .. ", tiXLenMax: " .. tiXLenMax[iChartNumber] .. "\n"
	while true do
--		sPair = svgChartValuePair (tsX[i], tsY[i], tiXLenMax[iChartNumber], tiXIntegerLen[iChartNumber], tiYIntegerLen[iChartNumber])
		sPair = svgChartValuePair (tsX[i], tsY[i], iXLenMax, iXIntegerLen, iYIntegerLen)
		if sPair == nil then break end
        sReturn = sReturn .. sPair
		i = i + 1
	end
	
    return sReturn
end

--  --
function svgGraphsDefs (dYMin, tsX, tsY, tsGraphValues, tsGraphFill, tsXFirst, tsXLast, tsGraphText, dGraph1WidthPx, iXLenMax, iXIntegerLen, iYIntegerLen)
   	if iserror () then return '' end
    local i, bGraphsOther, sStroke, sHeadFill, sHeadLine, sGraphEnd, sGraphText, sReturnFill, sReturnLine
    
    sReturnLine     = ''
    sReturnFill     = ''
    bGraphsOther    = false
    for i = MAXCHARTNUMBER, 1, -1 do
        -- charts --
        sStroke = ''
    	if tsGraphValues[i] and tsX[i] and tsY[i] then
--            sReturnLine = sReturnLine .. svgGraph (i, 0)
            -- graph no. 1 --
            if i == 1 then   
                sHeadLine  = '<g id="graph1">\n'
                sHeadFill  = '<g id="graph1-fill">\n'
                sStroke    = 'stroke-width="' .. dGraph1WidthPx .. '" '
            -- all the other graphs --
            else if not bGraphsOther then               -- 1x instructions for all graphs without graph 1
                sHeadLine       = '<g id="graphs">\n'
                sHeadFill       = '<g id="graphs-fill">\n'
                bGraphsOther    = true
            else                                        -- all the graphs without graph 1
                sHeadLine  = ''
                sHeadFill  = ''
            end end
            
            -- comment --
            if tsGraphText[i] then                      
                sGraphText = tsGraphText[i]
            else
                sGraphText = 'graph ' .. i
            end
               
            if i == 1 or (i == 2 and bGraphsOther) then   -- to times a </g> end: for 1st graph and summary of all other graphs
                sGraphEnd = '</g>\n'
            else 
                sGraphEnd = ''
            end

            -- complete svg instructions for every graph --
    		sReturnLine = sReturnLine ..
-- todo stroke-width="0.98
    			sHeadLine ..
    			'  &lt;!-- ' .. sGraphText .. ' -->\n' .. 
       			'  <polyline id="graph' .. i .. '-line" ' .. sStroke .. 'fill="none" points="\n' .. 
    		    svgChartValues (i, tsX[i], tsY[i], iXLenMax, iXIntegerLen, iYIntegerLen) .. 
    		    '  "/>\n' .. 
    		    sGraphEnd
        end

        -- fill the graph area if wanted --
		if tsGraphFill[i] and tsGraphValues[i] then
-- todo stroke-width="0.98
			sReturnFill = sReturnFill ..
    			sHeadFill .. 
    			'  &lt;!-- ' .. sGraphText .. ' -->\n' .. 
    			'  <polyline id="graphfill' .. i .. '-fill" stroke="none" points="\n' .. 
				svgChartValuePair (tsXFirst[i], dYMin, iXLenMax, iXIntegerLen, iYIntegerLen) ..
    		    svgChartValues (i, tsX[i], tsY[i], iXLenMax, iXIntegerLen, iYIntegerLen) ..
				svgChartValuePair (tsXLast[i],  dYMin, iXLenMax, iXIntegerLen, iYIntegerLen) ..
				'  "/>\n' ..
				'</g>\n'
		end
    end
    
    return 
        '&lt;!--====== graph data with origin values, you can manually copy or attach the values here ======-->\n' ..
        '&lt;!-- modify displacement "translate" -->\n' ..
        sReturnFill .. sReturnLine
end


---- GraphUse (), workaround for constant line thickness on stretched graphs: ----
--[[in case that the function vector-effect="non-scaling-stroke" is sometimes implemented in the rsvg lib 
    and that lib is impemented in wikicommons, this workaround should be dropped by
    - remove of the defs command for the lines, implementing vector-effect="non-scaling-stroke" there and 
    - propper scaling of the defined line thicknesses
    - remove all other factors of {{{GraphStretchWidth|100}}}|{{{GraphStretchHeight|100}}}
    1: line number
       dLineWidthPx
    2: DiagramSize, px
    3: href of graphs
    4: GraphStretchWidth, %
    5: GraphStretchHeight, %
    6: alternate line thicknes, %
    ]]
local function svgGraphUse (iID, dLineWidthPx, iDiagramSize, dGraphStretchWidth, dGraphStretchHeight, dThicknessAlternate)
--	local sReturn = ' &nbsp; &lt;!-- graph ' .. iID .. ' -->\n'
    local sHref
	local sReturn = '  &lt;!-- graph ' .. iID .. ' -->\n'
	
--[[	if iID == 1 then
	    sHref   = 'graph' .. iID
	else
	    sHref   = 'graph' .. iID .. '-line'
	end ]]
    sHref   = 'graph' .. iID .. '-line'
	
	if dGraphStretchWidth > dGraphStretchHeight then
		sReturn = sReturn ..
		    GraphUseLines (iID, dLineWidthPx, dGraphStretchWidth , dGraphStretchWidth / dGraphStretchHeight, 'y', sHref)  -- /LineWidth must have the same stroke-width expression as in def "graphgeneral"
	else
		sReturn = sReturn ..
		    GraphUseLines (iID, dLineWidthPx, dGraphStretchHeight, dGraphStretchHeight / dGraphStretchWidth, 'x', sHref)  -- /LineWidth must have the same stroke-width expression as in def "graphgeneral"
	end
	
    gsDebug = gsDebug .. 'svg-line.svgGraphUse(): iID: ' .. iID .. ', dGraphStretchWidth: ' .. dGraphStretchWidth .. ', dGraphStretchHeight: ' .. dGraphStretchHeight .. '\n'
	
	return sReturn
end

 -- function GraphUseLines (iID, dLineThickness, dWidthRequired, dThicknessRelation, sDirection, sHref)



---- svg generation with backward order, required for some visibilities ----
function svgGraphsUse (dXMin, dYMin, dLineWidthPx, dImageSize, tsGraphValues, tsGraphFill, dGraphStretchWidth, dGraphStretchHeight, svgGraphFill)
   	if iserror () then return '' end
    local i, sReturnLine
    local sReturnFill = ''

    sReturnLine = ' \n' .. 
        '<g class="graphgeneralstretch" transform="scale(' ..
        round (     dGraphStretchWidth  / 100, 1) .. ', ' ..
        round (-1 * dGraphStretchHeight / 100, 1) .. ') translate(' .. -1 * dXMin .. ', ' .. -1 * dYMin .. ')">\n'
        
    for i = MAXCHARTNUMBER, 1, -1 do
        -- chart lines --
        if tsGraphValues[i] then
            sReturnLine = sReturnLine .. 
--todo    		    svgGraphUse (i, iDiagramSize, dGraphStretchWidth, dGraphStretchHeight, dThicknessAlternate) (i, gtsX[i], gtsY[i]) .. 
                svgGraphUse (i, dLineWidthPx, dImageSize, dGraphStretchWidth, dGraphStretchHeight, dLineWidthPx) 
        end

        -- fill the chart area if wanted --
        if tsGraphFill[i] and tsGraphValues[i] then
            if sReturnFill == '' then 
                sReturnFill = ' \n' ..
                    '<g class="graphgeneralstretch" transform="scale(' ..
                    round (     dGraphStretchWidth  / 100, 1) .. ', ' ..
                    round (-1 * dGraphStretchHeight / 100, 1) .. ') translate(' .. -1 * dXMin .. ', ' .. -1 * dYMin .. ')">\n'
            end
--sReturnFill = sReturnFill .. ' ' .. i .. ', \n'

            sReturnFill = sReturnFill .. 
            	svgGraphFill (i, tsGraphFill[i])

            if i == 1 then end
        end
    end
    
    -- finalize svg instructions --
    if sReturnFill ~= '' then sReturnFill = sReturnFill .. '</g>\n' end
    if sReturnLine       then sReturnLine = sReturnLine .. '</g>\n' end
    
    gsDebug = gsDebug .. 'svg-line.svgGraphs(): dLineWidth: ' .. dLineWidthPx .. ', dImageSize: ' .. dImageSize .. '\n'
    
    return sReturnFill .. sReturnLine
end