<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://stockhub.co/index.php?action=history&amp;feed=atom&amp;title=Module%3AColor%2Fsandbox</id>
	<title>Module:Color/sandbox - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://stockhub.co/index.php?action=history&amp;feed=atom&amp;title=Module%3AColor%2Fsandbox"/>
	<link rel="alternate" type="text/html" href="https://stockhub.co/index.php?title=Module:Color/sandbox&amp;action=history"/>
	<updated>2026-07-02T05:50:39Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://stockhub.co/index.php?title=Module:Color/sandbox&amp;diff=135714&amp;oldid=prev</id>
		<title>imported&gt;Ftrebien: Undid revision 1069389334 by Ftrebien (talk) Not yet</title>
		<link rel="alternate" type="text/html" href="https://stockhub.co/index.php?title=Module:Color/sandbox&amp;diff=135714&amp;oldid=prev"/>
		<updated>2022-02-02T02:39:52Z</updated>

		<summary type="html">&lt;p&gt;Undid revision 1069389334 by &lt;a href=&quot;/research/Special:Contributions/Ftrebien&quot; title=&quot;Special:Contributions/Ftrebien&quot;&gt;Ftrebien&lt;/a&gt; (&lt;a href=&quot;/index.php?title=User_talk:Ftrebien&amp;amp;action=edit&amp;amp;redlink=1&quot; class=&quot;new&quot; title=&quot;User talk:Ftrebien (page does not exist)&quot;&gt;talk&lt;/a&gt;) Not yet&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;-- Introduction: https://colorspace.r-forge.r-project.org/articles/color_spaces.html&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
local function isEmpty(value)&lt;br /&gt;
	return value == nil or value == &amp;#039;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function isNotEmpty(value)&lt;br /&gt;
	return value ~= nil and value ~= &amp;#039;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function argDefault(value, default)&lt;br /&gt;
	if (value == nil or value == &amp;#039;&amp;#039;) then&lt;br /&gt;
		return default&lt;br /&gt;
	else&lt;br /&gt;
		return value&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function numArgDefault(value, default)&lt;br /&gt;
	if (value == nil or value == &amp;#039;&amp;#039;) then&lt;br /&gt;
		return default&lt;br /&gt;
	else&lt;br /&gt;
		return tonumber(value)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function isArgTrue(value)&lt;br /&gt;
	return (value ~= nil and value ~= &amp;#039;&amp;#039; and value ~= &amp;#039;0&amp;#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function isEmpty(value)&lt;br /&gt;
	return value == nil or value == &amp;#039;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function isNotEmpty(value)&lt;br /&gt;
	return value ~= nil and value ~= &amp;#039;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function hexToRgb(hexColor)&lt;br /&gt;
	local cleanColor = hexColor:gsub(&amp;#039;&amp;amp;#35;&amp;#039;, &amp;#039;#&amp;#039;):match(&amp;#039;^[%s#]*(.-)[%s;]*$&amp;#039;)&lt;br /&gt;
	if (#cleanColor == 6) then&lt;br /&gt;
		return&lt;br /&gt;
			tonumber(string.sub(cleanColor, 1, 2), 16),&lt;br /&gt;
			tonumber(string.sub(cleanColor, 3, 4), 16),&lt;br /&gt;
			tonumber(string.sub(cleanColor, 5, 6), 16)&lt;br /&gt;
	elseif (#cleanColor == 3) then&lt;br /&gt;
		return&lt;br /&gt;
			17 * tonumber(string.sub(cleanColor, 1, 1), 16),&lt;br /&gt;
			17 * tonumber(string.sub(cleanColor, 2, 2), 16),&lt;br /&gt;
			17 * tonumber(string.sub(cleanColor, 3, 3), 16)&lt;br /&gt;
	end&lt;br /&gt;
	error(&amp;#039;Invalid hexadecimal color &amp;#039; .. cleanColor, 1)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function round(value)&lt;br /&gt;
	if (value &amp;lt; 0) then&lt;br /&gt;
		return math.ceil(value - 0.5)&lt;br /&gt;
	else&lt;br /&gt;
		return math.floor(value + 0.5)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function rgbToHex(r, g, b)&lt;br /&gt;
	return string.format(&amp;#039;%02X%02X%02X&amp;#039;, round(r), round(g), round(b))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function checkRgb(r, g, b)&lt;br /&gt;
	if (r &amp;gt; 255 or g &amp;gt; 255 or b &amp;gt; 255 or r &amp;lt; 0 or g &amp;lt; 0 or b &amp;lt; 0) then&lt;br /&gt;
		error(&amp;#039;Color level out of bounds&amp;#039;)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function rgbToCmyk(r, g, b)&lt;br /&gt;
	local c = 1 - r / 255&lt;br /&gt;
	local m = 1 - g / 255&lt;br /&gt;
	local y = 1 - b / 255&lt;br /&gt;
	local k = math.min(c, m, y)&lt;br /&gt;
	if (k == 1) then&lt;br /&gt;
		c = 0&lt;br /&gt;
		m = 0&lt;br /&gt;
		y = 0&lt;br /&gt;
	else&lt;br /&gt;
		local kc = 1 - k&lt;br /&gt;
		c = (c - k) / kc&lt;br /&gt;
		m = (m - k) / kc&lt;br /&gt;
		y = (y - k) / kc&lt;br /&gt;
	end&lt;br /&gt;
	return c * 100, m * 100, y * 100, k * 100&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function rgbToHsl(r, g, b)&lt;br /&gt;
	local channelMax = math.max(r, g, b)&lt;br /&gt;
	local channelMin = math.min(r, g, b)&lt;br /&gt;
	local range = channelMax - channelMin&lt;br /&gt;
	local h, s&lt;br /&gt;
	if (range == 0) then&lt;br /&gt;
		h = 0&lt;br /&gt;
	elseif (channelMax == r) then&lt;br /&gt;
		h = 60 * ((g - b) / range)&lt;br /&gt;
		if (h &amp;lt; 0) then&lt;br /&gt;
			h = 360 + h&lt;br /&gt;
		end&lt;br /&gt;
	elseif (channelMax == g) then&lt;br /&gt;
		h = 60 * (2 + (b - r) / range)&lt;br /&gt;
	else&lt;br /&gt;
		h = 60 * (4 + (r - g) / range)&lt;br /&gt;
	end&lt;br /&gt;
	local L = channelMax + channelMin&lt;br /&gt;
	if (L == 0 or L == 510) then&lt;br /&gt;
		s = 0&lt;br /&gt;
	else&lt;br /&gt;
		s = 100 * range / math.min(L, 510 - L)&lt;br /&gt;
	end&lt;br /&gt;
	return h, s, L * 50 / 255&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function rgbToHsv(r, g, b)&lt;br /&gt;
	local channelMax = math.max(r, g, b)&lt;br /&gt;
	local channelMin = math.min(r, g, b)&lt;br /&gt;
	local range = channelMax - channelMin&lt;br /&gt;
	local h, s&lt;br /&gt;
	if (range == 0) then&lt;br /&gt;
		h = 0&lt;br /&gt;
	elseif (channelMax == r) then&lt;br /&gt;
		h = 60 * ((g - b) / range)&lt;br /&gt;
		if (h &amp;lt; 0) then&lt;br /&gt;
			h = 360 + h&lt;br /&gt;
		end&lt;br /&gt;
	elseif (channelMax == g) then&lt;br /&gt;
		h = 60 * (2 + (b - r) / range)&lt;br /&gt;
	else&lt;br /&gt;
		h = 60 * (4 + (r - g) / range)&lt;br /&gt;
	end&lt;br /&gt;
	if (channelMax == 0) then&lt;br /&gt;
		s = 0&lt;br /&gt;
	else&lt;br /&gt;
		s = 100 * range / channelMax&lt;br /&gt;
	end&lt;br /&gt;
	return h, s, channelMax * 100 / 255&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function checkHsv(h, s, v)&lt;br /&gt;
	if (s &amp;gt; 100 or v &amp;gt; 100 or s &amp;lt; 0 or v &amp;lt; 0) then&lt;br /&gt;
		error(&amp;#039;Color level out of bounds&amp;#039;)&lt;br /&gt;
	end	&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function hsvToRgb(h, s, v)&lt;br /&gt;
	local hn = (h / 60 - 6 * math.floor(h / 360))&lt;br /&gt;
	local hi = math.floor(hn)&lt;br /&gt;
	local hr = hn - hi&lt;br /&gt;
	local sn = s / 100&lt;br /&gt;
	local vs = v * 255 / 100&lt;br /&gt;
    local p = vs * (1 - sn);&lt;br /&gt;
    local q = vs * (1 - sn * hr);&lt;br /&gt;
    local t = vs * (1 - sn * (1 - hr));&lt;br /&gt;
    if (hi &amp;lt; 3) then&lt;br /&gt;
		if (hi == 0) then&lt;br /&gt;
			return vs, t, p&lt;br /&gt;
		elseif (hi == 1) then&lt;br /&gt;
			return q, vs, p&lt;br /&gt;
		else&lt;br /&gt;
			return p, vs, t&lt;br /&gt;
		end&lt;br /&gt;
    else&lt;br /&gt;
		if (hi == 3) then&lt;br /&gt;
			return p, q, vs&lt;br /&gt;
		elseif (hi == 4) then&lt;br /&gt;
			return t, p, vs&lt;br /&gt;
		else&lt;br /&gt;
			return vs, p, q&lt;br /&gt;
		end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- c in [0, 255], condition tweaked for no discontinuity&lt;br /&gt;
-- http://entropymine.com/imageworsener/srgbformula/&lt;br /&gt;
local function toLinear(c)&lt;br /&gt;
	if (c &amp;gt; 10.314300250662591) then&lt;br /&gt;
		return math.pow((c + 14.025) / 269.025, 2.4)&lt;br /&gt;
	else&lt;br /&gt;
		return c / 3294.6&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function toNonLinear(c)&lt;br /&gt;
	if (c &amp;gt; 0.00313066844250063) then&lt;br /&gt;
		return 269.025 * math.pow(c, 1.0/2.4) - 14.025&lt;br /&gt;
	else&lt;br /&gt;
		return 3294.6 * c&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function srgbToCielchuvD65o2deg(r, g, b)&lt;br /&gt;
	local R = toLinear(r)&lt;br /&gt;
	local G = toLinear(g)&lt;br /&gt;
	local B = toLinear(b)&lt;br /&gt;
	-- https://github.com/w3c/csswg-drafts/issues/5922&lt;br /&gt;
	local X = 0.1804807884018343 * B + 0.357584339383878 * G + 0.41239079926595934 * R&lt;br /&gt;
	local Y = 0.07219231536073371 * B + 0.21263900587151027 * R + 0.715168678767756 * G&lt;br /&gt;
	local Z = 0.01933081871559182 * R + 0.11919477979462598 * G + 0.9505321522496607 * B&lt;br /&gt;
	local L, C, h&lt;br /&gt;
	if (Y &amp;gt; 0.00885645167903563082) then&lt;br /&gt;
		L = 116 * math.pow(Y, 1/3) - 16&lt;br /&gt;
	else&lt;br /&gt;
		L = Y * 903.2962962962962962963&lt;br /&gt;
	end&lt;br /&gt;
	if ((r == g and g == b) or L == 0) then&lt;br /&gt;
		C = 0&lt;br /&gt;
		h = 0&lt;br /&gt;
	else&lt;br /&gt;
		d = X + 3 * Z + 15 * Y&lt;br /&gt;
		if (d == 0) then&lt;br /&gt;
			C = 0&lt;br /&gt;
			h = 0&lt;br /&gt;
		else&lt;br /&gt;
			-- 0.19783... and 0.4631... computed with extra precision from (X,Y,Z) when (R,G,B) = (1,1,1),&lt;br /&gt;
			-- in which case (u,v) ≈ (0,0)&lt;br /&gt;
			local us = 4 * X / d - 0.19783000664283678994&lt;br /&gt;
			local vs = 9 * Y / d - 0.46831999493879099801&lt;br /&gt;
			h = math.atan2(vs, us) * 57.2957795130823208768&lt;br /&gt;
			if (h &amp;lt; 0) then&lt;br /&gt;
				h = h + 360&lt;br /&gt;
			elseif (h == 0) then&lt;br /&gt;
				h = 0 -- ensure zero is positive&lt;br /&gt;
			end&lt;br /&gt;
			C = math.sqrt(us * us + vs * vs) * 13 * L&lt;br /&gt;
			if (C == 0) then&lt;br /&gt;
				C = 0&lt;br /&gt;
				h = 0&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return L, C, h&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function checkInterpolationParameter(t)&lt;br /&gt;
	if (t &amp;gt; 1 or t &amp;lt; 0) then&lt;br /&gt;
		error(&amp;#039;Interpolation parameter out of bounds&amp;#039;)&lt;br /&gt;
	end&lt;br /&gt;
end	&lt;br /&gt;
&lt;br /&gt;
local function srgbMix(t, r0, g0, b0, r1, g1, b1)&lt;br /&gt;
	local tc = 1 - t&lt;br /&gt;
	return&lt;br /&gt;
		toNonLinear(tc * toLinear(r0) + t * toLinear(r1)),&lt;br /&gt;
		toNonLinear(tc * toLinear(g0) + t * toLinear(g1)),&lt;br /&gt;
		toNonLinear(tc * toLinear(b0) + t * toLinear(b1))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- functions for generating gradients, inspired by OKLCH but not needing gamut mapping&lt;br /&gt;
local function adjustHueToCielch(h)&lt;br /&gt;
	local n = 180 * math.floor(h / 180)&lt;br /&gt;
	local d = h - n&lt;br /&gt;
	if (d &amp;lt; 60) then&lt;br /&gt;
		d = 73.7 * d / 60&lt;br /&gt;
	elseif (d &amp;lt; 120) then&lt;br /&gt;
		d = 0.6975 * d + 31.85&lt;br /&gt;
	else&lt;br /&gt;
		d = 1.07416666666666666667 * d - 13.35&lt;br /&gt;
	end&lt;br /&gt;
	return n + d&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function unadjustHueFromCielch(h)&lt;br /&gt;
	local n = 180 * math.floor(h / 180)&lt;br /&gt;
	local d = h - n&lt;br /&gt;
	if (d &amp;lt; 73.7) then&lt;br /&gt;
		d = 0.81411126187245590231 * d&lt;br /&gt;
	elseif (d &amp;lt; 115.55) then&lt;br /&gt;
		d = 1.43369175627240143369 * d - 45.66308243727598566308&lt;br /&gt;
	else&lt;br /&gt;
		d = 0.93095422808378588053 * d + 12.42823894491854150504&lt;br /&gt;
	end&lt;br /&gt;
	return n + d&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getLightness(r, g, b)&lt;br /&gt;
	local Y = 0.07219231536073371 * toLinear(b) + 0.21263900587151027 * toLinear(r) + 0.715168678767756 * toLinear(g)&lt;br /&gt;
	if (Y &amp;gt; 0.00885645167903563082) then&lt;br /&gt;
		return 116 * math.pow(Y, 1/3) - 16&lt;br /&gt;
	else&lt;br /&gt;
		return Y * 903.2962962962962962963&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function adjustLightness(L, r, g, b)&lt;br /&gt;
	if (L &amp;gt;= 100) then&lt;br /&gt;
		return 255, 255, 255&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local Yc&lt;br /&gt;
	if (L &amp;gt; 8) then&lt;br /&gt;
		Yc = (L + 16) / 116&lt;br /&gt;
		Yc = Yc * Yc * Yc&lt;br /&gt;
	else&lt;br /&gt;
		Yc = L * 0.00110705645987945385&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local R = toLinear(r)&lt;br /&gt;
	local G = toLinear(g)&lt;br /&gt;
	local B = toLinear(b)&lt;br /&gt;
	local Y = 0.07219231536073371 * B + 0.21263900587151027 * R + 0.715168678767756 * G&lt;br /&gt;
	if (Y &amp;gt; 0) then&lt;br /&gt;
		local scale = Yc / Y&lt;br /&gt;
		R = R * scale&lt;br /&gt;
		G = G * scale&lt;br /&gt;
		B = B * scale&lt;br /&gt;
		local cmax = math.max(R, G, B)&lt;br /&gt;
		if (cmax &amp;gt; 1) then&lt;br /&gt;
			R = R / cmax&lt;br /&gt;
			G = G / cmax&lt;br /&gt;
			B = B / cmax&lt;br /&gt;
			local d = 0.07219231536073371 * (1 - B) + 0.21263900587151027 * (1 - R) + 0.715168678767756 * (1 - G)&lt;br /&gt;
			if (d &amp;lt;= 0) then&lt;br /&gt;
				R = 1&lt;br /&gt;
				G = 1&lt;br /&gt;
				B = 1&lt;br /&gt;
			else&lt;br /&gt;
				local strength = 0.5 -- 1 yields equal lightness&lt;br /&gt;
				local t = (Yc - 0.07219231536073371 * B - 0.21263900587151027 * R - 0.715168678767756 * G) / d&lt;br /&gt;
				R = R + strength * (1 - R) * t&lt;br /&gt;
				G = G + strength * (1 - G) * t&lt;br /&gt;
				B = B + strength * (1 - B) * t&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		R = Yc&lt;br /&gt;
		G = Yc&lt;br /&gt;
		B = Yc&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return toNonLinear(R), toNonLinear(G), toNonLinear(B)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function interpolateHue(t, r0, g0, b0, r1, g1, b1, direction)&lt;br /&gt;
	local h0, s0, v0 = rgbToHsv(r0, g0, b0)&lt;br /&gt;
	local h1, s1, v1 = rgbToHsv(r1, g1, b1)&lt;br /&gt;
	&lt;br /&gt;
	if (s0 == 0) then&lt;br /&gt;
		h0 = h1&lt;br /&gt;
		if (v0 == 0) then&lt;br /&gt;
			s0 = s1&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if (s1 == 0) then&lt;br /&gt;
		h1 = h0&lt;br /&gt;
		if (v1 == 0) then&lt;br /&gt;
			s1 = s1&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local hn0 = h0 / 360&lt;br /&gt;
	local hn1 = h1 / 360&lt;br /&gt;
	if (direction == 0) then&lt;br /&gt;
		local dhn = hn1 - hn0&lt;br /&gt;
		if (dhn &amp;gt; 0.5) then&lt;br /&gt;
			dhn = dhn - math.ceil(dhn - 0.5)&lt;br /&gt;
		elseif (dhn &amp;lt; -0.5) then&lt;br /&gt;
			dhn = dhn - math.floor(dhn + 0.5)&lt;br /&gt;
		end&lt;br /&gt;
		if (dhn &amp;gt;= 0) then&lt;br /&gt;
			hn0 = hn0 - math.floor(hn0)&lt;br /&gt;
			hn1 = hn0 + dhn&lt;br /&gt;
		else&lt;br /&gt;
			hn1 = hn1 - math.floor(hn1)&lt;br /&gt;
			hn0 = hn1 - dhn&lt;br /&gt;
		end&lt;br /&gt;
	elseif (direction &amp;gt; 0) then&lt;br /&gt;
		hn1 = 1 - math.ceil(hn1 - hn0) - math.floor(hn0) + hn1&lt;br /&gt;
		hn0 = hn0 - math.floor(hn0)&lt;br /&gt;
	else&lt;br /&gt;
		hn0 = 1 - math.ceil(hn0 - hn1) - math.floor(hn1) + hn0&lt;br /&gt;
		hn1 = hn1 - math.floor(hn1)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if (t &amp;lt; 0) then&lt;br /&gt;
		t = 0&lt;br /&gt;
	elseif (t &amp;gt; 1) then&lt;br /&gt;
		t = 1&lt;br /&gt;
	end&lt;br /&gt;
	local tc = 1 - t&lt;br /&gt;
	local ha = tc * adjustHueToCielch(360 * hn0) + t * adjustHueToCielch(360 * hn1)&lt;br /&gt;
	local r, g, b = hsvToRgb(unadjustHueFromCielch(ha), tc * s0 + t * s1, tc * v0 + t * v1)&lt;br /&gt;
	&lt;br /&gt;
	local L0 = getLightness(r0, g0, b0)&lt;br /&gt;
	local L1 = getLightness(r1, g1, b1)&lt;br /&gt;
	return adjustLightness(tc * L0 + t * L1, r, g, b)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function formatToPrecision(value, p)&lt;br /&gt;
	return string.format(&amp;#039;%.&amp;#039; .. p .. &amp;#039;f&amp;#039;, value)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getFractionalZeros(p)&lt;br /&gt;
	if (p &amp;gt; 0) then&lt;br /&gt;
		return &amp;#039;.&amp;#039; .. string.rep(&amp;#039;0&amp;#039;, p)&lt;br /&gt;
	else&lt;br /&gt;
		return &amp;#039;&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function polyMix(t, palette)&lt;br /&gt;
	if (t &amp;lt;= 0) then&lt;br /&gt;
		return palette[1]&lt;br /&gt;
	elseif (t &amp;gt;= 1) then&lt;br /&gt;
		return palette[#palette]&lt;br /&gt;
	end&lt;br /&gt;
	local n, f = math.modf(t * (#palette - 1))&lt;br /&gt;
	if (f == 0) then&lt;br /&gt;
		return palette[n + 1]&lt;br /&gt;
	else&lt;br /&gt;
		local r0, g0, b0 = hexToRgb(palette[n + 1])&lt;br /&gt;
		local r1, g1, b1 = hexToRgb(palette[n + 2])&lt;br /&gt;
		return rgbToHex(srgbMix(f, r0, g0, b0, r1, g1, b1))&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- same principle: https://colorspace.r-forge.r-project.org/articles/hcl_palettes.html&lt;br /&gt;
-- the darkest colors do not yield an WCAG AA contrast with text, maybe this can be solved by using HCL Wizard from R&amp;#039;s Colorspace package&lt;br /&gt;
-- https://colorspace.r-forge.r-project.org/articles/approximations.html&lt;br /&gt;
-- R&amp;#039;s Colorspace does gamut mapping through simple clipping (as do most other color libraries, such as chroma.js and colorio), which is fast but not good&lt;br /&gt;
local function brewerGradient(t, palette)&lt;br /&gt;
	local colors = {&lt;br /&gt;
		spectral = { &amp;#039;9E0142&amp;#039;, &amp;#039;D53E4F&amp;#039;, &amp;#039;F46D43&amp;#039;, &amp;#039;FDAE61&amp;#039;, &amp;#039;FEE08B&amp;#039;, &amp;#039;FFFFBF&amp;#039;, &amp;#039;E6F598&amp;#039;, &amp;#039;ABDDA4&amp;#039;, &amp;#039;66C2A5&amp;#039;, &amp;#039;3288BD&amp;#039;, &amp;#039;5E4FA2&amp;#039; },&lt;br /&gt;
		rdylgn = { &amp;#039;A50026&amp;#039;, &amp;#039;D73027&amp;#039;, &amp;#039;F46D43&amp;#039;, &amp;#039;FDAE61&amp;#039;, &amp;#039;FEE08B&amp;#039;, &amp;#039;FFFFBF&amp;#039;, &amp;#039;D9EF8B&amp;#039;, &amp;#039;A6D96A&amp;#039;, &amp;#039;66BD63&amp;#039;, &amp;#039;1A9850&amp;#039;, &amp;#039;006837&amp;#039; },&lt;br /&gt;
		rdylbu = { &amp;#039;A50026&amp;#039;, &amp;#039;D73027&amp;#039;, &amp;#039;F46D43&amp;#039;, &amp;#039;FDAE61&amp;#039;, &amp;#039;FEE090&amp;#039;, &amp;#039;FFFFBF&amp;#039;, &amp;#039;E0F3F8&amp;#039;, &amp;#039;ABD9E9&amp;#039;, &amp;#039;74ADD1&amp;#039;, &amp;#039;4575B4&amp;#039;, &amp;#039;313695&amp;#039; },&lt;br /&gt;
		piyg = { &amp;#039;8E0152&amp;#039;, &amp;#039;C51B7D&amp;#039;, &amp;#039;DE77AE&amp;#039;, &amp;#039;F1B6DA&amp;#039;, &amp;#039;FDE0EF&amp;#039;, &amp;#039;F7F7F7&amp;#039;, &amp;#039;E6F5D0&amp;#039;, &amp;#039;B8E186&amp;#039;, &amp;#039;7FBC41&amp;#039;, &amp;#039;4D9221&amp;#039;, &amp;#039;276419&amp;#039; },&lt;br /&gt;
		brbg = { &amp;#039;543005&amp;#039;, &amp;#039;8C510A&amp;#039;, &amp;#039;BF812D&amp;#039;, &amp;#039;DFC27D&amp;#039;, &amp;#039;F6E8C3&amp;#039;, &amp;#039;F5F5F5&amp;#039;, &amp;#039;C7EAE5&amp;#039;, &amp;#039;80CDC1&amp;#039;, &amp;#039;35978F&amp;#039;, &amp;#039;01665E&amp;#039;, &amp;#039;003C30&amp;#039; },&lt;br /&gt;
		rdbu = { &amp;#039;67001F&amp;#039;, &amp;#039;B2182B&amp;#039;, &amp;#039;D6604D&amp;#039;, &amp;#039;F4A582&amp;#039;, &amp;#039;FDDBC7&amp;#039;, &amp;#039;F7F7F7&amp;#039;, &amp;#039;D1E5F0&amp;#039;, &amp;#039;92C5DE&amp;#039;, &amp;#039;4393C3&amp;#039;, &amp;#039;2166AC&amp;#039;, &amp;#039;053061&amp;#039; },&lt;br /&gt;
		prgn = { &amp;#039;40004B&amp;#039;, &amp;#039;762A83&amp;#039;, &amp;#039;9970AB&amp;#039;, &amp;#039;C2A5CF&amp;#039;, &amp;#039;E7D4E8&amp;#039;, &amp;#039;F7F7F7&amp;#039;, &amp;#039;D9F0D3&amp;#039;, &amp;#039;A6DBA0&amp;#039;, &amp;#039;5AAE61&amp;#039;, &amp;#039;1B7837&amp;#039;, &amp;#039;00441B&amp;#039; },&lt;br /&gt;
		puor = { &amp;#039;7F3B08&amp;#039;, &amp;#039;B35806&amp;#039;, &amp;#039;E08214&amp;#039;, &amp;#039;FDB863&amp;#039;, &amp;#039;FEE0B6&amp;#039;, &amp;#039;F7F7F7&amp;#039;, &amp;#039;D8DAEB&amp;#039;, &amp;#039;B2ABD2&amp;#039;, &amp;#039;8073AC&amp;#039;, &amp;#039;542788&amp;#039;, &amp;#039;2D004B&amp;#039; },&lt;br /&gt;
		rdgy = { &amp;#039;67001F&amp;#039;, &amp;#039;B2182B&amp;#039;, &amp;#039;D6604D&amp;#039;, &amp;#039;F4A582&amp;#039;, &amp;#039;FDDBC7&amp;#039;, &amp;#039;FFFFFF&amp;#039;, &amp;#039;E0E0E0&amp;#039;, &amp;#039;BABABA&amp;#039;, &amp;#039;878787&amp;#039;, &amp;#039;4D4D4D&amp;#039;, &amp;#039;1A1A1A&amp;#039; },&lt;br /&gt;
		pubugn = { &amp;#039;FFF7FB&amp;#039;, &amp;#039;ECE2F0&amp;#039;, &amp;#039;D0D1E6&amp;#039;, &amp;#039;A6BDDB&amp;#039;, &amp;#039;67A9CF&amp;#039;, &amp;#039;3690C0&amp;#039;, &amp;#039;02818A&amp;#039;, &amp;#039;016C59&amp;#039;, &amp;#039;014636&amp;#039; },&lt;br /&gt;
		ylorrd = { &amp;#039;FFFFCC&amp;#039;, &amp;#039;FFEDA0&amp;#039;, &amp;#039;FED976&amp;#039;, &amp;#039;FEB24C&amp;#039;, &amp;#039;FD8D3C&amp;#039;, &amp;#039;FC4E2A&amp;#039;, &amp;#039;E31A1C&amp;#039;, &amp;#039;BD0026&amp;#039;, &amp;#039;800026&amp;#039; },&lt;br /&gt;
		ylorbr = { &amp;#039;FFFFE5&amp;#039;, &amp;#039;FFF7BC&amp;#039;, &amp;#039;FEE391&amp;#039;, &amp;#039;FEC44F&amp;#039;, &amp;#039;FE9929&amp;#039;, &amp;#039;EC7014&amp;#039;, &amp;#039;CC4C02&amp;#039;, &amp;#039;993404&amp;#039;, &amp;#039;662506&amp;#039; },&lt;br /&gt;
		ylgnbu = { &amp;#039;FFFFD9&amp;#039;, &amp;#039;EDF8B1&amp;#039;, &amp;#039;C7E9B4&amp;#039;, &amp;#039;7FCDBB&amp;#039;, &amp;#039;41B6C4&amp;#039;, &amp;#039;1D91C0&amp;#039;, &amp;#039;225EA8&amp;#039;, &amp;#039;253494&amp;#039;, &amp;#039;081D58&amp;#039; },&lt;br /&gt;
		gnbu = { &amp;#039;F7FCF0&amp;#039;, &amp;#039;E0F3DB&amp;#039;, &amp;#039;CCEBC5&amp;#039;, &amp;#039;A8DDB5&amp;#039;, &amp;#039;7BCCC4&amp;#039;, &amp;#039;4EB3D3&amp;#039;, &amp;#039;2B8CBE&amp;#039;, &amp;#039;0868AC&amp;#039;, &amp;#039;084081&amp;#039; },&lt;br /&gt;
		orrd = { &amp;#039;FFF7EC&amp;#039;, &amp;#039;FEE8C8&amp;#039;, &amp;#039;FDD49E&amp;#039;, &amp;#039;FDBB84&amp;#039;, &amp;#039;FC8D59&amp;#039;, &amp;#039;EF6548&amp;#039;, &amp;#039;D7301F&amp;#039;, &amp;#039;B30000&amp;#039;, &amp;#039;7F0000&amp;#039; },&lt;br /&gt;
		ylgn = { &amp;#039;FFFFE5&amp;#039;, &amp;#039;F7FCB9&amp;#039;, &amp;#039;D9F0A3&amp;#039;, &amp;#039;ADDD8E&amp;#039;, &amp;#039;78C679&amp;#039;, &amp;#039;41AB5D&amp;#039;, &amp;#039;238443&amp;#039;, &amp;#039;006837&amp;#039;, &amp;#039;004529&amp;#039; },&lt;br /&gt;
		bugn = { &amp;#039;F7FCFD&amp;#039;, &amp;#039;E5F5F9&amp;#039;, &amp;#039;CCECE6&amp;#039;, &amp;#039;99D8C9&amp;#039;, &amp;#039;66C2A4&amp;#039;, &amp;#039;41AE76&amp;#039;, &amp;#039;238B45&amp;#039;, &amp;#039;006D2C&amp;#039;, &amp;#039;00441B&amp;#039; },&lt;br /&gt;
		pubu = { &amp;#039;FFF7FB&amp;#039;, &amp;#039;ECE7F2&amp;#039;, &amp;#039;D0D1E6&amp;#039;, &amp;#039;A6BDDB&amp;#039;, &amp;#039;74A9CF&amp;#039;, &amp;#039;3690C0&amp;#039;, &amp;#039;0570B0&amp;#039;, &amp;#039;045A8D&amp;#039;, &amp;#039;023858&amp;#039; },&lt;br /&gt;
		purd = { &amp;#039;F7F4F9&amp;#039;, &amp;#039;E7E1EF&amp;#039;, &amp;#039;D4B9DA&amp;#039;, &amp;#039;C994C7&amp;#039;, &amp;#039;DF65B0&amp;#039;, &amp;#039;E7298A&amp;#039;, &amp;#039;CE1256&amp;#039;, &amp;#039;980043&amp;#039;, &amp;#039;67001F&amp;#039; },&lt;br /&gt;
		rdpu = { &amp;#039;FFF7F3&amp;#039;, &amp;#039;FDE0DD&amp;#039;, &amp;#039;FCC5C0&amp;#039;, &amp;#039;FA9FB5&amp;#039;, &amp;#039;F768A1&amp;#039;, &amp;#039;DD3497&amp;#039;, &amp;#039;AE017E&amp;#039;, &amp;#039;7A0177&amp;#039;, &amp;#039;49006A&amp;#039; },&lt;br /&gt;
		bupu = { &amp;#039;F7FCFD&amp;#039;, &amp;#039;E0ECF4&amp;#039;, &amp;#039;BFD3E6&amp;#039;, &amp;#039;9EBCDA&amp;#039;, &amp;#039;8C96C6&amp;#039;, &amp;#039;8C6BB1&amp;#039;, &amp;#039;88419D&amp;#039;, &amp;#039;810F7C&amp;#039;, &amp;#039;4D004B&amp;#039; },&lt;br /&gt;
		oranges = { &amp;#039;FFF5EB&amp;#039;, &amp;#039;FEE6CE&amp;#039;, &amp;#039;FDD0A2&amp;#039;, &amp;#039;FDAE6B&amp;#039;, &amp;#039;FD8D3C&amp;#039;, &amp;#039;F16913&amp;#039;, &amp;#039;D94801&amp;#039;, &amp;#039;A63603&amp;#039;, &amp;#039;7F2704&amp;#039; },&lt;br /&gt;
		greens = { &amp;#039;F7FCF5&amp;#039;, &amp;#039;E5F5E0&amp;#039;, &amp;#039;C7E9C0&amp;#039;, &amp;#039;A1D99B&amp;#039;, &amp;#039;74C476&amp;#039;, &amp;#039;41AB5D&amp;#039;, &amp;#039;238B45&amp;#039;, &amp;#039;006D2C&amp;#039;, &amp;#039;00441B&amp;#039; },&lt;br /&gt;
		blues = { &amp;#039;F7FBFF&amp;#039;, &amp;#039;DEEBF7&amp;#039;, &amp;#039;C6DBEF&amp;#039;, &amp;#039;9ECAE1&amp;#039;, &amp;#039;6BAED6&amp;#039;, &amp;#039;4292C6&amp;#039;, &amp;#039;2171B5&amp;#039;, &amp;#039;08519C&amp;#039;, &amp;#039;08306B&amp;#039; },&lt;br /&gt;
		reds = { &amp;#039;FFF5F0&amp;#039;, &amp;#039;FEE0D2&amp;#039;, &amp;#039;FCBBA1&amp;#039;, &amp;#039;FC9272&amp;#039;, &amp;#039;FB6A4A&amp;#039;, &amp;#039;EF3B2C&amp;#039;, &amp;#039;CB181D&amp;#039;, &amp;#039;A50F15&amp;#039;, &amp;#039;67000D&amp;#039; },&lt;br /&gt;
		purples = { &amp;#039;FCFBFD&amp;#039;, &amp;#039;EFEDF5&amp;#039;, &amp;#039;DADAEB&amp;#039;, &amp;#039;BCBDDC&amp;#039;, &amp;#039;9E9AC8&amp;#039;, &amp;#039;807DBA&amp;#039;, &amp;#039;6A51A3&amp;#039;, &amp;#039;54278F&amp;#039;, &amp;#039;3F007D&amp;#039; },&lt;br /&gt;
		greys = { &amp;#039;FFFFFF&amp;#039;, &amp;#039;F0F0F0&amp;#039;, &amp;#039;D9D9D9&amp;#039;, &amp;#039;BDBDBD&amp;#039;, &amp;#039;969696&amp;#039;, &amp;#039;737373&amp;#039;, &amp;#039;525252&amp;#039;, &amp;#039;252525&amp;#039;, &amp;#039;000000&amp;#039; }&lt;br /&gt;
	}&lt;br /&gt;
	return polyMix(t, colors[palette])&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function softSigmoid(x)&lt;br /&gt;
	local ax = math.abs(x)&lt;br /&gt;
	if (ax &amp;gt; 0.000000000000000111) then&lt;br /&gt;
		return x / (1 + ax)&lt;br /&gt;
	else&lt;br /&gt;
		return x&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.hexToRgbTriplet(frame)&lt;br /&gt;
	local args = frame.args or frame:getParent().args&lt;br /&gt;
	local hex = args[1]&lt;br /&gt;
	if (isEmpty(hex)) then&lt;br /&gt;
		return &amp;#039;&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	local r, g, b = hexToRgb(hex)&lt;br /&gt;
	return r .. &amp;#039;, &amp;#039; .. g .. &amp;#039;, &amp;#039; .. b&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.hexToCmyk(frame)&lt;br /&gt;
	local args = frame.args or frame:getParent().args&lt;br /&gt;
	local hex = args[1]&lt;br /&gt;
	if (isEmpty(hex)) then&lt;br /&gt;
		return &amp;#039;&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	local p = numArgDefault(args.precision, 0)&lt;br /&gt;
	local s = args.pctsign or &amp;#039;1&amp;#039;&lt;br /&gt;
	local c, m, y, k = rgbToCmyk(hexToRgb(hex))&lt;br /&gt;
	local fk = formatToPrecision(k, p)&lt;br /&gt;
	local fc, fm, fy&lt;br /&gt;
	local fracZeros = getFractionalZeros(p)&lt;br /&gt;
	if (fk == 100  .. fracZeros) then&lt;br /&gt;
		local fZero = 0 .. fracZeros&lt;br /&gt;
		fc = fZero&lt;br /&gt;
		fm = fZero&lt;br /&gt;
		fy = fZero&lt;br /&gt;
	else&lt;br /&gt;
		fc = formatToPrecision(c, p)&lt;br /&gt;
		fm = formatToPrecision(m, p)&lt;br /&gt;
		fy = formatToPrecision(y, p)&lt;br /&gt;
	end&lt;br /&gt;
	if (s ~= &amp;#039;0&amp;#039;) then&lt;br /&gt;
		return fc .. &amp;#039;%, &amp;#039; .. fm .. &amp;#039;%, &amp;#039; .. fy .. &amp;#039;%, &amp;#039; .. fk .. &amp;#039;%&amp;#039;&lt;br /&gt;
	else&lt;br /&gt;
		return fc .. &amp;#039;, &amp;#039; .. fm .. &amp;#039;, &amp;#039; .. fy .. &amp;#039;, &amp;#039; .. fk&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.hexToHsl(frame)&lt;br /&gt;
	local args = frame.args or frame:getParent().args&lt;br /&gt;
	local hex = args[1]&lt;br /&gt;
	if (isEmpty(hex)) then&lt;br /&gt;
		return &amp;#039;&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	local p = numArgDefault(args.precision, 0)&lt;br /&gt;
	local h, s, l = rgbToHsl(hexToRgb(hex))&lt;br /&gt;
	local fl = formatToPrecision(l, p)&lt;br /&gt;
	local fs, fh&lt;br /&gt;
	local fracZeros = getFractionalZeros(p)&lt;br /&gt;
	local fZero = 0 .. fracZeros&lt;br /&gt;
	if (fl == fZero or fl == 100 .. fracZeros) then&lt;br /&gt;
		fs = fZero&lt;br /&gt;
		fh = fZero&lt;br /&gt;
	else&lt;br /&gt;
		fs = formatToPrecision(s, p)&lt;br /&gt;
		if (fs == fZero) then&lt;br /&gt;
			fh = fZero&lt;br /&gt;
		else&lt;br /&gt;
			fh = formatToPrecision(h, p)&lt;br /&gt;
			if (fh == 360 .. fracZeros) then&lt;br /&gt;
				fh = fZero -- handle rounding to 360&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return fh .. &amp;#039;°, &amp;#039; .. fs .. &amp;#039;%, &amp;#039; .. fl .. &amp;#039;%&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.hexToHsv(frame)&lt;br /&gt;
	local args = frame.args or frame:getParent().args&lt;br /&gt;
	local hex = args[1]&lt;br /&gt;
	if (isEmpty(hex)) then&lt;br /&gt;
		return &amp;#039;&amp;#039;&lt;br /&gt;
	end	&lt;br /&gt;
	local p = numArgDefault(args.precision, 0)&lt;br /&gt;
	local h, s, v = rgbToHsv(hexToRgb(hex))&lt;br /&gt;
	local fv = formatToPrecision(v, p)&lt;br /&gt;
	local fs, fh&lt;br /&gt;
	local fracZeros = getFractionalZeros(p)&lt;br /&gt;
	local fZero = 0 .. fracZeros&lt;br /&gt;
	if (fv == fZero) then&lt;br /&gt;
		fh = fZero&lt;br /&gt;
		fs = fZero&lt;br /&gt;
	else&lt;br /&gt;
		fs = formatToPrecision(s, p)&lt;br /&gt;
		if (fs == fZero) then&lt;br /&gt;
			fh = fZero&lt;br /&gt;
		else&lt;br /&gt;
			fh = formatToPrecision(h, p)&lt;br /&gt;
			if (fh == 360 .. fracZeros) then&lt;br /&gt;
				fh = fZero -- handle rounding to 360&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return fh .. &amp;#039;°, &amp;#039; .. fs .. &amp;#039;%, &amp;#039; .. fv .. &amp;#039;%&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.hexToCielch(frame)&lt;br /&gt;
	local args = frame.args or frame:getParent().args&lt;br /&gt;
	local hex = args[1]&lt;br /&gt;
	if (isEmpty(hex)) then&lt;br /&gt;
		return &amp;#039;&amp;#039;&lt;br /&gt;
	end	&lt;br /&gt;
	local p = numArgDefault(args.precision, 0)&lt;br /&gt;
	local L, C, h = srgbToCielchuvD65o2deg(hexToRgb(hex))&lt;br /&gt;
	local fL = formatToPrecision(L, p)&lt;br /&gt;
	local fC, fh&lt;br /&gt;
	local fracZeros = getFractionalZeros(p)&lt;br /&gt;
	local fZero = 0 .. fracZeros&lt;br /&gt;
	if (fL == fZero or fL == 100 .. fracZeros) then&lt;br /&gt;
		fC = fZero&lt;br /&gt;
		fh = fZero&lt;br /&gt;
	else&lt;br /&gt;
		fC = formatToPrecision(C, p)&lt;br /&gt;
		if (fC == fZero) then&lt;br /&gt;
			fh = fZero&lt;br /&gt;
		else&lt;br /&gt;
			fh = formatToPrecision(h, p)&lt;br /&gt;
			if (fh == 360 .. fracZeros) then&lt;br /&gt;
				fh = fZero -- handle rounding to 360&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return fL .. &amp;#039;, &amp;#039; .. fC .. &amp;#039;, &amp;#039; .. fh .. &amp;#039;°&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.hexMix(frame)&lt;br /&gt;
	local args = frame.args or frame:getParent().args&lt;br /&gt;
	local hex0 = args[1]&lt;br /&gt;
	local hex1 = args[2]&lt;br /&gt;
	if (isEmpty(hex0) or isEmpty(hex1)) then&lt;br /&gt;
		return &amp;#039;&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	local t = args[3]&lt;br /&gt;
	if (isEmpty(t)) then&lt;br /&gt;
		t = 0.5&lt;br /&gt;
	else&lt;br /&gt;
		t = tonumber(t)&lt;br /&gt;
		local amin = numArgDefault(args.min, 0)&lt;br /&gt;
		local amax = numArgDefault(args.max, 100)&lt;br /&gt;
		if (amax == amin) then&lt;br /&gt;
			t = 0.5&lt;br /&gt;
		else&lt;br /&gt;
			t = (t - amin) / (amax - amin)&lt;br /&gt;
			if (t &amp;gt; 1) then&lt;br /&gt;
				t = 1&lt;br /&gt;
			elseif (t &amp;lt; 0) then&lt;br /&gt;
				t = 0&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	local r0, g0, b0 = hexToRgb(hex0)&lt;br /&gt;
	local r1, g1, b1 = hexToRgb(hex1)&lt;br /&gt;
	return rgbToHex(srgbMix(t, r0, g0, b0, r1, g1, b1))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.hexInterpolate(frame)&lt;br /&gt;
	local args = frame.args or frame:getParent().args&lt;br /&gt;
	local hex0 = args[1]&lt;br /&gt;
	local hex1 = args[2]&lt;br /&gt;
	if (isEmpty(hex0)) then&lt;br /&gt;
		return hex1&lt;br /&gt;
	elseif (isEmpty(hex1)) then&lt;br /&gt;
		return hex0&lt;br /&gt;
	end&lt;br /&gt;
	local t = args[3]&lt;br /&gt;
	if (isEmpty(t)) then&lt;br /&gt;
		t = 0.5&lt;br /&gt;
	else&lt;br /&gt;
		t = tonumber(t)&lt;br /&gt;
		local amin = numArgDefault(args.min, 0)&lt;br /&gt;
		local amax = numArgDefault(args.max, 100)&lt;br /&gt;
		if (amax == amin) then&lt;br /&gt;
			t = 0.5&lt;br /&gt;
		else&lt;br /&gt;
			t = (t - amin) / (amax - amin)&lt;br /&gt;
			if (t &amp;gt; 1) then&lt;br /&gt;
				t = 1&lt;br /&gt;
			elseif (t &amp;lt; 0) then&lt;br /&gt;
				t = 0&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	local direction = numArgDefault(args.direction, 0)&lt;br /&gt;
	local r0, g0, b0 = hexToRgb(hex0)&lt;br /&gt;
	local r1, g1, b1 = hexToRgb(hex1)&lt;br /&gt;
	return rgbToHex(interpolateHue(t, r0, g0, b0, r1, g1, b1, direction))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.hexBrewerGradient(frame)&lt;br /&gt;
	local args = frame.args or frame:getParent().args&lt;br /&gt;
	local pal = argDefault(args.pal, &amp;#039;spectral&amp;#039;):lower()&lt;br /&gt;
	local value = args[1]&lt;br /&gt;
	local t&lt;br /&gt;
	if (isEmpty(value)) then&lt;br /&gt;
		t = 0.5&lt;br /&gt;
	else&lt;br /&gt;
		value = tonumber(value)&lt;br /&gt;
		local high = numArgDefault(args.high, 100)&lt;br /&gt;
		local low = numArgDefault(args.low, -100)&lt;br /&gt;
		if (isEmpty(args.low)) then&lt;br /&gt;
			if (pal ~= &amp;#039;spectral&amp;#039; and pal ~= &amp;#039;rdylgn&amp;#039; and pal ~= &amp;#039;rdylbu&amp;#039; and (pal:len() ~= 4 or&lt;br /&gt;
				(pal ~= &amp;#039;rdgy&amp;#039; and pal ~= &amp;#039;rdbu&amp;#039; and pal ~= &amp;#039;puor&amp;#039; and pal ~= &amp;#039;prgn&amp;#039; and pal ~= &amp;#039;piyg&amp;#039; and pal ~= &amp;#039;brbg&amp;#039;))) then&lt;br /&gt;
				low = 0&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		if (high == low) then&lt;br /&gt;
			t = 0.5&lt;br /&gt;
		elseif (isArgTrue(args.inv)) then&lt;br /&gt;
			t = (high - value) / (high - low)&lt;br /&gt;
		else&lt;br /&gt;
			t = (value - low) / (high - low)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if (isArgTrue(args.comp)) then&lt;br /&gt;
		t = 0.5 * softSigmoid(2 * t - 1) + 0.5&lt;br /&gt;
	end&lt;br /&gt;
	return brewerGradient(t, pal)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>imported&gt;Ftrebien</name></author>
	</entry>
</feed>