Модуль:Color

![]() | Цей модуль позначений як К:реліз, готовий до загального вжитку (60). Він досягнув стадії готовності й вважається, що вільний від помилок і може використовуватись всюди, де знадобиться. Його можна вживати на допоміжних сторінках та інших сторінках Вікіпедії як можливість для навчання новачків. Аби зменшити навантаження на сервери та некоректний показ сторінок, його можна вдосконалювати в рамцях чернеткового тестування[en], а не з застосуванням спроб і помилок. |
Цей модуль призначений для корисних функцій на обробку кольору. Модуль має дві реалізації: одна, що взята з французького модуля, а інша — з англійського модуля. Англійська реалізаця переважно використовується шаблоном {{Картка кольору}}, прибираючи потребу у використанні зовнішніх конверторів кольору та запобігаючи невідповідності між кольоровими координатами.
Використання
[ред. код]Англійська реалізація
[ред. код]Щоб використати цей модуль, то ви можете використати один з шаблонів, перерахованих вище, або викликати модуль напряму. Всі функції, що приймають три пари шістнадцяткових цифр, також оброблюють скорочений формат з трьох цифр.
Щоб перетворити три пари шістнадцяткових цифр до трьох пар цифр RGB, що розділені комою:
{{#invoke:Color|hexToRgbTriplet|color}}
Щоб перетворити три пари шістнадцяткових цифр до колірної моделі CMYK без кольорового профілю (що є дуже поганою ідеєю!):
{{#invoke:Color|hexToCmyk|color|precision=?|pctsign=?}}
Щоб перетворити три пари шістнадцяткових цифр до HSL або HSV:
{{#invoke:Color|hexToHsl|color|precision=?}}
{{#invoke:Color|hexToHsv|color|precision=?}}
Щоб перетворити три пари шістнадцяткових цифр до перцептивного колірного простору CIELChuv[en]:
{{#invoke:Color|hexToCielch|color|precision=?}}
Щоб поєднати два кольори в більш фізично правильному лінійному просторі RGB:
{{#invoke:Color|hexMix|color1|color2|proportion|min=?|max=?}}
Щоб перетворити три пари цифр RGB до коду hex:
{{#invoke:Color|rgbTripletToHex|r|g|b}}
Наступні параметри є необов'язковими:
precision
: по стандарту —0
(нуль)pctsign
: встановіть0
(нуль), щоб приховати символ відсотка в створеному виводуproportion
: пропорціяcolor2
, по стандарту — 50min
: мінімальне значення діапазону пропорція, по стандарту — 0max
: максимальне значення діапазону пропорція, по стандарту — 100
Французька реалізація
[ред. код]![]() | Цей модуль залежить від наступних модулів: |
Функція p.shade забезпечує колірний шістнадцятковий код, коли відомо колір. У разі не відомої назви кольору, функція вертає до того самого. Наприклад, якщо ми використовуємо шістнадцятковий колірний код — буде без змін.
Приклади
[ред. код]{{#invoke:Color|shade|ocher}} дає нам: ocher
{{#invoke:Color|shade|a27ed3}} дає нам: a27ed3
Документація вище включена з Модуль:Color/документація. (ред. | історія) Дописувачі можуть експериментувати на підсторінках пісочниця (створити | дзеркало) та тести (створити) цього модуля. Будь ласка, додавайте категорії до підсторінки /документація. Підсторінки цієї сторінки. |
local p = {}
-- English realization of module
-- Introduction: https://colorspace.r-forge.r-project.org/articles/color_spaces.html
local function isempty(v)
return v == nil or v == ''
end
local function hexToRgb(color)
local cleanColor = color:gsub("#", "#"):match('^[%s#]*(.-)[%s;]*$')
if (#cleanColor == 6) then
return {
r = tonumber(string.sub(cleanColor, 1, 2), 16),
g = tonumber(string.sub(cleanColor, 3, 4), 16),
b = tonumber(string.sub(cleanColor, 5, 6), 16)
}
elseif (#cleanColor == 3) then
return {
r = 17 * tonumber(string.sub(cleanColor, 1, 1), 16),
g = 17 * tonumber(string.sub(cleanColor, 2, 2), 16),
b = 17 * tonumber(string.sub(cleanColor, 3, 3), 16)
}
end
error("Недійсне значення шістнадцяткового кольору " .. cleanColor, 1)
end
local function round(v)
if (v < 0) then
return math.ceil(v - 0.5)
else
return math.floor(v + 0.5)
end
end
local function rgbToHex(r, g, b)
return string.format("%02X%02X%02X", round(r), round(g), round(b))
end
local function rgbToCmyk(r, g, b)
if (r > 255 or g > 255 or b > 255 or r < 0 or g < 0 or b < 0) then
error("Рівень кольру поза доступними межами")
end
local c = 1 - r / 255
local m = 1 - g / 255
local y = 1 - b / 255
local k = math.min(c, m, y)
if (k == 1) then
c = 0
m = 0
y = 0
else
local d = 1 - k
c = (c - k) / d
m = (m - k) / d
y = (y - k) / d
end
return { c = c * 100, m = m * 100, y = y * 100, k = k * 100 }
end
local function rgbToHsl(r, g, b)
if (r > 255 or g > 255 or b > 255 or r < 0 or g < 0 or b < 0) then
error("Рівень кольру поза доступними межами")
end
local channelMax = math.max(r, g, b)
local channelMin = math.min(r, g, b)
local range = channelMax - channelMin
local h, s
if (range == 0) then
h = 0
elseif (channelMax == r) then
h = 60 * ((g - b) / range)
if (h < 0) then
h = 360 + h
end
elseif (channelMax == g) then
h = 60 * (2 + (b - r) / range)
else
h = 60 * (4 + (r - g) / range)
end
local L = channelMax + channelMin
if (L == 0 or L == 510) then
s = 0
else
s = 100 * range / math.min(L, 510 - L)
end
return { h = h, s = s, l = L * 50 / 255 }
end
local function rgbToHsv(r, g, b)
if (r > 255 or g > 255 or b > 255 or r < 0 or g < 0 or b < 0) then
error("Рівень кольру поза доступними межами")
end
local channelMax = math.max(r, g, b)
local channelMin = math.min(r, g, b)
local range = channelMax - channelMin
local h, s
if (range == 0) then
h = 0
elseif (channelMax == r) then
h = 60 * ((g - b) / range)
if (h < 0) then
h = 360 + h
end
elseif (channelMax == g) then
h = 60 * (2 + (b - r) / range)
else
h = 60 * (4 + (r - g) / range)
end
if (channelMax == 0) then
s = 0
else
s = 100 * range / channelMax
end
return { h = h, s = s, v = channelMax * 100 / 255 }
end
-- c in [0, 255], condition tweaked for no discontinuity
-- http://entropymine.com/imageworsener/srgbformula/
local function toLinear(c)
if (c > 10.314300250662591) then
return math.pow((c + 14.025) / 269.025, 2.4)
else
return c / 3294.6
end
end
local function toNonLinear(c)
if (c > 0.00313066844250063) then
return 269.025 * math.pow(c, 1.0/2.4) - 14.025
else
return 3294.6 * c
end
end
local function srgbToCielchuvD65o2deg(r, g, b)
if (r > 255 or g > 255 or b > 255 or r < 0 or g < 0 or b < 0) then
error("Рівень кольру поза доступними межами")
end
local R = toLinear(r)
local G = toLinear(g)
local B = toLinear(b)
-- https://github.com/w3c/csswg-drafts/issues/5922
local X = 0.1804807884018343 * B + 0.357584339383878 * G + 0.41239079926595934 * R
local Y = 0.07219231536073371 * B + 0.21263900587151027 * R + 0.715168678767756 * G
local Z = 0.01933081871559182 * R + 0.11919477979462598 * G + 0.9505321522496607 * B
local L, C, h
if (Y > 0.00885645167903563082) then
L = 116 * math.pow(Y, 1/3) - 16
else
L = Y * 903.2962962962962962963
end
if ((r == g and g == b) or L == 0) then
C = 0
h = 0
else
d = X + 3 * Z + 15 * Y
if (d == 0) then
C = 0
h = 0
else
-- 0.19783... and 0.4631... computed with extra precision from (X,Y,Z) when (R,G,B) = (1,1,1),
-- in which case (u,v) ≈ (0,0)
local us = 4 * X / d - 0.19783000664283678994
local vs = 9 * Y / d - 0.46831999493879099801
h = math.atan2(vs, us) * 57.2957795130823208768
if (h < 0) then
h = h + 360
elseif (h == 0) then
h = 0 -- ensure zero is positive
end
C = math.sqrt(us * us + vs * vs) * 13 * L
if (C == 0) then
C = 0
h = 0
end
end
end
return { L = L, C = C, h = h }
end
local function srgbMix(t, r0, g0, b0, r1, g1, b1)
if (t > 1 or t < 0) then
error("Інтерполяціний параметр поза доступними межами")
end
if (r0 > 255 or g0 > 255 or b0 > 255 or r1 > 255 or g1 > 255 or b1 > 255 or r0 < 0 or g0 < 0 or b0 < 0 or r1 < 0 or g1 < 0 or b1 < 0) then
error("Рівень кольру поза доступними межами")
end
local tc = 1 - t
return {
r = toNonLinear(tc * toLinear(r0) + t * toLinear(r1)),
g = toNonLinear(tc * toLinear(g0) + t * toLinear(g1)),
b = toNonLinear(tc * toLinear(b0) + t * toLinear(b1))
}
end
local function formatToPrecision(value, p)
return string.format("%." .. p .. "f", value)
end
local function getFractionalZeros(p)
if (p > 0) then
return "." .. string.rep("0", p)
else
return ""
end
end
function p.hexToRgbTriplet(frame)
local args = frame.args or frame:getParent().args
local hex = args[1]
if (hex) then
local rgb = hexToRgb(hex)
return rgb.r .. ', ' .. rgb.g .. ', ' .. rgb.b
else
return ""
end
end
function p.rgbTripletToHex(frame)
local args = frame.args or frame:getParent().args
local r = tonumber(args[1])
local g = tonumber(args[2])
local b = tonumber(args [3])
if (isempty(r) or isempty(g) or isempty(b)) then
return ""
else
return rgbToHex(r,g,b)
end
end
function p.hexToCmyk(frame)
local args = frame.args or frame:getParent().args
local hex = args[1]
if (hex) then
local p = tonumber(args.precision) or 0
local s = args.pctsign or "1"
local rgb = hexToRgb(hex)
local cmyk = rgbToCmyk(rgb.r, rgb.g, rgb.b)
local fk = formatToPrecision(cmyk.k, p)
local fc, fm, fy
local fracZeros = getFractionalZeros(p)
if (fk == 100 .. fracZeros) then
local fZero = 0 .. fracZeros
fc = fZero
fm = fZero
fy = fZero
else
fc = formatToPrecision(cmyk.c, p)
fm = formatToPrecision(cmyk.m, p)
fy = formatToPrecision(cmyk.y, p)
end
if (s ~= "0") then
return fc .. "%, " .. fm .. "%, " .. fy .. "%, " .. fk .. "%"
else
return fc .. ", " .. fm .. ", " .. fy .. ", " .. fk
end
else
return ""
end
end
function p.hexToHsl(frame)
local args = frame.args or frame:getParent().args
local hex = args[1]
if (hex) then
local p = tonumber(args.precision) or 0
local rgb = hexToRgb(hex)
local hsl = rgbToHsl(rgb.r, rgb.g, rgb.b)
local fl = formatToPrecision(hsl.l, p)
local fs, fh
local fracZeros = getFractionalZeros(p)
local fZero = 0 .. fracZeros
if (fl == fZero or fl == 100 .. fracZeros) then
fs = fZero
fh = fZero
else
fs = formatToPrecision(hsl.s, p)
if (fs == fZero) then
fh = fZero
else
fh = formatToPrecision(hsl.h, p)
if (fh == 360 .. fracZeros) then
fh = fZero -- handle rounding to 360
end
end
end
return fh .. "°, " .. fs .. "%, " .. fl .. "%"
else
return ""
end
end
function p.hexToHsv(frame)
local args = frame.args or frame:getParent().args
local hex = args[1]
if (hex) then
local p = tonumber(args.precision) or 0
local rgb = hexToRgb(hex)
local hsv = rgbToHsv(rgb.r, rgb.g, rgb.b)
local fv = formatToPrecision(hsv.v, p)
local fs, fh
local fracZeros = getFractionalZeros(p)
local fZero = 0 .. fracZeros
if (fv == fZero) then
fh = fZero
fs = fZero
else
fs = formatToPrecision(hsv.s, p)
if (fs == fZero) then
fh = fZero
else
fh = formatToPrecision(hsv.h, p)
if (fh == 360 .. fracZeros) then
fh = fZero -- handle rounding to 360
end
end
end
return fh .. "°, " .. fs .. "%, " .. fv .. "%"
else
return ""
end
end
function p.hexToCielch(frame)
local args = frame.args or frame:getParent().args
local hex = args[1]
if (hex) then
local p = tonumber(args.precision) or 0
local rgb = hexToRgb(hex)
local LCh = srgbToCielchuvD65o2deg(rgb.r, rgb.g, rgb.b)
local fL = formatToPrecision(LCh.L, p)
local fC, fh
local fracZeros = getFractionalZeros(p)
local fZero = 0 .. fracZeros
if (fL == fZero or fL == 100 .. fracZeros) then
fC = fZero
fh = fZero
else
fC = formatToPrecision(LCh.C, p)
if (fC == fZero) then
fh = fZero
else
fh = formatToPrecision(LCh.h, p)
if (fh == 360 .. fracZeros) then
fh = fZero -- handle rounding to 360
end
end
end
return fL .. ", " .. fC .. ", " .. fh .. "°"
else
return ""
end
end
function p.hexMix(frame)
local args = frame.args or frame:getParent().args
local hex0 = args[1]
local hex1 = args[2]
if (isempty(hex0) or isempty(hex1)) then
return ""
end
local t = args[3]
if (isempty(t)) then
t = 0.5
else
t = tonumber(t)
local min = tonumber(args.min) or 0
local max = tonumber(args.max) or 100
if (min >= max) then
error("Мінімальна пропроція більша ніж або дорівнює максимальній пропорції")
elseif (t < min) then
t = 0
elseif (t > max) then
t = 1
else
t = (t - min) / (max - min)
end
end
local rgb0 = hexToRgb(hex0)
local rgb1 = hexToRgb(hex1)
local rgb = srgbMix(t, rgb0.r, rgb0.g, rgb0.b, rgb1.r, rgb1.g, rgb1.b)
return rgbToHex(rgb.r, rgb.g, rgb.b)
end
-- French realization of module
function p.shade(frame)
local colorChart = mw.loadData('Модуль:Color/Data')
local param =frame.args[1]
local code = colorChart[string.lower(param)]
if code == nil then
return param
else
return code
end
end
-- fonction destiné à affiché l'ensemble des couleurs de 'Module:Couleur/Data'
-- destiné à la documentation de ce sous-module, pour aider à choisir une couleur.
function p.colorChart( frame )
local list = mw.loadData('Модуль:Color/Data')
local sortList = {}
for name, _ in pairs( list ) do
table.insert( sortList, name )
end
table.sort( sortList )
local colorNode = function( name, color )
local node = mw.html.create( 'li' )
node:cssText( 'display:inline-block; margin-left:.2em; width:7em; height:5em; vertical-align:top;' )
:tag( 'div' )
:cssText( 'border:1px solid grey; margin:.2em; padding:.2em;' )
:css( 'background-color', '#' .. color )
:wikitext( '\194\160' )
:done()
:wikitext( name )
:done()
return node
end
local root = mw.html.create( 'div' )
root:addClass( 'mw-collapsible' )
:cssText( 'margin:2em; border:1px solid grey; background-color:white; padding:0.2em 1em;' )
:tag( 'h2' )
:cssText( 'border:0; margin:.5em;' )
:wikitext( 'Атлас кольорів' )
:done()
local ul = root:tag( 'ul' )
ul :addClass( 'mw-collapsible-content' )
:cssText( 'margin:0; text-align:center; font-size:90%; line-height:1.25em;' )
for i, name in ipairs( sortList ) do
ul :node( colorNode( name, list[ name ] ) )
end
return tostring( root )
end
return p