Modul:Svg

Zur Navigation springen Zur Suche springen

Prozedur proc aufrufen: {{#invoke:Svg|proc}} · Modul einbinden: local t = require('Module:Svg')

Hilfeseite Lua · Erweiterung Scribunto

Unterseiten

Verwendung


Aufruf: {{#invoke:Svg|name}} Einbindung: require('Module:Svg')

Hilfeseite Lua · Erweiterung Scribunto

local p = {} -- p stands for package

p.farbliste = { '#ff0000', '#ff1a00', '#ff3300', '#ff4d00', '#ff6600', '#ff8000', '#ff9a00', '#ffb300', '#ffcc00', '#ffe600', '#ffff00', '#e6ff00', '#ccff00', '#b3ff00', '#9aff00', '#80ff00', '#66ff00', '#4dff00', '#33ff00', '#1aff00', '#00ff00', '#00ff1a', '#00ff33', '#00ff4d', '#00ff66', '#00ff80', '#00ff9a', '#00ffb3', '#00ffcc', '#00ffe6', '#00ffff', '#00e6ff', '#00ccff', '#00b3ff', '#009aff', '#0080ff', '#0066ff', '#004dff', '#0033ff', '#001aff', '#0000ff', '#1a00ff', '#3300ff', '#4d00ff', '#6600ff', '#8000ff', '#9a00ff', '#b300ff', '#cc00ff', '#e600ff', '#ff00ff', '#ff00e6', '#ff00cc', '#ff00b3', '#ff009a', '#ff0080', '#ff0066', '#ff004d', '#ff0033', '#ff001a' }

local p = {} -- p stands for package

function p.singlepath(frame)
	local a = frame.args              -- direkt per #invoke: übergeben
	local b = frame:getParent().args  -- an die Vorlage übergebene Parameter
	-- returnd Lilypond path definition as single path even if there are multiple svg paths
	-- pfad or unnamed parameter 1: SVG path definition
	-- pfadname or parameter 2:     name of Lilypond path, default = svgpath
	
	local pfad = trim(b["pfad"] or a["pfad"] or b[1] or a[1] or '')
	if pfad == '' then return "<div class=\"blue-gradient fett noclick lily\" style=\"font-size: 1.4em; padding: 10px\">Pfad fehlt</div>" end
	local pfadname = trim(b["pfadname"] or a["pfadname"] or b[2] or a[2] or 'svgpath') -- name of Lilypond path
	local pathpattern = "<path[%s%c]+d=\"([^\"]*)"
	-- path definitions in SVG syntax, there must be only whiteroom between string "path" and definition
	-- definition ends with doublequotes
	local subpath, wikipath, check
	
	if string.find(pfad, "transform") then
		return "Pfad enthält Transformationen, kann nicht verarbeitet werden"
	end
	check = string.match(pfad:gsub("path",""), "[AaQq]")
	if check then
		return "Pfad enthält Elemente, die derzeit nicht verarbeitet werden können: " .. check
	end
	check = string.match(pfad, "[SCLHV]")
	if check then
		return "Pfad enthält absoute Angaben: " .. check .. " kann nicht verarbeitet werden"
	end
	
	local i
	local lilypath = '\n#(define ' .. pfadname .. " '("
	local path, lily, fehler, minx, maxx, miny, maxy, mysvg
	local svgpath = ''
	local gminx, gmaxx, gminy, gmaxy = 9999, -9999, 9999, -9999
	local minmaxis = ''

	local retour = trim(b["test"] or a["test"] or '')
	local was = trim(b["was"] or a["was"] or '')
	
	for subpath in pfad:gmatch(pathpattern) do
		lily, fehler, minx, maxx, miny, maxy, mysvg = lpath(subpath)
		if type(fehler) == "string" then
			return "Pfad enthält Fehler\n" .. "\n" .. fehler .. "\n" .. subpath ..
			"\nLily Teilpfad\n" .. lily .. "\nLilypond Pfad bisher:\n" .. lilypath
		end
		if tonumber(minx) < gminx then gminx = tonumber(minx) end
		if tonumber(maxx) > gmaxx then gmaxx = tonumber(maxx) end
		if tonumber(miny) < gminy then gminy = tonumber(miny) end
		if tonumber(maxy) > gmaxy then gmaxy = tonumber(maxy) end
		minmaxis = minmaxis .. table.concat({minx, maxx, miny, maxy}, ', ')
		lilypath = lilypath .. lily
		svgpath = svgpath .. mysvg
	end
	if retour ~= '' then return "Liste der Minima/maxima\n" .. minmaxis end
	lilypath = lilypath .. '\n(closepath)\n))\n'
	local width = gmaxx - gminx
	local height = gmaxy - gminy
	svgpath = '\n<svg version=\"1.1\" viewBox=\" ' .. table.concat({ gminx, gminy, width, height}, " ") .. 
	'\" xmlns=\"http://www.w3.org/2000/svg\">\n<path d=\"' .. svgpath .. '\" />\n</svg>'
	wikipath = '{{#tag:tabber|\n' ..
				'Lilypond path definition=<syntaxhighlight lang=\"latex\" style=\"display: inline-block\">' ..
				lilypath .. "</syntaxhighlight>\n{{!}}-{{!}}\n" .. 
				'Lilypond path definition oneclick=\n' ..
				'<syntaxhighlight lang=\"latex\" style=\"display: inline-block\" class=\"oneclick\">' ..
				lilypath .. "</syntaxhighlight>\n{{!}}-{{!}}\n" ..
				'SVG definition=<syntaxhighlight lang=\"xml\" style=\"display: inline-block\">' .. svgpath ..
				"</syntaxhighlight>\n{{!}}-{{!}}\n" ..
				'SVG definition oneclick=<syntaxhighlight lang=\"xml\" style=\"display: inline-block\" class=\"oneclick\">' ..
				svgpath .. "</syntaxhighlight>}}"
				
	return wikipath
end

function lpath(s)
	-- s ... SVG path definition, the part between the quotes following d=
	-- if path does not start with m, m 0 0 is added
	-- if an error occours, we get the path
	
	local mpath = trim(s)
	local number, coords
	-- Define the regular expression pattern
	local pattern = "[cCmMsSlLhHvVzZ][^cCmMsSlLhHvVzZ]*" -- wir zerteilen den String anhand der Pfadelemente und analysieren in einer
	-- separaten Schleife die Zahlen
	local numpattern = "[-]?%d*%.?%d+"
	local mypath, mycube  = {}, {}
	local i, j, elts = 1, 1, 1
	local exppat = "[-+]?%d+%.?%d*[eE][-+]?%d+"
	local error = ""

	if type(mpath) ~= "string" or mpath == '' then return "kein Pfad angegeben", "error" .. tostring(mpath) end

	pfad = mpath:gsub(exppat, function(w) return " " .. w*1 end)  -- wir ersetzen Exponentialzahlen durch Kommazahlen
	-- if 1 then return pfad end
	-- Iterate over the matches
	for match in pfad:gmatch(pattern) do -- ein ganzes Pfadelement
		letter = match:match("[cCmMsSlLhHvVzZ]") -- zulässige Pfadkommandos
		-- a, q fehlen weil keine Umrechnung auf c vorhnaden
		table.insert(mypath, letter)
		local nn = {}
		for number in match:gmatch(numpattern) do
			table.insert(nn, number)
		end
		table.insert(mycube, nn)
	end
	
	local x0, x1, x2, x3, x4, x5, x6  -- Koordinaten der Kontrollpunkte
	local y0, y1, y2, y3, y4, y5, y6  -- Koordinaten; stimmen aus historischen Gründen nicht mit x und y überein
	local mydim, letter

	local actx, acty = 0, 0 -- Start bei 0
	local minx, maxx, miny, maxy = 9999, -9999, 9999, -9999 -- Minima und Maxima auf unmögliche Werte setzen
	
	local lily, svg = '', ''
	
	for i = 1, #mypath do
		letter = mypath[i]
		mydim = #mycube[i]
		--svg = svg .. "\nZahl der Elemente #mycube " .. mydim .. "\nLetter " .. letter
		-------------------------------------------------------------------------------------------------
		if letter == "M" then -- absolute Bewegung
			x1, y1 = unpack(mycube[i], 1, 2)
			actx = x1  -- neue Koordinaten berechnen
			acty = y1
			if tonumber(actx) < minx then minx = tonumber(actx) end  -- Ausmaße des Pfades mitberechnen
			if tonumber(actx) > maxx then maxx = tonumber(actx) end
			if tonumber(acty) < miny then miny = tonumber(acty) end
			if tonumber(acty) > maxy then maxy = tonumber(acty) end

			lily = lily .. "\n(moveto " .. x1 .. " " .. y1 --.. ")" .. " letter i x1 y1" .. letter .. i .. x1 .. y1
			elts = mydim / 2 -- wenn nach einem M noch weitere Elemente ohne Bezeichnung kommen, wird l ergänzt
			svg = svg .. "\nM " .. x1 .. " " .. y1
			for j = 2, elts do
				x1, y1 = unpack(mycube[i], j * 2 - 1, j * 2)
				actx = x1  -- neue Koordinaten berechnen
				acty = y1
				lily = lily .. "\n" .. "(lineto " .. x1 .. " " .. y1.. ")"
				svg = svg .. "\nL " .. x1 .. " " .. y1
				if tonumber(actx) < minx then minx = tonumber(actx) end  -- Ausmaße des Pfades mitberechnen
				if tonumber(actx) > maxx then maxx = tonumber(actx) end
				if tonumber(acty) < miny then miny = tonumber(acty) end
				if tonumber(acty) > maxy then maxy = tonumber(acty) end
			end
		end
		-------------------------------------------------------------------------------------------------
		if letter == "m" then -- m
			x1, y1 = unpack(mycube[i], 1, 2)

			if i == 1 then -- zu Beginn eines Pfades absolute Werte verwenden
				lily = lily .. "\n(moveto " .. x1 .. " " .. y1 .. ")" 
				svg  = svg  .. "\nM " .. x1 .. " " .. y1
				actx = x1
				axty = y1
			else
				lily = lily .. "\n" .. "(rmoveto " .. x1 .. " " .. y1 .. ")"
				svg  = svg  .. "\nm " .. x1 .. " " .. y1
				actx = actx + x1  -- neue Koordinaten berechnen
				acty = acty + y1
			end
			if tonumber(actx) < minx then minx = tonumber(actx) end  -- Ausmaße des Pfades mitberechnen
			if tonumber(actx) > maxx then maxx = tonumber(actx) end
			if tonumber(acty) < miny then miny = tonumber(acty) end
			if tonumber(acty) > maxy then maxy = tonumber(acty) end
			elts = mydim / 2 -- wenn nach einem m noch weitere Elemente ohne Bezeichnung kommen, wird l ergänzt
			for j = 2, elts do
				x1, y1 = unpack(mycube[i], j * 2 - 1, j * 2)
				actx = actx + x1  -- neue Koordinaten berechnen
				acty = acty + y1
				lily = lily .. "\n(rlineto " .. x1 .. " " .. y1 .. ")"
				svg = svg .. "\nl " .. x1 .. " " .. y1
				if tonumber(actx) < minx then minx = tonumber(actx) end  -- Ausmaße des Pfades mitberechnen
				if tonumber(actx) > maxx then maxx = tonumber(actx) end
				if tonumber(acty) < miny then miny = tonumber(acty) end
				if tonumber(acty) > maxy then maxy = tonumber(acty) end
			end
		end
		-------------------------------------------------------------------------------------------------
		if letter == "l" then -- lineto
			if mydim % 2 ~= 0 then
				return lily, "ungültiger Pfad " .. letter .. table.concat( mycube[i], ", " )
			end

			elts = mydim / 2
			for j = 1, elts do
				x1, y1 = unpack(mycube[i], j * 2 - 1, j * 2)
				actx = actx + x1  -- neue Koordinaten berechnen
				acty = acty + y1
				lily = lily .. "\n" .. "(rlineto " .. x1 .. " " .. y1 .. ")"
				svg = svg .. "\nl " .. x1 .. " " .. y1
				if tonumber(actx) < minx then minx = tonumber(actx) end  -- Ausmaße des Pfades mitberechnen
				if tonumber(actx) > maxx then maxx = tonumber(actx) end
				if tonumber(acty) < miny then miny = tonumber(acty) end
				if tonumber(acty) > maxy then maxy = tonumber(acty) end
			end
		end
		-------------------------------------------------------------------------------------------------
		if letter == "L" then -- lineto
			if mydim % 2 ~= 0 then
				return lily, "ungültiger Pfad " .. letter .. table.concat( mycube[i], ", " )
			end

			elts = mydim / 2
			for j = 1, elts do
				x1, y1 = unpack(mycube[i], j * 2 - 1, j * 2)
				actx = x1  -- neue Koordinaten berechnen
				acty = y1
				lily = lily .. "\n" .. "(lineto " .. x1 .. " " .. y1 .. ")"
				svg = svg .. "\nL " .. x1 .. " " .. y1
				if tonumber(actx) < minx then minx = tonumber(actx) end  -- Ausmaße des Pfades mitberechnen
				if tonumber(actx) > maxx then maxx = tonumber(actx) end
				if tonumber(acty) < miny then miny = tonumber(acty) end
				if tonumber(acty) > maxy then maxy = tonumber(acty) end
			end
		end
		-------------------------------------------------------------------------------------------------
		if letter == "v" or letter == "h" then  -- vertikal oder horizonal
			if mydim ~= 1 then -- mehrere h's oder v's hintereinander ergeben keinen Sinn
				return lily, "ungültiger Pfad " .. letter .. table.concat( mycube[i], ", " )
			end
			x1 = mycube[i][1]
			if letter == "h" then -- horizontale Linie
				actx = actx + x1  -- nur x-Koordinate des Endpunktes ändert sich
				lily = lily .. "\n" .. "(rlineto " .. x1 .. " 0)"
				svg = svg .. "\nl " .. x1 .. " 0"
			end
			if letter == "v" then -- vertikale Linie
				acty = acty + x1
				lily = lily .. "\n" .. "(rlineto 0 " .. x1 .. ")"
				svg = svg .. "\nl 0 " .. x1
			end
		end

		-------------------------------------------------------------------------------------------------
		if letter == "s" then  -- spline
			if mydim ~= 4 then
				return lily, "ungültiger Pfad " .. letter .. " hat nicht 4 Angaben " .. table.concat( mycube[i], ", " ) 
			end
			local vorletter
			if i == 1 then -- in case path starts with "s"
				vorletter = "m"	
			else
				vorletter = mypath[i-1]
			end
			if vorletter ~= "c" and vorletter ~= "s" and vorletter ~= "m" and vorletter ~= "l" 
				and vorletter ~= "h" and mypath[i-1] ~= "v" then
				return lily, "Pfadangabe s nur nach c, m, l, v, h oder s zulässig " .. table.concat( mycube[i], ", " )
			end
			x1, x2, x3, x4 = unpack(mycube[i])
			
			actx = actx + x3
			acty = acty + x4 -- neuen aktuellen Endpunkt berechnen, unabhängig von der Umwandlung nach c
			-- wir ergänzen s zu einem c
			-- die Koordinaten des ersten Punktes errechnen sich (relativ) aus
			-- x2 = x0 - x1
			-- y3 = y0 - y1
			-- x0, y0 sind die Koordinaten des Zielpunktes der vorigen Kurve
			-- x1, y1 sind die Koordinaten des 2. Stützpunktes
			if vorletter == "c" then -- voriger Abschnitt war c
				elts = #mycube[i-1] / 6
				y3, y4, y5, y6 = mycube[i-1][elts * 6 - 3], mycube[i-1][elts * 6 - 2], mycube[i-1][elts * 6 - 1], mycube[i-1][elts * 6]
			end
			if vorletter == "s" then -- voriger Abschnitt war s
				y3, y4, y5, y6 = unpack(mycube[i-1])
			end
			if vorletter == "m" or vorletter == "l" or vorletter == "h" or vorletter == "v"
			then -- voriger Abschnitt war m, l, h oder v
				-- c wird folgendermaßen berechnet:
				-- 1. Kontrollpunkt wird 0 0
				-- die vier Kontrollpunkte werden die letzten vier Kontrollpunkte der c-Kurve
				local koordinaten
				koordinaten = table.concat({0, 0, x1, x2, x3, x4},' ')
				lily = lily .. "\n(rcurveto " .. koordinaten .. ")"
				svg = svg .. "\nc " .. koordinaten
			end
			if vorletter == "c" or vorletter == "s" then
				z1 = y5 - y3
				z2 = y6 - y4
				lily = lily .. "\n(rcurveto " .. z1 .. " " .. z2 .. " " .. table.concat(mycube[i],' ') .. ")"
				svg = svg .. "\nc " .. z1 .. " " .. z2 .. " " .. table.concat(mycube[i],' ')
			end
		end
		-------------------------------------------------------------------------------------------------
		if letter == "c" then  -- bezier kurve
			if mydim % 6 ~= 0 then
				return lily, "ungültiger Pfad " .. letter .. table.concat( mycube[i], ", " ) .. "\n" .. output
			end
			-- wenn mehrere c hintereinander, aufdröseln!
			elts = mydim / 6 
			for j = 1, elts do
				x1, x2, x3, x4, x5, x6 = unpack(mycube[i], j * 6 - 5, j * 6)
				coords = { unpack(mycube[i], j * 6 - 5, j * 6) }
				actx = actx + x5 -- Koordinaten des aktuellen Punktes aktualisieren
				acty = acty + x6
				lily = lily .. "\n(rcurveto " .. table.concat(coords,' ') .. ")"
				svg = svg .. "\nc ".. table.concat(coords,' ')
				if tonumber(actx) < minx then minx = tonumber(actx) end  -- Ausmaße des Pfades mitberechnen
				if tonumber(actx) > maxx then maxx = tonumber(actx) end
				if tonumber(acty) < miny then miny = tonumber(acty) end
				if tonumber(acty) > maxy then maxy = tonumber(acty) end
			end
			--svg = svg .. "\nc ".. table.concat(mycube[i],' ')
		end

		-------------------------------------------------------------------------------------------------
		if tonumber(actx) < minx then minx = tonumber(actx) end  -- Ausmaße des Pfades mitberechnen
		if tonumber(actx) > maxx then maxx = tonumber(actx) end
		if tonumber(acty) < miny then miny = tonumber(acty) end
		if tonumber(acty) > maxy then maxy = tonumber(acty) end
		-------------------------------------------------------------------------------------------------
		
		if letter == "z" then -- Pfad wird geschlossen
			svg = svg .. "\nz"
			lily = lily .. "\n(closepath)"
		end
	end	
	if mypath[1] ~= "m" and mypath[1] ~= "M" then
		-- path does not start with m, default added m 0 0
		lily = "(moveto 0 0)\n" .. lily
		svg = "m 0 0\n" .. svg
	end
	
	return lily, false, tostring(minx), tostring(maxx), tostring(miny), tostring(maxy), svg
end

function trim(s)
  -- from PiL2 20.4
  return (s:gsub("^%s*(.-)%s*$", "%1"))
end

return p