Thanks @Olga_Galvanova, much obliged for all the research.
I looked at the HSLuv website you linked. A Lua implementation has been written by Alexei Boronine. I tried generating a palette with it.
-- https://github.com/hsluv/hsluv-lua/blob/master/hsluv.lua
dofile("./hsluv.lua")
local dlg = Dialog { title = "HSLuv" }
dlg:shades{
id = "preview",
label = "Preview:",
mode = "pick",
onclick = function(ev)
if ev.button == MouseButton.LEFT then
app.fgColor = ev.color
elseif ev.button == MouseButton.RIGHT then
app.bgColor = ev.color
end
end,
colors = {
Color(0xff6400ea),
Color(0xff005cbc),
Color(0xff007295),
Color(0xff007c77),
Color(0xff00873f),
Color(0xff5d8800),
Color(0xff7c8600),
Color(0xff938300),
Color(0xffb77e00),
Color(0xffff5979),
Color(0xffe200cd),
Color(0xffaa00de) }
}
dlg:slider {
id = "hueCount",
label = "Hue Count:",
min = 1,
max = 32,
value = 8
}
dlg:slider {
id = "lightCount",
label = "Light Count:",
min = 1,
max = 32,
value = 4
}
dlg:slider {
id = "hueStart",
label = "Hue Start:",
min = 0,
max = 360,
value = 0
}
dlg:slider {
id = "hueEnd",
label = "Hue End:",
min = 0,
max = 360,
value = 360
}
dlg:slider {
id = "sat",
label = "Saturation:",
min = 0,
max = 100,
value = 100
}
dlg:slider {
id = "lightStart",
label = "Light Start:",
min = 0,
max = 100,
value = 7
}
dlg:slider {
id = "lightEnd",
label = "Light End:",
min = 0,
max = 100,
value = 88
}
dlg:button {
id = "ok",
text = "OK",
focus = true,
onclick = function()
local args = dlg.data
if args.ok then
local hueCount = args.hueCount
local lightCount = args.lightCount
local hueStart = args.hueStart
local hueEnd = args.hueEnd
local saturation = args.sat
local lightStart = args.lightStart
local lightEnd = args.lightEnd
local palIdx = 0
local totLen = hueCount * lightCount
local palette = Palette(totLen)
local iToFac = 1.0
local jToFac = 1.0
if hueCount > 1 then iToFac = 1.0 / (hueCount - 1.0) end
if lightCount > 1 then jToFac = 1.0 / (lightCount - 1.0) end
for i = 1, hueCount, 1 do
local iFac = (i - 1) * iToFac
-- This is a simplification, there are four options
-- to lerp hue: CW, CCW, near and far.
local hue = (1.0 - iFac) * hueStart
+ iFac * hueEnd
hue = hue % 360.0
for j = 1, lightCount, 1 do
local jFac = (j - 1) * jToFac
local lightness = (1.0 - jFac) * lightStart
+ jFac * lightEnd
local rgbtuple = hsluv.hsluv_to_rgb({
hue, saturation, lightness
})
local r = rgbtuple[1]
local g = rgbtuple[2]
local b = rgbtuple[3]
r = 255.0 * r + 0.5
g = 255.0 * g + 0.5
b = 255.0 * b + 0.5
r = math.tointeger(r)
g = math.tointeger(g)
b = math.tointeger(b)
local aseClr = Color(r, g, b, 255)
palette:setColor(palIdx, aseClr)
palIdx = palIdx + 1
end
end
app.activeSprite:setPalette(palette)
app.refresh()
end
end
}
dlg:button {
id = "cancel",
text = "CANCEL",
onclick = function()
dlg:close()
end
}
dlg:show { wait = false }
The HSL inputs are dialog friendly, as the ranges [0, 360], [0, 100] and [0, 100] are accepted. The outputs are in [0.0, 1.0] and need to be converted to [0, 255]. Hex conversion methods return web-formatted strings like #aabbcc, so they’d need to be converted to integers in ABGR, 0xffccbbaa.
In the trial run, I assumed that hue was the major axis, lightness the minor axis and saturation a fixed constant set by the user. Hue and lightness could be rotated / transposed, depending on how a user wanted to cycle through with the [
and ]
keys. Or saturation could be swapped in as one of the axes.
And here’s what a palette looks like when you crank the saturation down to zero. It’s the opposite, I think, of where this discussion started, but also useful.