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