Module:IPA vowel chart

local export = {}

local CURVATURE = 0.51

local function make_error(message, index) local specifier_index = math.floor((index - 1) / 4) + 1 error(message .. " (argument #" .. index .. ", specifier #" .. specifier_index .. ")") end

local function parse_fraction(arg, quality, index) local num, denom = mw.ustring.match(arg, "^([0-9]+)/([0-9]+)$") return num / denom end

local function parse_scalar(arg, quality, index) arg = mw.text.trim(arg) local result if mw.ustring.match(arg, "^[0-9]+/[0-9]+$") then result = parse_fraction(arg, quality, index) else result = tonumber(arg) end if result == nil or result 1 then return make_error(quality .. " must be a value within [0, 1]", index) end return result end

local function parse_boolean(arg, quality, index) arg = mw.text.trim(arg) if arg == "0" then return 0 elseif arg == "1" then return 1 else return make_error(quality .. " must be a boolean", index) end end

local function parse_vowel_specifier(args, index) local vowel = args[index] local openness = parse_scalar(args[index + 1], "openness", index + 1) local backness = parse_scalar(args[index + 2], "backness", index + 2) local roundness = parse_boolean(args[index + 3], "roundness", index + 3) return { vowel = vowel, openness = openness, backness = backness, roundness = roundness, } end

local function curve(backness, openness, curvature) return backness * (CURVATURE + (1 - CURVATURE) * (1 - openness)) + openness * (1 - CURVATURE) end

local function display_chart(vowels, args) local content = "" table.sort(vowels, function (a, b) return a.roundness < b.roundness end)

local vowels_merged = {} local vowels_table = {} for _, vowel in ipairs(vowels) do local key = vowel.openness .. ":" .. vowel.backness if vowels_table[key] then table.insert(vowels_table[key].variants, vowel.vowel) else local new_table = { backness = vowel.backness, openness = vowel.openness, variants = {vowel.vowel} }			vowels_table[key] = new_table table.insert(vowels_merged, new_table) end end

vowels_table = nil for _, vowel in ipairs(vowels_merged) do		local y = vowel.openness local x = curve(vowel.backness, y, CURVATURE) local parent = mw.html.create('div') :css('position', 'absolute') :css('top', (y * 100) .. "%") :css('left', (x * 100) .. "%") :css('font-size', '120%;') :tag('div') :css('position', 'relative') :css('left', '-2em') :css('top', '-1.25em') :css('width', '3em') :css('height', '2em') :css('padding', '0.5em') :css('text-align', 'center') for index, phoneme in ipairs(vowel.variants) do			if index > 1 then parent:wikitext(' &bull; ') end parent = parent :tag('span') :attr('class', 'IPA') :wikitext(phoneme) :done end content = content .. tostring(parent:allDone) end local style = args.style or "" local title_row if args.title then title_row = "\n|-\n! colspan=4 | " .. args.title .. "\n|-\n" else title_row = "\n" end return end

function export.show(frame) local args = frame:getParent.args local index = 1 local vowels = {} while args[index] do		if not args[index + 3] then error("Incomplete vowel specifier") end table.insert(vowels, parse_vowel_specifier(args, index)) index = index + 4 end return display_chart(vowels, { style = args["style"], title = args["title"] }) end

return export