sorcery  Changes On Branch glowpelt/hsl

Changes In Branch glowpelt/hsl Excluding Merge-Ins

This is equivalent to a diff from 956134c50b to 794d5b023a

2020-08-16
02:05
changes, merges, additions galore check-in: 82178e0a16 user: lexi tags: trunk
2020-08-14
06:17
fix(color): Actually get hsl brightening working Reimplemented based on the algorithms in Computer Graphics: Principles and Practice. Only lightens based on luminosity right now, which makes beautifully saturated, but not pastel colors, so some tweaking might be recommended. Leaf check-in: 794d5b023a user: glowpelt tags: glowpelt/hsl
2020-08-13
05:11
feat(color): Change color lightening to use HSL Change color lightening, including the readable utility, to use HSL. This is because the earlier implementation was broken and hacky, and using HSL is a way to implement this in a much more natural-feeling way (being closer to percieved lightness), especially for the current uses. Add a color:to_hsl() function to make this easier, as well as a from_hsl utility that is only in color, for now, but maybe should be exposed as an alternate constructor? check-in: 0a49ac4849 user: glowpelt tags: glowpelt/hsl
2020-08-11
21:39
initial commit check-in: 956134c50b user: lexi tags: trunk
21:37
initial empty check-in check-in: e463b1bb20 user: lexi tags: trunk

Modified lib/color.lua from [16b55419da] to [ef48edf309].

    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     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
          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;
    64    108   		end;
................................................................................
    70    114   			fmt = function(self, text) return
    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;
          121  +
          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
          138  +                                else
          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
          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;
    77    165   
    78    166   			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
    89         -				else
    90         -					return self
    91         -				end
          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