sorcery  Diff

Differences From Artifact [16b55419da]:

To Artifact [e07c3f3979]:


    43     43   	end;
    44     44   	
    45     45   	cast = {
    46     46   		number = function(n) return {
    47     47   			red = n; green = n; blue = n;
    48     48   		} end;
    49     49   		table = function(t) return {
    50         -			red = t[1]; green = t[2]; blue = t[3];
           50  +			red = t[1]; green = t[2]; blue = t[3]; alpha = t[4];
    51     51   		} end;
    52     52   	};
    53     53   
    54     54   	construct = function(r,g,b,a)
    55     55   		local clip = function(v)
    56     56   			return math.max(0,math.min(255,v))
           57  +		end;
           58  +		local from_hsl = function(hsl, alpha)
           59  +			-- Based on the algorithm in Computer Graphics: Principles and Practice, by
           60  +			-- James D. Foley et. al., 2nd ed., p. 596
           61  +			-- Degree version, though radian is more natural, I don't want to translate it yet
           62  +			local h = hsl.hue
           63  +			local s = hsl.saturation
           64  +			local l = hsl.luminosity
           65  +			local value = function(n1, n2, hue)
           66  +				if hue > 360 then
           67  +					hue = hue - 360
           68  +				elseif hue < 0 then
           69  +					hue = hue + 360
           70  +				end
           71  +				if hue < 60 then
           72  +					return n1 + (n2 - n1) * hue/60
           73  +				elseif hue < 180 then
           74  +					return n2
           75  +				elseif hue < 240 then
           76  +					return n1 + (n2 - n1) * (240 - hue)/60
           77  +				else
           78  +					return n1
           79  +				end
           80  +			end
           81  +			local m2
           82  +			if l < 0.5 then
           83  +				m2 = l * (1 + s)
           84  +			else
           85  +				m2 = l + s - l * s
           86  +			end
           87  +			local m1 = 2 * l - m2
           88  +			if s == 0 then
           89  +				-- Achromatic, there is no hue
           90  +				-- In book this errors if hue is not undefined, but we set hue to 0 in this case, not nil or something, so
           91  +				return color(l, l, l, alpha)
           92  +			else
           93  +				-- Chromatic case, so there is a hue
           94  +				return color(
           95  +					clip(value(m1, m2, h + 120)*255),
           96  +					clip(value(m1, m2, h)*255),
           97  +					clip(value(m1, m2, h - 120)*255),
           98  +					alpha
           99  +				)
          100  +			end
    57    101   		end;
    58    102   		local warp = function(f)
    59    103   			return function(self, ...)
    60    104   				local n = color(self)
    61    105   				f(n, ...)
    62    106   				return n
    63    107   			end;
................................................................................
    71    115   				minetest.colorize(self:hex(), text)
    72    116   			end;
    73    117   
    74    118   			luminosity = function(self) return
    75    119   				(self.red + self.green + self.blue) / 3
    76    120   			end;
    77    121   
    78         -			readable = function(self, target)
    79         -				target = target or 200
    80         -				local lum = self:luminosity()
    81         -				if lum < target then
    82         -					local f = 1.0 + (target - lum) / 255
    83         -					local nc = self:brighten(f * 1.5)
    84         -					-- i don't know why the *1.5 is necessary. it's
    85         -					-- an ugly hack to work around broken math,
    86         -					-- because i'm too much of a mathtard to actually
    87         -					-- find what's wrong
    88         -					return nc
          122  +			to_hsl = function(self)
          123  +				-- Based on the algorithm in Computer Graphics: Principles and Practice, by
          124  +				-- James D. Foley et. al., 2nd ed., p. 595
          125  +				-- We need the rgb between 0 and 1
          126  +				local r = self.red/255
          127  +				local g = self.green/255
          128  +				local b = self.blue/255
          129  +				local max = math.max(r, g, b)
          130  +				local min = math.min(r, g, b)
          131  +				local luminosity = (max + min)/2
          132  +				local hue = 0
          133  +				local saturation = 0
          134  +				if max == min then
          135  +					-- Achromatic case, because r=g=b
          136  +					saturation = 0
          137  +					hue = 0 -- Undefined, so just replace w/ 0 for usability
    89    138   				else
    90         -					return self
          139  +					-- Chromatic case
          140  +					if luminosity <= 0.5 then
          141  +						saturation = (max - min)/(max + min)
          142  +					else
          143  +						saturation = (max - min)/(2 - max - min)
          144  +					end
          145  +					-- Next calculate the hue
          146  +					local delta = max - min
          147  +					if r == max then
          148  +						hue = (g - b)/delta
          149  +					elseif g == max then
          150  +						hue = 2 + (b - r)/delta
          151  +					else -- blue must be max, so no point in checking
          152  +						hue = 4 + (r - g)/delta
          153  +					end
          154  +					hue = hue * 60 -- degrees
          155  +					--hue = hue * (math.pi / 3) -- for hue in radians instead of degrees
          156  +					if hue < 0 then
          157  +						hue = hue + 2 * math.pi
          158  +					end
    91    159   				end
          160  +				-- print("r"..self.red.."g"..self.green.."b"..self.blue.." is h"..hue.."s"..saturation.."l"..luminosity)
          161  +				local temp = from_hsl({hue=hue,saturation=saturation,luminosity=luminosity},self.alpha)
          162  +				-- print("back is r"..temp.red.."g"..temp.green.."b"..temp.blue)
          163  +				return { hue = hue, saturation = saturation, luminosity = luminosity }
          164  +			end;
          165  +
          166  +			readable = function(self, target)
          167  +				target = target or 0.5
          168  +				local hsl = self:to_hsl()
          169  +				hsl.luminosity = target
          170  +				return from_hsl(hsl, self.alpha)
    92    171   			end;
    93    172   
    94    173   			bg = function(self, text) return
    95    174   				text .. minetest.get_background_escape_sequence(self:hex())
    96    175   			end;
    97    176   
    98    177   			fade = warp(function(new, fac)
    99    178   				new.alpha = math.min(255, (new.alpha or 255) * fac)
   100    179   			end);
   101    180   
   102         -			brighten = warp(function(new, fac)
   103         -				local lum = new:luminosity()
   104         -				local newlum = lum * fac
   105         -				local delta = (newlum - lum)
   106         -				new.red   = clip(new.red   + delta)
   107         -				new.blue  = clip(new.blue  + delta)
   108         -				new.green = clip(new.green + delta)
   109         -			end);
          181  +			brighten = function(self, fac)
          182  +				-- Use HSL to brighten
          183  +				-- To HSL
          184  +				local hsl = self:to_hsl()
          185  +				-- Do the calculation, clamp to 0-1 instead of the clamp fn
          186  +				hsl.luminosity = math.min(math.max(hsl.luminosity * fac, 0), 1)
          187  +				-- Turn back into RGB color
          188  +				local t = from_hsl(hsl, self.alpha)
          189  +				-- print("brighten is r"..t.red.."g"..t.green.."b"..t.blue)
          190  +				return from_hsl(hsl, self.alpha)
          191  +			end;
   110    192   
   111    193   			darken = warp(function(new, fac)
          194  +				-- TODO: is there any point to this being different than brighten? Probably especially not now.
   112    195   				new.red = clip(new.red - (new.red * fac))
   113    196   				new.blue = clip(new.blue - (new.blue * fac))
   114    197   				new.green = clip(new.green - (new.green * fac))
   115    198   			end);
   116    199   		}
   117    200   		if g == nil then
   118    201   			if type(r) == 'string' then