Module:Tincture
CodeDiscussionEditHistoryLinksLink count Subpages:DocumentationTestsResultsSandboxLive code All modules
GPL palettes
editIn 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:
- gpltab=1 default value, is automatically set to tincturize files
- gpltab=2 value to generate tables
- 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 actionz-cat when a tincture field value is specified without any tincturesno actions-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) == '#' then
hexi = mw.ustring.sub (hexv, 6)
hext = '3'
elseif mw.ustring.sub ( hexv, 1, 6) == '#' then
hexi = mw.ustring.sub (hexv, 7)
hext = '4'
elseif mw.ustring.sub ( hexv, 1, 6) == '#' then
hexi = mw.ustring.sub (hexv, 7, #hexv)
hext = '5'
elseif mw.ustring.sub ( hexv, 1, 7) == '#' 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 = ' '
for i = 1, 3 do
if dtab[i] < 100 then
if dtab[i] < 10 then
dtxt = dtxt .. ' '
end
dtxt = dtxt .. ' '
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 .. ' '
-- end
-- contrast = contrast .. ' '
-- 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 .. ' '
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 '#' .. 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 = { "#<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;