Moduuli:Kitarakirja/Otekaavio
Ulkoasu
Tämän moduulin ohjeistuksen voi tehdä sivulle Moduuli:Kitarakirja/Otekaavio/ohje
--- Moduuli kitaran otekaavioiden piirtämiseen. Generoi timeline-lisäosan avulla kuvan.
local p = {}
local tekstipohja = require "Moduuli:Tekstipohja"
local timeline_template = [=[
Define $width = {{{Width}}}
Define $height = 176
Define $start = 0
Define $end = {{{TableHeight}}}
Define $first = 21
Define $last = {{{LastString}}}
ImageSize = width:$width height:$height
PlotArea = right:{{{RightMargin}}} left:5 bottom:0 top:5
Period = from:$start till:$end
TimeAxis = orientation:vertical
AlignBars = justify
Colors =
id:bg value:white
id:string value:black
id:fret value:gray(0.7)
id:text value:black
id:fing1 value:{{{Fing1Color}}}
id:fing2 value:{{{Fing2Color}}}
id:fing3 value:{{{Fing3Color}}}
id:fing4 value:{{{Fing4Color}}}
id:fingp value:{{{FingPColor}}}
{{{Colors
}}}
BackgroundColors = canvas:bg
PlotData=
{{{Strings
}}}
bar:o color:bg from:start till:7 width:12
{{{FretPos}}}
LineData =
layer:back color:fret width:1 frompos:$first tillpos:$last
{{{Frets
}}}
{{{Nut}}}
layer:front
{{{Barres
}}}
# {{{DUMMY}}}
]=]
--- Kieltä esittävien viivojen kohdat. Käytetään barrejen piirtämiseen.
--local stringpos = { 26, 54, 82, 109, 137, 165 }
local stringpos = { 20, 40, 60, 80, 100, 120 }
--- Timeline-koodin riviä kuvaava olio. Rivin tiedot asetetaan taulukkoon, esim. ["at"] = 0.
-- tostring palauttaa rivin tekstinä.
local function new_timeline_object(tab)
tab = tab or {}
setmetatable(tab, {
__tostring = function (tab)
local out = {}
for k,v in pairs(tab) do
if k ~= "text" then
table.insert(out, k .. ":" .. v)
end
end
-- text-parametri jätetään viimeiseksi, että tyhjät merkkijonot toimii
if tab.text then
table.insert(out, "text:" .. tab.text)
end
return table.concat(out, " ")
end
})
return tab
end
--- Alustaa annetunkokoisen taulukon annetulla arvolla.
--
-- @param size: taulukon koko
-- @param init: alkioihin alustettava arvo
local function make_array(size, init)
local arr = {}
for i = 1, size do
arr[i] = init
end
return arr
end
--------------------------------------------
-- Timeline-merkkausta tuottavat funktot. --
--------------------------------------------
--- Sormitusnumeron merkkaus
--
-- @param number: sormen numero, 1–4 ja "p"
local function get_fingering_markup(number)
if not number then
return ""
end
local markup = new_timeline_object{
["at"] = 0.5,
["text"] = number,
["fontsize"] = 10,
["shift"] = "(10,0)",
["textcolor"] = "text",
["align"] = "center"
}
return tostring(markup)
end
--- Avoimen kielen merkkaus.
--
-- @param text: joko "X", "O", "(X)" tai "(O)"
local function get_open_string_markup(text)
if not text then
return ""
end
local markup = new_timeline_object{
["at"] = 6.5,
["text"] = text,
["fontsize"] = 12,
["shift"] = "(10,8)",
["textcolor"] = "text",
["align"] = "center"
}
return tostring(markup)
end
local I = 3
local V = 9
local X = 9
local fret_marks = {
[1] = { text = "", width = 0 },
[2] = { text = "II", width = 0+I+I },
[3] = { text = "III", width = 0+I+I+I },
[4] = { text = "IV", width = 0+I+V },
[5] = { text = "V", width = 0+V },
[6] = { text = "VI", width = 0+V+I },
[7] = { text = "VII", width = 0+V+I+I },
[8] = { text = "VIII", width = 0+V+I+I+I },
[9] = { text = "IX", width = 0+I+X },
[10] = { text = "X", width = 0+X },
[11] = { text = "XI", width = 0+X+I },
[12] = { text = "XII", width = 0+X+I+I },
[13] = { text = "XIII", width = 0+X+I+I+I },
[14] = { text = "XIV", width = 0+X+I+V },
[15] = { text = "XV", width = 0+X+V },
[16] = { text = "XVI", width = 0+X+V+I },
[17] = { text = "XVII", width = 0+X+V+I+I },
[18] = { text = "XVIII", width = 0+X+V+I+I+I },
[19] = { text = "XIX", width = 0+X+I+X },
[20] = { text = "XX", width = 0+X+X },
[21] = { text = "XXI", width = 0+X+X+I },
[22] = { text = "XXII", width = 0+X+X+I+I },
[23] = { text = "XXIII", width = 0+X+X+I+I+I },
[24] = { text = "XXIV", width = 0+X+X+I+V },
}
--- Nauhavälin numeron tulostus
local function get_fret_pos_markup(fret)
if not fret or fret == 1 then
return ""
end
assert(fret >= 1 and fret < 25, "Nauhavälin pitää olla väliltä 1–24")
local markup = new_timeline_object{
["at"] = 5.5,
["text"] = fret_marks[fret].text,
["fontsize"] = "M",
["shift"] = "(23,8)",
["textcolor"] = "string",
}
return tostring(markup)
end
--- Palauttaa nauhanumerosta johtuvan leveyslisän.
local function get_right_margin(fret)
assert(fret >= 1 and fret < 25, "Nauhavälin pitää olla väliltä 1–24")
return fret_marks[fret].width
end
--- Painetun kielen merkkaus.
--
-- @param pos: väli, johon merkintä tulee (suhteellinen; 1 = ensimmäinen väli)
-- @param finger_color: sormen väri, jota käytetään (esim. "fing1" tai "fingp")
-- @param string_color: (valinnainen) kielen väri (esim. "str1"), jos annettu tekee sormiympyrän
-- sisälle pienen tämän värisen ympyrän
local function get_pressed_string_markup(conf, pos, finger, string_color)
if pos < 1 then
return ""
end
local fing_color
if finger == "1" or finger == "2" or finger == "3" or finger == "4" or finger == "P" then
fing_color = "fing" .. finger
else
fing_color = "fing1"
end
local mpos = tostring(7 - pos)
local markup = new_timeline_object{
["at"] = mpos - 0.5,
["text"] = "•", -- BULLET
["textcolor"] = fing_color,
["fontsize"] = 44,
--["shift"] = "(0,-12)" -- 190
--["shift"] = "(0,-8)" -- 120
["shift"] = "(0,-9)" -- 140
}
--assert ( finger and finger ~= "", "Sormi puuttuu" )
if not string_color then
return tostring(markup)
end
-- Tehdään merkki kahdesta päällekkäisestä bulletista, joista päällimmäinen on tyhjä keskeltä.
local markup2 = new_timeline_object{
["at"] = mpos - 0.5,
["text"] = "◦", -- WHITE BULLET
["textcolor"] = fing_color,
["fontsize"] = markup.fontsize,
["shift"] = "(0,-8)"
}
markup["textcolor"] = string_color
return tostring(markup) .. "\n" .. tostring(markup2)
end
--- Barre-otetta kuvaava viiva.
-- @param pos: väli, johon viiva piirretään
-- @param finger: sormen tunnus (1–4, p)
-- @param string_b: ensimmäinen kieli
-- @param string_e: jälkimmäinen kieli
local function get_barre_bar_markup(symbol, string, fret, finger)
local mpos = tostring(7 - fret) .. ".5"
local markup = new_timeline_object{
["at"] = mpos - 0.5,
["width"] = 10
}
if finger == "1" or finger == "2" or finger == "3" or finger == "4" or finger == "P" then
markup.color = "fing" .. finger
else
markup.color = "fing1"
end
if symbol == "C" then
markup.frompos = stringpos[string]
markup.tillpos = stringpos[string] + 11
elseif symbol == "D" then
markup.frompos = stringpos[string] - 11
markup.tillpos = stringpos[string]
elseif symbol == "-" or symbol == "H" then
markup.frompos = stringpos[string] - 11
markup.tillpos = stringpos[string] + 11
else
error ( "Virheellinen tyyppi: " .. symbol )
end
--assert ( finger and finger ~= "", "Sormi puuttuu" )
return tostring(markup)
end
--- Barre-otteita kuvaavat viivat.
-- @param barres: väli, johon viiva piirretään
local function get_barres_markup(barres)
local out = {}
for i, barre in ipairs(barres) do
table.insert(out, get_barre_bar_markup(barre.type, barre.string, barre.fret, barre.finger))
end
return table.concat(out, "\n")
end
--- Kielten välin merkkaus
--
-- @param index: seuraavan kielen numero
local function get_string_space_markup(index)
local markup = new_timeline_object{
["bar"] = "space" .. index,
["width"] = 5,
}
return tostring(markup)
end
--- Yksittäisen kielen merkkaus
--
-- @param index: kielen numero
local function get_string_markup(index, open_pos, pressed_pos, finger, string_color)
local markup = new_timeline_object{
["bar"] = "string" .. index,
["from"] = 1.5,
["till"] = 6.5,
["color"] = "black",
["width"] = 1,
}
return tostring(markup)
end
--- Kielten merkkaus
--
-- Barret piirretään erikseen, koska ne tulevat eri paikkaan koodia.
--
-- @param n_strings: kielten määrä
-- @param open_poss: avointen kielten tekstit
-- @param pressed_poss: painettujen kielten nauhanumerot
-- @param fingering: kielten sormitukset
-- @param string_colors: mahdolliset kielikohtaisten väriläikkien värit
local function get_strings_markup(conf, open_poss, pressed_poss, fingering, string_colors)
local out = {}
for i = 1, conf.n_strings do
table.insert(out, get_string_space_markup(i))
table.insert(out, "")
table.insert(out, get_string_markup(i))
table.insert(out, get_open_string_markup(open_poss[i]))
table.insert(out, get_pressed_string_markup(conf, pressed_poss[i], fingering[i], string_colors[i] and ("str" .. i)))
table.insert(out, get_fingering_markup(fingering[i]))
end
return table.concat(out, "\n")
end
--- Satulan piirto
--
-- @param fret_pos: jos 1, piirretään satula, muuten ei
local function get_nut_markup(conf, fret_pos)
if fret_pos == 1 then
local markup = new_timeline_object{
["at"] = 6.6,
["frompos"] = 20,
["tillpos"] = conf.min_width - 20,
["color"] = "black",
["width"] = 4,
["layer"] = "back",
}
return tostring(markup)
end
return ""
end
--- Nauhojen piirto
local function get_fret_markup(conf)
return "at:" .. table.concat({ 6.5, 5.5, 4.5, 3.5, 2.5, 1.5 }, "\nat:")
end
--- Väriä kuvaavan rivin.
-- @param id: värin nimi
-- @param color: väri
local function get_color_markup(id, value)
local markup = new_timeline_object{
["id"] = id,
["value"] = value
}
return tostring(markup)
end
local function get_colors_markup(colors)
local out = {}
for i, value in pairs(colors) do
table.insert(out, get_color_markup("str" .. i, value))
end
return table.concat(out, "\n")
end
--- Tekee otekaavion annetuista parametreista.
--
-- Parametrit `string_colors`, `pos0`, `pos` ja `fingering` ovat taulukoita, joissa on jokaiselle kielelle alkio.
-- @param conf: konfiguraatio
-- @param fing_colors: (valinnainen) sormien värit, taulukko { [1]–[4], ["p"] }
-- @param string_colors: (valinnainen) kielikohtaiset lisävärit
-- @param fret_pos: ensimmäisen piirrettävän nauhan nauhaväli (merkitään marginaaliin jos > 1)
-- @param pos0: avoimia kieliä kuvaavat tekstit (esim. "X", "O")
-- @param pos: välit, joista kieliä painetaan (esim. 3, suhteellinen `fret_pos` -parametriin nähden)
-- @param fingering: sormitukset
-- @return: timeline-elementin teksti
function p.chord_diagram(args)
local conf = args.conf
local fing_colors = args.fing_colors or {}
local fret_pos = args.fret_pos
local open_poss = args.pos0s
local pressed_poss = args.poss
local fingering = args.fingering
local string_colors = args.string_colors or {}
local barres = args.barres
local dummy = args.dummy
local text = tekstipohja.korvaaMuuttujat(timeline_template, {
["TableHeight"] = tostring(conf.cells_vertically),
["Nut"] = get_nut_markup(conf, fret_pos),
["Frets"] = get_fret_markup(conf),
["FretPos"] = get_fret_pos_markup(fret_pos),
["Colors"] = get_colors_markup(string_colors),
["Barres"] = get_barres_markup(barres),
["Fing1Color"] = fing_colors[1] or "black",
["Fing2Color"] = fing_colors[2] or "black",
["Fing3Color"] = fing_colors[3] or "black",
["Fing4Color"] = fing_colors[4] or "black",
["FingPColor"] = fing_colors.p or "black",
["Strings"] = get_strings_markup(conf,
open_poss,
pressed_poss,
fingering,
string_colors),
["RightMargin"] = tostring(5 + get_right_margin(fret_pos)),
["Width"] = tostring(conf.min_width + get_right_margin(fret_pos)),
["LastString"] = tostring(conf.min_width - 20),
["DUMMY"] = string.rep("X", tonumber(dummy or "0"))
})
return text
end
----------------------------------------
-- Syöteparametreja lukevat funktiot. --
----------------------------------------
local function read_finger_colors(args)
local colors = {}
if args.sormi1 then
colors[1] = args.sormi1
end
if args.sormi2 then
colors[2] = args.sormi2
end
if args.sormi3 then
colors[3] = args.sormi3
end
if args.sormi4 then
colors[4] = args.sormi4
end
if args.sormiP then
colors.p = args.sormiP
end
return colors
end
local function read_string_colors(conf, args)
local colors = {}
for i = 1, conf.n_strings do
if args["kieli" .. i] then
colors[i] = args["kieli" .. i]
end
end
return colors
end
--- Lukee vapaat kielet argumenteista ja palauttaa taulukon niiden teksteistä.
local function read_open_strings(conf, args)
local pos0_text = make_array(conf.n_strings, "")
local s_idx = 0
local cur
for i = 1, conf.n_strings do
-- Poistetaan tyhjät alusta ja lopusta.
cur = args[i]:gsub("^%s*(.-)%s*$", "%1")
-- Kielen numero.
s_idx = ((i - 1) % conf.n_strings) + 1
if i >= 1 and i < 7 then
if cur == "o" then
pos0_text[s_idx] = "O"
elseif cur == "x" then
pos0_text[s_idx] = "X"
else
pos0_text[s_idx] = cur
end
end
end
return pos0_text
end
--- Lukee ei-vapaat kielet ja palauttaa niiden suurimmat arvot taulukkona.
local function read_fret_positions(conf, args, fingering)
local pos = make_array(conf.n_strings, 0)
local s_idx = 0
local f_idx = 0
local cur
local bars = {} -- Luettelo piirrettävistä barreista
local from = conf.n_strings + 1
local till = conf.n_strings * (1 + 5) -- avoimet kielet + 5 nauhaväliä
for i = from, till do
-- Poistetaan tyhjät alusta ja lopusta.
cur = args[i]:gsub("^%s*(.-)%s*$", "%1")
-- Kielen ja nauhan numero.
s_idx = ((i - 1) % conf.n_strings) + 1
if s_idx == 1 then
f_idx = f_idx + 1
end
if cur == "o" then
pos[s_idx] = f_idx
elseif cur == "C" then
pos[s_idx] = f_idx
table.insert(bars, { type = "C", string = s_idx, fret = f_idx, finger = fingering[s_idx] })
elseif cur == "D" or cur == "H" then
pos[s_idx] = f_idx
assert ( (#bars == 0 or bars[#bars].fret == f_idx) and s_idx > 1, "Barresta puuttuu alkumerkki" )
table.insert(bars, { type = cur, string = s_idx, fret = f_idx, finger = bars[#bars].finger })
elseif cur == "-" then
assert ( (#bars == 0 or bars[#bars].fret == f_idx) and s_idx > 1, "Barresta puuttuu alkumerkki" )
table.insert(bars, { type = "-", string = s_idx, fret = f_idx, finger = bars[#bars].finger })
end
end
return pos, bars
end
--- Lukee vapaat kielet argumenteista ja palauttaa taulukon niiden teksteistä.
local function read_fingering(conf, args)
local fingering = make_array(conf.n_strings, 0)
local s_idx = 0
local cur
local from = conf.n_strings * (1 + 5) + 1
local till = from + conf.n_strings - 1
for i = from, till do
-- Poistetaan tyhjät alusta ja lopusta.
cur = args[i]:gsub("^%s*(.-)%s*$", "%1")
-- Kielen numero.
s_idx = ((i - 1) % conf.n_strings) + 1
fingering[s_idx] = cur
end
return fingering
end
function p.Otekaavio(frame)
local n_strings = frame.args["kieliä"] or 6
local conf = {
n_strings = n_strings, -- kielten määrä
min_width = (n_strings + 1) * 20, -- kaavion leveys pikseleinä; tähän lisätään mahdollisen nauhanumeron leveys
cells_vertically = 8, -- kaavion korkeus soluina eli nauhaväleinä
}
local fingering = read_fingering(conf, frame.args)
local poss, bars = read_fret_positions(conf, frame.args, fingering)
local text = p.chord_diagram{
conf = conf,
fret_pos = (tonumber(frame.args.nauhanumero) or 1),
pos0s = read_open_strings(conf, frame.args),
poss = poss,
fingering = fingering,
fing_colors = read_finger_colors(frame.args),
string_colors = read_string_colors(conf, frame.args),
barres = bars,
dummy = frame.args.dummy
}
if frame.args.nowiki then
return frame:extensionTag{ name = "pre", content = text }
else
return frame:extensionTag{ name = "timeline", content = text }
end
end
return p