Differences From
Artifact [16b55419da]:
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