Module:Tincture

From Wikimedia Commons, the free media repository
Jump to navigation Jump to search
Lua

CodeDiscussionEditHistoryLinksLink count Subpages:DocumentationTestsResultsSandboxLive code All modules

See Template:{{Tincture}}

GPL palettes[edit]

In August 2020 a substantial expansion occurred, to generate the GPL palettes, wished by users for heraldry works. A new (automatic and optional) Tincture-parameter 'gpltab=' is used to control whether tincture boxes are generated for files, or table data is generated for GPL. Conversion functions care for hexadecimal/decimal conversion as needed for gpl, and additional parameters are passed to the subtemplates. A complete instruction how to generate such palettes can be found in Commons talk:Colour palettes; it contains also some hints how to add new colour variations. To use the template:Tincture for table creation, the parameter |gpltab= can be used:

  1. gpltab=1 default value, is automatically set to tincturize files
  2. gpltab=2 value to generate tables
  3. gpltab=3 value to do both, is automatically set on that page

Because this parameter needs never to be set, except for test cases, it is not described above.



The tincture field value may be empty, or contain

  • tincture codes
  • a palette code
  • control codes (+ * - ~ ?)
  • + create field name
  • * force tincture treating
  • - suppress tincture-cat
  • ~ suppress palette-cat
  • ? tinctures uncategorizeable

Charges (coa elements) need neither tinctures nor palettes, but it's no error when they do; tincture treatment can be forced.


Two kinds of categories are maintained:

1) Palette category
  • Standard: Category "Tinctures (Xxxxxx)" always when a palette code "XY" is specified, independent about specified tinctures
    (it is no error when tinctures are missing, or their category output may be suppressed)
  • no action when no tincture field value at all is specified
  • no action when palette category output is suppressed
  • category "Tinctures (Palette unknown)" when category does not exist, else
  • category "Tincture palette not specified" when a tincture field value is specified without a palette code
2) Tincture category
  • Standard: Category like "Aaaaa, Bbbb and Ccccc in heraldry", dependent on the specified tincture(s)
  • no action z-cat when a tincture field value is specified without any tinctures
  • no action s-cat when tincture category output is suppressed, else
  • category "Tinctures not specified" when no tincture field value at all is specified
  • category "Undefined color combinations of heraldic shields" for unknown combinations


Code

local p = {}  -- Tincture

-------------- locals 
local function draw(color, ss, tc, pf, gt)
	if mw.title.new('Tincture/draw' .. ss,10).exists then
		return mw.getCurrentFrame():expandTemplate{ title = 'Tincture/draw' .. ss, args = { color, gt, tc = tc, pf = pf } }
	else
		return '<strong><span style="color: red; text-decoration: inherit;">Bad color definition in [[Template:Tincture]]: '.. ss ..'</span></strong>'
	end
end -- local function draw
-- 
local function sortc ( itab, otab, ccode )
	local tinct = ccode;
	if	tinct == 'A'	then tinct = 'a';	end	-- in categories treat argent-dark like argent (2023-05-10)
	if  tinct == 'l' 
	and ss == 'CH'	then tinct = 'b';	end		--
	for i, v in ipairs(itab) do
		if v == ccode then 
			table.insert(otab, tinct)
			return true
		end
	end
	return false
end -- local function sortc
--
local function category(colors, ss, tc, cat, no_error_cat)
	return mw.getCurrentFrame():expandTemplate{ title = 'Tincture/cat' .. ss, args = { table.concat(colors, '/'), tc = tc, cat = cat, ['no error cat'] = no_error_cat and '1' or nil } }
end -- local function category
--
local function paletcat(ss, cat)
	return mw.getCurrentFrame():expandTemplate{ title = 'Tincture/catP', args = { ss, cat = cat } }
end -- local function p-category
--
local function pnotspec()
	local exp = '';
	if	mw.ustring.lower(mw.ustring.sub(mw.title.getCurrentTitle().text,-4)) ~= '.svg' then	
		exp = '_(nonSVG)';
	end
	return mw.getCurrentFrame():expandTemplate{ title = 'Igen/cat', args = {'Tincture palette not specified'..exp } }
end -- local function pnotspec
--
local function defnonsvg()
	return mw.getCurrentFrame():expandTemplate{ title = 'Igen/cat', args = {'Tinctures (Defined nonSVG)' } }
end -- local function defnonsvg
--
local function tnotspec()
	local exp = '';
	if	mw.ustring.lower(mw.ustring.sub(mw.title.getCurrentTitle().text,-4)) ~= '.svg' then	
		exp = '_(nonSVG)';
	end
--	return mw.getCurrentFrame():expandTemplate{ title = 'Igen/cat', args = {'Tinctures not specified'..exp } }
	return mw.getCurrentFrame():expandTemplate{ title = 'Igen/cat', args = {'No tincture specified'..exp } }
end -- local function tnotspec
--
local function tsuppress()
	local exp = '';
--	if	mw.ustring.lower(mw.ustring.sub(mw.title.getCurrentTitle().text,-4)) ~= '.svg' then	
--		exp = '_(nonSVG)';
--	end
	return mw.getCurrentFrame():expandTemplate{ title = 'Igen/cat', args = {'Tincture categorizing suppressed'..exp } }
end -- local function t-suppress
--
local function tinc_cat(colors)
	return mw.getCurrentFrame():expandTemplate{ title = 'Tincture/cat0', args = { table.concat(colors, '/') } }
end -- local function t-category
--
local function unab_cat(colors)			--	colors for catsort ?
	return mw.getCurrentFrame():expandTemplate{ title = 'Igen/cat', args = {'Tinctures uncategorizeable' } }
end -- local function unable to categorize

-- local function: converts 'ddd ddd ddd'  to  '#rrggbb' (or '#rgb')
local function convdh ( p1, p2, p3 )
	local lpar = {}; -- separated by either pipe, slash, minus, comma or space 
	local dect = {}
	local hext = {}
	local numb = {}
	local same = true
	lpar [1] = mw.text.trim ( p1 );
	if p3 == nil then				 -- split-pattern: REGEXP won't work
		if p2 ~= nil then 
			lpar[1] = lpar[1] .. '-' .. mw.text.trim(p2)
		end
		lpar[1] = mw.ustring.gsub (lpar[1], '-', '/', 3)
		lpar[1] = mw.ustring.gsub (lpar[1], ',', '/', 3)
		lpar[1] = mw.ustring.gsub (lpar[1], ' ', '/', 3)
		dect = mw.text.split(lpar[1] or '1/2/3', '/');
	else
		lpar [2] = mw.text.trim ( p2 );
		lpar [3] = mw.text.trim ( p3 );
		dect = lpar;
	end
	for i = 1, 3 do
		numb[i] = tonumber( dect[i] )
		if numb[i] == nil or numb[i] > 255 then
			error (i .. 'value "' .. dect[i] or '?' .. '" cannot be converted to hexadecimal')
--				..dect[1]..','..dect[2]..','..dect[3]..'.'..i)
		end
		hext [i] = mw.ustring.format ( "%X", numb[i] )
		if numb[i] < 16 then hext[i] = '0' .. hext[i] end;
		if mw.ustring.byte ( hext [i], 1 )  ~= mw.ustring.byte ( hext[i], 2 )  then
			same = false
		end
	end
	if same then 
		hext[1] = mw.ustring.sub (hext[1], 2)
		hext[2] = mw.ustring.sub (hext[2], 2)
		hext[3] = mw.ustring.sub (hext[3], 2)
	end
	return '#'..hext[1]..hext[2]..hext[3]
end -- local function convdh

--
local function cats ( titl, outs, tinc )
	if mw.ustring.find (titl, tinc .. ',') 
	or mw.ustring.find (titl, tinc .. ' ') then 
		outs = outs .. ', ' .. tinc;
	end
	return outs;	
end -- local function cats


-------------------- local / global ----------``-------------------------------
-- local / global function: converts h → d: #rrggbb or #rgb to table {rr, gg, bb}
function p.convht ( frame )
	local gpar = {};	
	local hexv =  nil;
	local hexi = '000';
	local hwxt = '0';
 	local gpar = frame.args;
 	if gpar then 
 		hexv = tostring (gpar[1]);	-- global
 	else	 
		hexv = tostring ( frame );	-- local
 	end

	hexv = mw.text.trim( hexv )
	if  mw.ustring.sub ( hexv, 1, 1 ) == '#' then 
		hexi = mw.ustring.sub (hexv, 2)
		hext = '1'
	elseif mw.ustring.sub ( hexv, 1, 3) == '\\35' then 
		hexi = mw.ustring.sub (hexv, 4)
		hext = '2'
	elseif mw.ustring.sub ( hexv, 1, 5) == '&#35;' then 
		hexi = mw.ustring.sub (hexv, 6)
		hext = '3'
	elseif mw.ustring.sub ( hexv, 1, 6) == '&#035;' then 
		hexi = mw.ustring.sub (hexv, 7)
		hext = '4'
	elseif mw.ustring.sub ( hexv, 1, 6) == '&#x23;' then 
		hexi = mw.ustring.sub (hexv, 7, #hexv)
		hext = '5'
	elseif mw.ustring.sub ( hexv, 1, 7) == '&#x023;' then 
		hexi = mw.ustring.sub (hexv, 8, #hexv)
		hext = '6'
	else	
		hext = '9'
		error ('value "' .. hexv .. '" cannot be converted to decimal ' .. #hexv )
	end
    if #hexi ~= 3 and #hexi ~= 6 then
		error ('value "' .. hexi .. '" with length ' .. #hexi .. ' are invalid' )
	end
--	error ('value "' .. hexv .. '" type ' .. hext .. ' = ' .. hexi)
	local dec  =  {};
	for i = 1, 3 do
		if #hexi == 3 then
			dec [i] = tonumber ( mw.ustring.sub (hexi, i, i)..mw.ustring.sub (hexi, i, i), 16 ) 
		else
			dec [i] = tonumber ( mw.ustring.sub (hexi, 2*i - 1, 2*i), 16 ) 
		end
	end	
	return dec;
end -- function convht

-- local / global function: converts h ← d '#rrggbb' or '#rgb' and returns 'ddd ddd ddd'
function p.convhd ( frame )
	local gpar = frame.args;
	if gpar then decval = p.convht (gpar[1]);	-- global
		else	 decval = p.convht ( frame );	-- local
	end
	return decval[1]..' '..decval[2]..' '..decval[3]
end -- function convhd

-- local / global function: converts h → d'#rrggbb' or '#rgb' and returns 'ddd ddd ddd' formatted
function p.convhdf ( frame )
	local gpar = frame.args;
	if gpar then dtab = p.convht (gpar[1]);	-- global
			else dtab = p.convht ( frame );	-- local
	end
	local dtxt = '&nbsp;'
	for i = 1, 3 do
		if dtab[i] < 100 then
			if dtab[i] < 10 then 
				dtxt = dtxt .. ' '
			end
			dtxt = dtxt .. '&nbsp;'
		end
		dtxt = dtxt .. ' ' .. tostring ( dtab [i] ) 
	end
	local contrast = '0';
	if  dtab [1] + dtab [2] + dtab [3] < 400 then 
		  contrast = 'F'; 
	end
-- TEST ----------
--	local contval = dtab [1] + dtab [2] + dtab [3]
-- 	contrast = contrast .. ' - ';
--	if contval < 100 then
--		if contval < 10 then 
--			contrast = contrast .. '&nbsp;'
--		end
--		contrast = contrast .. '&nbsp;'
--	end
-- 	contrast = contrast .. tostring ( contval ); 
-- TEST ----------
	dtxt = contrast .. dtxt; 
	return dtxt
end -- function convhdf

-- global function returns contrast color
function p.titcolor ( frame )
	local gpar = frame.args
	local decval = p.convhdf ( gpar[1] );
	if mw.ustring.sub ( decval, 1, 1 ) == 'F'
		then return 'FFF'
		else return '000'
	end
end -- function titcolor - does not work perfectly

-- global function tbcbox: returns a Tbc box
function p.tbcbox ( frame )
	local gpar = frame.args
	local hstr = convdh ( gpar[1], gpar[2], gpar[3] )	
	return frame:expandTemplate { title = 'colorbox', args = { hstr, title = '"' .. hstr ..'"' } }
end -- function tbxbox

-- global function convgpl: gets #rgb, contrast, name; returns line formatted
function p.convgpl ( frame )
	local gpar = frame.args;
	local line = p.convhdf ( gpar [1] );						-- convert #rgb  
	local expl = mw.ustring.sub ( line, 2) .. ' ' .. gpar [1];	-- d d d   #rgb
	if #gpar[1] == 4 then
		expl = expl .. '&nbsp; &nbsp;'
	end
	local contrast = gpar [2] or '#001'
	expl = expl .. ' '				--	.. contrast;								-- contrast		-test 
--	if mw.ustring.sub ( contrast, 2, 2) == mw.ustring.sub ( line, 1, 1) 
--		then	expl = expl .. ' '	--	'{{mono|1= }}'
--		else	expl = expl .. '·'	--	'{{mono|1=·}}''
--	end
	expl = expl .. gpar [3];									-- name
	return expl
end -- function convgpl

-- global function convert:  gets 3 num, returns hex and dec formatted
function p.convert ( frame )
	local gpar = frame.args;
	local hcod = convdh ( gpar[1], gpar[2], gpar[3] );
	local fnum = p.convhdf (hcod)
	return '&#35;' .. mw.ustring.sub ( hcod, 2 ) .. mw.ustring.sub ( fnum, 2);
end -- function convert

--  ============================================================================
--  ============================================================================
--  main function tincture
function p.main (frame)
	local getArgs = require( 'Module:Arguments' ).getArgs
	local args = getArgs(frame)
	   ss = args.ss  or '0'
	if ss == '≈' then ss = '0' end
	local top = ''
	if args.s == 'f' then top = 'flag ' end
	local cm = args.cm or ' ';
	local tc = args.tc
	local pf = args.pf
	local gt = args.gpltab or ""
	local align = 'tincturebox-left'
	local InFi = (args['+'] == '+')
	local cols = args
	local tincunt = 0;
	local colors = {}
	local box = {}
	local tab = {}
	local out = {}
	local ordtab = { }
--	local ssytab = { ' ',  }	--	table of all palettes: catyes
--	local ssntab = { '0',  }	--	table of all palettes: catnot
	local gpltab = false
	local insert = false
	local gennot = false		--	generell   = don't t-cat
	local genyes = true			--	generell   = force t-cat
	local catnot = false		--	indiv. '-' = don't t-cat
	local catyes = false		--	indiv. '*' = force t-cat
	local catimp = false		--	indiv. '?' = not t-categorizeable 		
	local nopcat = false		--	indiv. '-' = don't p-cat
	
	if gt == "" then 
		if mw.title.getCurrentTitle().namespace == 4 then gt = '3' 
		else gt = '1'
		end
	end
	if gt == "2" or gt == "3" then
		InFi   = true
		gpltab = true
	end
	if cols.ss	then
		ss =  cols.ss 
	end 	

	if type(args[1]) == 'string' and args[1]:sub(1, 6) == '<table' then
		return args[1]
	end

	if args[2] == nil  then
		cols = mw.text.split(args[1] or '', '%s*/%s*')
	end
	for _, v in ipairs(cols) do
		if not v or v == '' then
			break			--	empty par
		elseif v == '-' then
			   v =  '0'		--  t-cat suppression
			   catnot = true
		elseif v == '~' then
			   v =  '0'		--  p-cat suppression
			   nopcat = true
		elseif v == '*' then
			   v =  '0'		--  t-cat forcing
			   catyes = true
		elseif v == '?' then
			   v =  '0'		--  not t-catable
			   catimp = true
		elseif v == '+' then
			InFi = true
		elseif mw.ustring.sub( v, 1, 3 ) == 'ss=' then  -- any case when ss=
			ss =  mw.ustring.sub( v, 4 )
		elseif mw.ustring.len( v ) >= 2 
		   and v == mw.ustring.upper( v ) then  --	belongs to character class %u
				ss = v							--	tincture palette
		else
			table.insert(colors, v)				--	it's a tincture 
			tincunt = tincunt + 1
		end
	end

	ss = mw.ustring.upper( ss )					--	final formatting
----			general yes/not depending on ss
--	for i = 1, #ssytab do			--	force the specified palette ?
--		if ss == ssytab [i] then genyes = true; end 
--	end
--	for i = 1, #ssntab do			--	exclude the specified palette ?
--		if ss == ssntab [i] then gennot = true; end 
--	end
	if	args.el == "y" then gennot = true; end	--	exclude coa elements 
--				indiv. precedes gener.
	if	catnot == false	then
		if	catyes == false	then				--	expl. indiv. "yes" ?
			catnot  = gennot 
		end
	end
	if	catyes == false	then
		catyes  = genyes
	end

--  0) headline and boxes
	if gt == "2" or gt =="3" then
		table.insert(out, frame:expandTemplate{title='=', args={'<h4>GPLtab '..draw('gpltabnam',ss,'','','tab')..'</h4>'}})
	end
	for i, v in ipairs(colors) do
		box[i] = draw(v, ss, tc, pf, 'box')
	end

-- 1) categories: p_cat, t_cat
	if	gpltab == false					--	either palette or tincture
	and args.ns == "6" then			
		if	nopcat ~= true	then		--	when not suppressed:
			if	ss ~= '0'	then		--	Palette def	(^GN)
				table.insert(box, paletcat( ss, args.cat ))
				if	mw.ustring.lower(mw.ustring.sub(mw.title.getCurrentTitle().text,-4)) ~= '.svg' then	
					table.insert(box, defnonsvg() )
				end
			else						--	Palette not specified
				if	args.el ~= "y" 
				and	catimp	== 	false 
				and	tincunt ~= 0	then
					table.insert(box, pnotspec() )
				end
			end
		end
										--	Tincture categorizaton
		if	catnot == true then   		--	suppressed ?
			if args.el ~= "y" then		--	when not an element
				table.insert(box, tsuppress() )		--	categorization suppressed
			end
		else
			if	catimp == true then 	--	cat impossible ?
				table.insert(box, unab_cat() )		--	categorizationt unable
			elseif tincunt > 0 then		--	sorted table
				insort = sortc (colors, ordtab, 'a' )
				insort = sortc (colors, ordtab, 'A' )
				insort = sortc (colors, ordtab, 'o' )
				insort = sortc (colors, ordtab, 'b' )
				insort = sortc (colors, ordtab, 'B' )
				insort = sortc (colors, ordtab, 'l' )	--	l-azure (CH)
				insort = sortc (colors, ordtab, 'c' )
				insort = sortc (colors, ordtab, 'C' )
				insort = sortc (colors, ordtab, 'e' )	--	(h)ermine
				insort = sortc (colors, ordtab, 'g' )
				insort = sortc (colors, ordtab, 'm' )	--	multicolor
				insort = sortc (colors, ordtab, 'n' )
				insort = sortc (colors, ordtab, 'p' )
				insort = sortc (colors, ordtab, 's' )
				insort = sortc (colors, ordtab, 't' )
				insort = sortc (colors, ordtab, 'T' )
				insort = sortc (colors, ordtab, 'v' )
				insort = sortc (colors, ordtab, 'V' )	--	vair, fur
				insort = sortc (colors, ordtab, 'x' )
				table.insert(box, tinc_cat(ordtab))	--	std tincture cat
			else						--	tincunt	= zero:
				table.insert(box, tnotspec() )
			end							--	catimp false & tincunt > 0
		end								--	catnot false
	end									--	gpltab false

-- 2) box
	if gt == "1" or gt =="3" then
		if args.align == 'right' or args.align == 'center' then
			align = 'tincturebox-' .. align
		end
		local frame = mw.getCurrentFrame()
		text = frame:extensionTag('templatestyles', '', { src = 'Tincture/styles.css' }) ..
			'<div class="tincturebox ' .. align .. '">' .. table.concat(box) .. cm .. '</div>'
		if InFi then
			local name = mw.getContentLanguage():ucfirst(frame:expandTemplate{ title = 'I18n/COA', args = { top .. 'tincture' } })
			local link = ' <small>([[Template:Tincture/draw'..ss..'|'..ss..']])</small>'
			if ss ~= '' and ss ~= '0' then name = name .. link end 
			table.insert(out, frame:expandTemplate{ title = 'InFi', args = { name, text } })
		else
			table.insert(out, text )
		end
	end
	
-- 3) tab
	if gt == "2" or gt =="3" then
		table.insert(out, frame:expandTemplate{ title = '=', args = { "&#35;<br>" } })
		for i, v in ipairs(colors) do
			table.insert(out, draw( v, ss, tc, pf, 'tab') ) 
		end
		table.insert(out, frame:expandTemplate{ title = '=', args = { "<br><br>" } })
	end
	return table.concat(out)
end -- function main / tincture 


--  main function: create sorted tincture categories
function p.cstc (frame)
	local titl = mw.ustring.lower( mw.title.getCurrentTitle().text )
	local outs = ''
	
	outs = cats (titl, outs, 'argent' )
	outs = cats (titl, outs, 'argent-dark' )
	outs = cats (titl, outs, 'or' )
	outs = cats (titl, outs, 'azure' )
	outs = cats (titl, outs, 'brunâtre' )	-- +
	outs = cats (titl, outs, 'céleste' )
	outs = cats (titl, outs, 'carnation' )
	outs = cats (titl, outs, 'cendrée' )
	outs = cats (titl, outs, 'ermine' )
	outs = cats (titl, outs, 'gules' )
--	outs = cats (titl, outs, 'multiple' )
	outs = cats (titl, outs, 'murrey' )		-- +
	outs = cats (titl, outs, 'naranja' )
	outs = cats (titl, outs, 'orange_t' )	-- + +
	outs = cats (titl, outs, 'purpure' )
	outs = cats (titl, outs, 'sable' )
	outs = cats (titl, outs, 'sanguine' )	-- +
--	outs = cats (titl, outs, 'tawny' )		-- +?
	outs = cats (titl, outs, 'tenné' )
	outs = cats (titl, outs, 'vert' )
	outs = cats (titl, outs, 'vair' )
	outs = cats (titl, outs, 'invisible' )
	
	outs = mw.ustring.upper( mw.ustring.sub( outs, 3, 3 ) ) .. mw.ustring.sub( outs, 4 ) .. ' in heraldry'
	if mw.ustring.gsub( outs, ', ', '? ' ) == mw.ustring.gsub( outs, ', ', '? ', 1 ) then
		outs = mw.ustring.gsub( outs, ', ', ' and ' );	-- "and" when only two colors
	end
	return outs	
end -- cstc

return p;