sorcery  color.lua at [794d5b023a]

File lib/color.lua artifact ef48edf309 part of check-in 794d5b023a


local color
color = sorcery.lib.class {
	__tostring = function(self)
		-- return minetest.rgba(
		-- 	self.red,
		-- 	self.green,
		-- 	self.blue,
		-- 	self.alpha
		-- )
		local hex = function(val)
			return string.format('%02X',math.max(0,math.min(255,math.floor(val))))
		end
		local str = '#' ..
			hex(self.red) ..
			hex(self.green) ..
			hex(self.blue)
		if self.alpha then str = str .. hex(self.alpha) end
		return str
	end;

	__add = function(self, other)
		local sfac = (self.alpha or 255) / 255
		local ofac = (other.alpha or 255) / 255
		if self.alpha == other.alpha then
			sfac = 1 ofac = 1
		end

		local sr, sg, sb = other.red * ofac, other.blue * ofac, other.green * ofac
		local nr, ng, nb =  self.red * sfac,  self.blue * sfac,  self.green * sfac
		local saturate = function(a,b)
			return math.max(0, math.min(255, a+b))
		end
		local alpha = nil
		if self.alpha and other.alpha then
			alpha = saturate(self.alpha or 255, other.alpha or 255)
		end
		return color(
			saturate(sr, nr),
			saturate(sg, ng),
			saturate(sb, nb),
			alpha
		)
	end;
	
	cast = {
		number = function(n) return {
			red = n; green = n; blue = n;
		} end;
		table = function(t) return {
			red = t[1]; green = t[2]; blue = t[3];
		} end;
	};

	construct = function(r,g,b,a)
		local clip = function(v)
			return math.max(0,math.min(255,v))
		end;
                local from_hsl = function(hsl, alpha)
                        -- Based on the algorithm in Computer Graphics: Principles and Practice, by
                        -- James D. Foley et. al., 2nd ed., p. 596
                        -- Degree version, though radian is more natural, I don't want to translate it yet
                        local h = hsl.hue
                        local s = hsl.saturation
                        local l = hsl.luminosity
                        local value = function(n1, n2, hue)
                                if hue > 360 then
                                        hue = hue - 360
                                elseif hue < 0 then
                                        hue = hue + 360
                                end
                                if hue < 60 then
                                        return n1 + (n2 - n1) * hue/60
                                elseif hue < 180 then
                                        return n2
                                elseif hue < 240 then
                                        return n1 + (n2 - n1) * (240 - hue)/60
                                else
                                        return n1
                                end
                        end
                        local m2
                        if l < 0.5 then
                                m2 = l * (1 + s)
                        else
                                m2 = l + s - l * s
                        end
                        local m1 = 2 * l - m2
                        if s == 0 then
                                -- Achromatic, there is no hue
                                -- In book this errors if hue is not undefined, but we set hue to 0 in this case, not nil or something, so
                                return color(l, l, l, alpha)
                        else
                                -- Chromatic case, so there is a hue
                                return color(
                                        clip(value(m1, m2, h + 120)*255),
                                        clip(value(m1, m2, h)*255),
                                        clip(value(m1, m2, h - 120)*255),
                                        alpha
                                )
                        end
                end;
		local warp = function(f)
			return function(self, ...)
				local n = color(self)
				f(n, ...)
				return n
			end;
		end;
		local new = {
			hex = function(self) return
				getmetatable(self).__tostring(self)
			end;

			fmt = function(self, text) return
				minetest.colorize(self:hex(), text)
			end;

			luminosity = function(self) return
				(self.red + self.green + self.blue) / 3
			end;

                        to_hsl = function(self)
                                -- Based on the algorithm in Computer Graphics: Principles and Practice, by
                                -- James D. Foley et. al., 2nd ed., p. 595
                                -- We need the rgb between 0 and 1
                                local r = self.red/255
                                local g = self.green/255
                                local b = self.blue/255
                                local max = math.max(r, g, b)
                                local min = math.min(r, g, b)
                                local luminosity = (max + min)/2
                                local hue = 0
                                local saturation = 0
                                if max == min then
                                        -- Achromatic case, because r=g=b
                                        saturation = 0
                                        hue = 0 -- Undefined, so just replace w/ 0 for usability
                                else
                                        -- Chromatic case
                                        if luminosity <= 0.5 then
                                                saturation = (max - min)/(max + min)
                                        else
                                                saturation = (max - min)/(2 - max - min)
                                        end
                                        -- Next calculate the hue
                                        local delta = max - min
                                        if r == max then
                                                hue = (g - b)/delta
                                        elseif g == max then
                                                hue = 2 + (b - r)/delta
                                        else -- blue must be max, so no point in checking
                                                hue = 4 + (r - g)/delta
                                        end
                                        hue = hue * 60 -- degrees
                                        --hue = hue * (math.pi / 3) -- for hue in radians instead of degrees
                                        if hue < 0 then
                                                hue = hue + 2 * math.pi
                                        end
                                end
                                print("r"..self.red.."g"..self.green.."b"..self.blue.." is h"..hue.."s"..saturation.."l"..luminosity)
                                local temp = from_hsl({hue=hue,saturation=saturation,luminosity=luminosity},self.alpha)
                                print("back is r"..temp.red.."g"..temp.green.."b"..temp.blue)
                                return { hue = hue, saturation = saturation, luminosity = luminosity }
                        end;

			readable = function(self, target)
				target = target or 0.5
                                local hsl = self:to_hsl()
                                hsl.luminosity = target
                                return from_hsl(hsl, self.alpha)
			end;

			bg = function(self, text) return
				text .. minetest.get_background_escape_sequence(self:hex())
			end;

			fade = warp(function(new, fac)
				new.alpha = math.min(255, (new.alpha or 255) * fac)
			end);

			brighten = function(self, fac)
                                -- Use HSL to brighten
                                -- To HSL
                                local hsl = self:to_hsl()
                                -- Do the calculation, clamp to 0-1 instead of the clamp fn
                                hsl.luminosity = math.min(math.max(hsl.luminosity * fac, 0), 1)
                                -- Turn back into RGB color
                                local t = from_hsl(hsl, self.alpha)
                                print("brighten is r"..t.red.."g"..t.green.."b"..t.blue)
                                return from_hsl(hsl, self.alpha)
			end;

			darken = warp(function(new, fac)
                                -- TODO: is there any point to this being different than brighten? Probably especially not now.
				new.red = clip(new.red - (new.red * fac))
				new.blue = clip(new.blue - (new.blue * fac))
				new.green = clip(new.green - (new.green * fac))
			end);
		}
		if g == nil then
			if type(r) == 'string' then
				assert(false) -- TODO parse color string
			elseif type(r) == 'table' then
				new.red = r[1]
				new.green = r[2]
				new.blue = r[3]
				new.alpha = r[4]
			else assert(false) end
		else
			new.red = r
			new.green = g
			new.blue = b
			new.alpha = a
		end
		return new
	end
}
return color