From 509594aec2c6a8d36de9d6fcc3b67aabfa058155 Mon Sep 17 00:00:00 2001 From: David Briscoe Date: Sat, 26 Mar 2022 23:15:28 -0700 Subject: [PATCH 1/7] color: Use color_mt and normal indexing Fix #34. Set color_mt on new colors so add, subtract, multiply, is_color work. We don't use ffi for color (32cf0e8), so remove cdata check for 0-based offset indexing. Add tests for creating colours and using operators. --- modules/color.lua | 21 ++------------------- spec/color_spec.lua | 32 +++++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/modules/color.lua b/modules/color.lua index 414a451..39cc592 100644 --- a/modules/color.lua +++ b/modules/color.lua @@ -10,7 +10,7 @@ local color_mt = {} local function new(r, g, b, a) local c = { r, g, b, a } c._c = c - return setmetatable(c, color) + return setmetatable(c, color_mt) end -- HSV utilities (adapted from http://www.cs.rit.edu/~ncs/color/t_convert.html) @@ -341,24 +341,7 @@ function color.to_string(a) return string.format("[ %3.0f, %3.0f, %3.0f, %3.0f ]", a[1], a[2], a[3], a[4]) end -function color_mt.__index(t, k) - if type(t) == "cdata" then - if type(k) == "number" then - return t._c[k-1] - end - end - - return rawget(color, k) -end - -function color_mt.__newindex(t, k, v) - if type(t) == "cdata" then - if type(k) == "number" then - t._c[k-1] = v - end - end -end - +color_mt.__index = color color_mt.__tostring = color.to_string function color_mt.__call(_, r, g, b, a) diff --git a/spec/color_spec.lua b/spec/color_spec.lua index 5297ca9..f964c7b 100644 --- a/spec/color_spec.lua +++ b/spec/color_spec.lua @@ -1,10 +1,37 @@ local color = require "modules.color" +local DBL_EPSILON = require("modules.constants").DBL_EPSILON + +local function assert_is_float_equal(a, b) + if math.abs(a - b) > DBL_EPSILON then + assert.is.equal(a, b) + end +end + describe("color:", function() + it("operators: add, subract, multiply", function() + local c = color(1,1,1,1) + assert.is_true(c:is_color()) + local r = c + c + assert.is_true(r:is_color()) + assert_is_float_equal(r[1], 2) + assert_is_float_equal(r[2], 2) + assert_is_float_equal(r[3], 2) + r = c - c + assert.is_true(r:is_color()) + assert_is_float_equal(r[1], 0) + assert_is_float_equal(r[2], 0) + assert_is_float_equal(r[3], 0) + r = c * 5 + assert.is_true(r:is_color()) + assert_is_float_equal(r[1], 5) + assert_is_float_equal(r[2], 5) + assert_is_float_equal(r[3], 5) + end) + end) --[[ -new(r, g, b, a) from_hsv(h, s, v) from_hsva(h, s, v, a) invert(c) @@ -19,6 +46,5 @@ saturation(color, percent) value(color, percent) gamma_to_linear(r, g, b, a) linear_to_gamma(r, g, b, a) -is_color(a) to_string(a) ---]] \ No newline at end of file +--]] From df92f0c5cd91b9554fddd515fdd645d7a6894314 Mon Sep 17 00:00:00 2001 From: David Briscoe Date: Sat, 26 Mar 2022 23:38:41 -0700 Subject: [PATCH 2/7] [Breaking] color: Make expected range match docs lighten/darken: documented range is 0-255, so we don't need to multiply. Makes more sense to work with colors in the same range that we're incrementing by, but this changes behaviour. saturation/value: we use a percent, so it's 0-1. It's clamped so that's obviously expected. Add tests to validate this new behaviour. --- modules/color.lua | 18 ++++++------ spec/color_spec.lua | 68 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 70 insertions(+), 16 deletions(-) diff --git a/modules/color.lua b/modules/color.lua index 39cc592..e26e361 100644 --- a/modules/color.lua +++ b/modules/color.lua @@ -167,9 +167,9 @@ end -- @treturn color out function color.lighten(c, v) return new( - utils.clamp(c[1] + v * 255, 0, 255), - utils.clamp(c[2] + v * 255, 0, 255), - utils.clamp(c[3] + v * 255, 0, 255), + utils.clamp(c[1] + v, 0, 255), + utils.clamp(c[2] + v, 0, 255), + utils.clamp(c[3] + v, 0, 255), c[4] ) end @@ -184,9 +184,9 @@ end -- @treturn color out function color.darken(c, v) return new( - utils.clamp(c[1] - v * 255, 0, 255), - utils.clamp(c[2] - v * 255, 0, 255), - utils.clamp(c[3] - v * 255, 0, 255), + utils.clamp(c[1] - v, 0, 255), + utils.clamp(c[2] - v, 0, 255), + utils.clamp(c[3] - v, 0, 255), c[4] ) end @@ -215,7 +215,7 @@ function color.alpha(c, v) t[i] = c[i] end - t[4] = v * 255 + t[4] = v return t end @@ -245,7 +245,7 @@ end --- Set a color's saturation (hue, value, alpha unchanged) -- @tparam color to alter --- @tparam hue to set 0-359 +-- @tparam saturation to set 0-1 -- @treturn color out function color.saturation(col, percent) local c = color_to_hsv(col) @@ -255,7 +255,7 @@ end --- Set a color's value (saturation, hue, alpha unchanged) -- @tparam color to alter --- @tparam hue to set 0-359 +-- @tparam value to set 0-1 -- @treturn color out function color.value(col, percent) local c = color_to_hsv(col) diff --git a/spec/color_spec.lua b/spec/color_spec.lua index f964c7b..5f7aa16 100644 --- a/spec/color_spec.lua +++ b/spec/color_spec.lua @@ -7,10 +7,16 @@ local function assert_is_float_equal(a, b) end end +local function assert_is_approx_equal(a, b) + if math.abs(a - b) > 0.001 then + assert.is.equal(a, b) + end +end + describe("color:", function() it("operators: add, subract, multiply", function() - local c = color(1,1,1,1) + local c = color(1, 1, 1, 1) assert.is_true(c:is_color()) local r = c + c assert.is_true(r:is_color()) @@ -29,18 +35,66 @@ describe("color:", function() assert_is_float_equal(r[3], 5) end) + it("rgb -> hsv -> rgb", function() + local c = color(1,1,1,1) + local hsv = c:color_to_hsv_table() + local c1 = color.hsv_to_color_table(hsv) + local c2 = color.from_hsva(hsv[1], hsv[2], hsv[3], hsv[4]) + local c3 = color.from_hsv(hsv[1], hsv[2], hsv[3]) + c3[4] = c[4] + for i=1,4 do + assert_is_float_equal(c[i], c1[i]) + assert_is_float_equal(c[i], c2[i]) + assert_is_float_equal(c[i], c3[i]) + end + assert.is_true(c:is_color()) + assert.is_true(c1:is_color()) + assert.is_true(c2:is_color()) + assert.is_true(c3:is_color()) + end) + + it("hsv -> rgb -> hsv", function() + local hsv1 = { 0, 0.3, 0.8, 0.9 } + for h=0,359, 10 do + hsv1[1] = h + local cc = color.hsv_to_color_table(hsv1) + local hsv2 = cc:color_to_hsv_table() + for i=1,4 do + assert_is_approx_equal(hsv1[i], hsv2[i]) + end + end + end) + + it("lighten a color", function() + local c = color(0, 0, 0, 0) + local r = c:lighten(10) + assert.is.equal(r[1], 10) + r = c:lighten(1000) + assert.is.equal(r[1], 255) + end) + + it("darken a color", function() + local c = color(255, 255, 255, 255) + local r = c:darken(10) + assert.is.equal(r[1], 245) + r = c:darken(1000) + assert.is.equal(r[1], 0) + end) + + it("modify alpha", function() + local c = color(255, 255, 255, 255) + local r = c:alpha(10) + assert.is.equal(r[4], 10) + r = c:opacity(0.5) + assert.is.equal(r[4], 255/2) + end) + end) --[[ -from_hsv(h, s, v) -from_hsva(h, s, v, a) invert(c) -lighten(c, v) lerp(a, b, s) -darken(c, v) multiply(c, v) -alpha(c, v) -opacity(c, v) hue(color, hue) saturation(color, percent) value(color, percent) From 343f320e2f0e9b6c760e3b91478b3f4d24587e1d Mon Sep 17 00:00:00 2001 From: David Briscoe Date: Sun, 27 Mar 2022 00:30:30 -0700 Subject: [PATCH 3/7] [Breaking] color: Convert 0-255 -> 0-1 range Our hsv logic assumes 0-1 (cs.rit.edu says "r,g,b values are from 0 to 1") and it makes more sense to provide __mul for multiplying in 0-1 since the result will stay in that range. Additionally, love switched to 0-1 color since 11.0. Included an as_255() function to unpack the color in 0-255 for some amount of backwards compatibility. Doesn't make sense to provide any kind of color object for it since most of our functions won't work correctly. Add tests for hue(), saturation(), value() that fail (even with /255 removed) for the old code, but pass on the new 0-1 range because the hsv logic outputs colors in 0-1. Test comparisons use reduced precision because my input data has limited precision. --- modules/color.lua | 76 ++++++++++++++++++++++++++++----------------- spec/color_spec.lua | 61 ++++++++++++++++++++++++++++-------- 2 files changed, 96 insertions(+), 41 deletions(-) diff --git a/modules/color.lua b/modules/color.lua index e26e361..04ec3d1 100644 --- a/modules/color.lua +++ b/modules/color.lua @@ -21,7 +21,7 @@ local function hsv_to_color(hsv) local i local f, q, p, t local h, s, v - local a = hsv[4] or 255 + local a = hsv[4] or 1 s = hsv[2] v = hsv[3] @@ -29,10 +29,10 @@ local function hsv_to_color(hsv) return new(v, v, v, a) end - h = hsv[1] / 60 + h = hsv[1] / 60 -- sector 0 to 5 i = math.floor(h) - f = h - i + f = h - i -- factorial part of h p = v * (1-s) q = v * (1-s*f) t = v * (1-s*(1-f)) @@ -52,7 +52,7 @@ local function color_to_hsv(c) local r = c[1] local g = c[2] local b = c[3] - local a = c[4] or 255 + local a = c[4] or 1 local h, s, v local min = math.min(r, g, b) @@ -72,7 +72,7 @@ local function color_to_hsv(c) -- r = g = b = 0 s = 0, v is undefined s = 0 h = -1 - return { h, s, v, 255 } + return { h, s, v, 1 } end if r == max then @@ -94,12 +94,12 @@ end --- The public constructor. -- @param x Can be of three types:
--- number red component 0-255 +-- number red component 0-1 -- table {r, g, b, a} -- nil for {0,0,0,0} --- @tparam number g Green component 0-255 --- @tparam number b Blue component 0-255 --- @tparam number a Alpha component 0-255 +-- @tparam number g Green component 0-1 +-- @tparam number b Blue component 0-1 +-- @tparam number a Alpha component 0-1 -- @treturn color out function color.new(r, g, b, a) -- number, number, number, number @@ -126,13 +126,13 @@ function color.new(r, g, b, a) end --- Convert hue,saturation,value table to color object. --- @tparam table hsva {hue 0-359, saturation 0-1, value 0-1, alpha 0-255} +-- @tparam table hsva {hue 0-359, saturation 0-1, value 0-1, alpha 0-1} -- @treturn color out color.hsv_to_color_table = hsv_to_color --- Convert color to hue,saturation,value table -- @tparam color in --- @treturn table hsva {hue 0-359, saturation 0-1, value 0-1, alpha 0-255} +-- @treturn table hsva {hue 0-359, saturation 0-1, value 0-1, alpha 0-1} color.color_to_hsv_table = color_to_hsv --- Convert hue,saturation,value to color object. @@ -148,7 +148,7 @@ end -- @tparam number h hue 0-359 -- @tparam number s saturation 0-1 -- @tparam number v value 0-1 --- @tparam number a alpha 0-255 +-- @tparam number a alpha 0-1 -- @treturn color out function color.from_hsva(h, s, v, a) return hsv_to_color { h, s, v, a } @@ -158,18 +158,18 @@ end -- @tparam color to invert -- @treturn color out function color.invert(c) - return new(255 - c[1], 255 - c[2], 255 - c[3], c[4]) + return new(1 - c[1], 1 - c[2], 1 - c[3], c[4]) end --- Lighten a color by a component-wise fixed amount (alpha unchanged) -- @tparam color to lighten --- @tparam number amount to increase each component by, 0-255 scale +-- @tparam number amount to increase each component by, 0-1 scale -- @treturn color out function color.lighten(c, v) return new( - utils.clamp(c[1] + v, 0, 255), - utils.clamp(c[2] + v, 0, 255), - utils.clamp(c[3] + v, 0, 255), + utils.clamp(c[1] + v, 0, 1), + utils.clamp(c[2] + v, 0, 1), + utils.clamp(c[3] + v, 0, 1), c[4] ) end @@ -178,15 +178,35 @@ function color.lerp(a, b, s) return a + s * (b - a) end +--- Unpack a color into individual components in 0-1. +-- @tparam color to unpack +-- @treturn number r in 0-1 +-- @treturn number g in 0-1 +-- @treturn number b in 0-1 +-- @treturn number a in 0-1 +function color.unpack(c) + return c[1], c[2], c[3], c[4] +end + +--- Unpack a color into individual components in 0-255. +-- @tparam color to unpack +-- @treturn number r in 0-255 +-- @treturn number g in 0-255 +-- @treturn number b in 0-255 +-- @treturn number a in 0-255 +function color.as_255(c) + return c[1] * 255, c[2] * 255, c[3] * 255, c[4] * 255 +end + --- Darken a color by a component-wise fixed amount (alpha unchanged) -- @tparam color to darken --- @tparam number amount to decrease each component by, 0-255 scale +-- @tparam number amount to decrease each component by, 0-1 scale -- @treturn color out function color.darken(c, v) return new( - utils.clamp(c[1] - v, 0, 255), - utils.clamp(c[2] - v, 0, 255), - utils.clamp(c[3] - v, 0, 255), + utils.clamp(c[1] - v, 0, 1), + utils.clamp(c[2] - v, 0, 1), + utils.clamp(c[3] - v, 0, 1), c[4] ) end @@ -207,7 +227,7 @@ end -- directly set alpha channel -- @tparam color to alter --- @tparam number new alpha 0-255 +-- @tparam number new alpha 0-1 -- @treturn color out function color.alpha(c, v) local t = color.new() @@ -280,13 +300,13 @@ function color.gamma_to_linear(r, g, b, a) if type(r) == "table" then local c = {} for i = 1, 3 do - c[i] = convert(r[i] / 255) * 255 + c[i] = convert(r[i]) end - c[4] = convert(r[4] / 255) * 255 + c[4] = convert(r[4]) return c else - return convert(r / 255) * 255, convert(g / 255) * 255, convert(b / 255) * 255, a or 255 + return convert(r), convert(g), convert(b), a or 1 end end @@ -307,13 +327,13 @@ function color.linear_to_gamma(r, g, b, a) if type(r) == "table" then local c = {} for i = 1, 3 do - c[i] = convert(r[i] / 255) * 255 + c[i] = convert(r[i]) end - c[4] = convert(r[4] / 255) * 255 + c[4] = convert(r[4]) return c else - return convert(r / 255) * 255, convert(g / 255) * 255, convert(b / 255) * 255, a or 255 + return convert(r), convert(g), convert(b), a or 1 end end diff --git a/spec/color_spec.lua b/spec/color_spec.lua index 5f7aa16..5309ced 100644 --- a/spec/color_spec.lua +++ b/spec/color_spec.lua @@ -65,28 +65,66 @@ describe("color:", function() end end) + it("unpack", function() + local c = color(122/255, 20/255, 122/255, 255/255) + local r, g, b, a = c:unpack() + assert_is_float_equal(c[1], r) + assert_is_float_equal(c[2], g) + assert_is_float_equal(c[3], b) + assert_is_float_equal(c[4], a) + r, g, b, a = c:as_255() + assert_is_float_equal(122, r) + assert_is_float_equal(20, g) + assert_is_float_equal(122, b) + assert_is_float_equal(255, a) + end) + + it("set hsv", function() + -- hsv value conversion values from http://colorizer.org/ + local c = color(122/255, 20/255, 122/255, 1) + local hsv = c:color_to_hsv_table() + assert_is_approx_equal(hsv[1], 300) + assert_is_approx_equal(hsv[2], 0.8361) + assert_is_approx_equal(hsv[3], 0.4784) + local r = c:hue(200) + assert_is_approx_equal(r[1], 20/255) + assert_is_approx_equal(r[2], 88/255) + assert_is_approx_equal(r[3], 122/255) + r = c:saturation(0.2) + assert_is_approx_equal(r[1], 122/255) + assert_is_approx_equal(r[2], 97.6/255) + assert_is_approx_equal(r[3], 122/255) + r = c:value(0.2) + assert_is_approx_equal(r[1], 51/255) + assert_is_approx_equal(r[2], 8.36/255) + assert_is_approx_equal(r[3], 51/255) + end) + it("lighten a color", function() local c = color(0, 0, 0, 0) - local r = c:lighten(10) - assert.is.equal(r[1], 10) + local r = c:lighten(0.1) + assert.is.equal(r[1], 0.1) r = c:lighten(1000) - assert.is.equal(r[1], 255) + assert.is.equal(r[1], 1) end) it("darken a color", function() - local c = color(255, 255, 255, 255) - local r = c:darken(10) - assert.is.equal(r[1], 245) + local c = color(1, 1, 1, 1) + local r = c:darken(0.04) + assert.is.equal(r[1], 0.96) r = c:darken(1000) assert.is.equal(r[1], 0) end) it("modify alpha", function() - local c = color(255, 255, 255, 255) - local r = c:alpha(10) - assert.is.equal(r[4], 10) + local c = color(1, 1, 1, 1) + local r = c:alpha(0.1) + assert.is.equal(r[4], 0.1) r = c:opacity(0.5) - assert.is.equal(r[4], 255/2) + assert.is.equal(r[4], 0.5) + r = c:opacity(0.5) + :opacity(0.5) + assert.is.equal(r[4], 0.25) end) end) @@ -95,9 +133,6 @@ end) invert(c) lerp(a, b, s) multiply(c, v) -hue(color, hue) -saturation(color, percent) -value(color, percent) gamma_to_linear(r, g, b, a) linear_to_gamma(r, g, b, a) to_string(a) From 41b1f0b5b9e873cf2bc11e5801ed2a481dcd146e Mon Sep 17 00:00:00 2001 From: David Briscoe Date: Sun, 27 Mar 2022 00:53:37 -0700 Subject: [PATCH 4/7] color: Add more tests --- modules/color.lua | 5 +++++ spec/color_spec.lua | 45 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/modules/color.lua b/modules/color.lua index 04ec3d1..148f5b3 100644 --- a/modules/color.lua +++ b/modules/color.lua @@ -174,6 +174,11 @@ function color.lighten(c, v) ) end +--- Interpolate between two colors. +-- @tparam color at start +-- @tparam color at end +-- @tparam number s in 0-1 progress between the two colors +-- @treturn color out function color.lerp(a, b, s) return a + s * (b - a) end diff --git a/spec/color_spec.lua b/spec/color_spec.lua index 5309ced..01f2769 100644 --- a/spec/color_spec.lua +++ b/spec/color_spec.lua @@ -116,6 +116,18 @@ describe("color:", function() assert.is.equal(r[1], 0) end) + it("multiply a color by a scalar", function() + local c = color(1, 1, 1, 1) + local r = c:multiply(0.04) + assert.is.equal(r[1], 0.04) + + r = c:multiply(0) + for i=1,3 do + assert.is.equal(0, r[i]) + end + assert.is.equal(1, r[4]) + end) + it("modify alpha", function() local c = color(1, 1, 1, 1) local r = c:alpha(0.1) @@ -127,12 +139,39 @@ describe("color:", function() assert.is.equal(r[4], 0.25) end) + it("invert", function() + local c = color(1, 0.6, 0.25, 1) + local r = c:invert() + assert_is_float_equal(r[1], 0) + assert_is_float_equal(r[2], 0.4) + assert_is_float_equal(r[3], 0.75) + assert_is_float_equal(r[4], 1) + r = c:invert() + :invert() + for i=1,4 do + assert.is.equal(c[i], r[i]) + end + end) + + it("lerp", function() + local a = color(1, 0.6, 0.25, 1) + local b = color(1, 0.8, 0.75, 0.5) + local r = a:lerp(b, 0.5) + assert_is_float_equal(r[1], 1) + assert_is_float_equal(r[2], 0.7) + assert_is_float_equal(r[3], 0.5) + assert_is_float_equal(r[4], 0.75) + local r_a = a:lerp(b, 0) + local r_b = a:lerp(b, 1) + for i=1,4 do + assert.is.equal(a[i], r_a[i]) + assert.is.equal(b[i], r_b[i]) + end + end) + end) --[[ -invert(c) -lerp(a, b, s) -multiply(c, v) gamma_to_linear(r, g, b, a) linear_to_gamma(r, g, b, a) to_string(a) From aec3b9fd7f7decec49e53f28da1e2cef9ea08478 Mon Sep 17 00:00:00 2001 From: David Briscoe Date: Fri, 1 Apr 2022 21:27:56 -0700 Subject: [PATCH 5/7] Prevent nan when using color_to_hsv(white) When r,g,b are equal, delta will be zero and hue will be nan due to divide by zero. This is also an issue with the reference C++ code: https://tio.run/##Sy4o0E3OScxL//9fOTMvOac0JVXBJjO/uKQoNTHXjiszr0QhNzEzT0NToZqLs7gkxcoqOb@0RMHGRkHJIzUnJ19HITy/KCdFUQkkBJZPzUvJseaq/f8fAA An alternative approach is to always add epsilon or use a different hue calculation: http://web.archive.org/web/20210515082154/http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv --- modules/color.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/color.lua b/modules/color.lua index 148f5b3..e410e5f 100644 --- a/modules/color.lua +++ b/modules/color.lua @@ -2,6 +2,7 @@ -- @module color local modules = (...):gsub('%.[^%.]+$', '') .. "." +local constants = require(modules .. "constants") local utils = require(modules .. "utils") local precond = require(modules .. "_private_precond") local color = {} @@ -75,6 +76,11 @@ local function color_to_hsv(c) return { h, s, v, 1 } end + -- Prevent division by zero. + if delta == 0 then + delta = constants.DBL_EPSILON + end + if r == max then h = ( g - b ) / delta -- yellow/magenta elseif g == max then From 0c12f17805caa6ede67fc54d94dda5eecf5688ca Mon Sep 17 00:00:00 2001 From: David Briscoe Date: Sun, 27 Mar 2022 01:06:41 -0700 Subject: [PATCH 6/7] color: Linear alpha in linear<->gamma conversions Maintain linear alpha when converting to gamma. Update to latest wikipedia links. Add simple test that does the round trip conversion. --- modules/color.lua | 8 ++++---- spec/color_spec.lua | 10 ++++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/modules/color.lua b/modules/color.lua index e410e5f..ac3852a 100644 --- a/modules/color.lua +++ b/modules/color.lua @@ -294,7 +294,7 @@ function color.value(col, percent) return hsv_to_color(c) end --- http://en.wikipedia.org/wiki/SRGB#The_reverse_transformation +-- https://en.wikipedia.org/wiki/SRGB#From_sRGB_to_CIE_XYZ function color.gamma_to_linear(r, g, b, a) local function convert(c) if c > 1.0 then @@ -314,14 +314,14 @@ function color.gamma_to_linear(r, g, b, a) c[i] = convert(r[i]) end - c[4] = convert(r[4]) + c[4] = r[4] return c else return convert(r), convert(g), convert(b), a or 1 end end --- http://en.wikipedia.org/wiki/SRGB#The_forward_transformation_.28CIE_xyY_or_CIE_XYZ_to_sRGB.29 +-- https://en.wikipedia.org/wiki/SRGB#From_CIE_XYZ_to_sRGB function color.linear_to_gamma(r, g, b, a) local function convert(c) if c > 1.0 then @@ -341,7 +341,7 @@ function color.linear_to_gamma(r, g, b, a) c[i] = convert(r[i]) end - c[4] = convert(r[4]) + c[4] = r[4] return c else return convert(r), convert(g), convert(b), a or 1 diff --git a/spec/color_spec.lua b/spec/color_spec.lua index 01f2769..20d9fde 100644 --- a/spec/color_spec.lua +++ b/spec/color_spec.lua @@ -169,10 +169,16 @@ describe("color:", function() end end) + it("linear_to_gamma -> gamma_to_linear round trip", function() + local c = color(0.25, 0.25, 0.25, 1) + local r = color.gamma_to_linear(c:linear_to_gamma()) + for i=1,4 do + assert_is_approx_equal(c[i], r[i]) + end + end) + end) --[[ -gamma_to_linear(r, g, b, a) -linear_to_gamma(r, g, b, a) to_string(a) --]] From 7d99a08134273506eaaf7c960df6c57bbfc24c51 Mon Sep 17 00:00:00 2001 From: David Briscoe Date: Fri, 1 Apr 2022 20:49:18 -0700 Subject: [PATCH 7/7] [Breaking] color: Convert hue [0,359] -> [0,1] imgui [uses 0-1 for everything internally]( https://github.com/ocornut/imgui/blob/master/imgui.cpp#L2045). [Both Unity](https://docs.unity3d.com/ScriptReference/Color.RGBToHSV.html) [and Godot](https://docs.godotengine.org/en/stable/classes/class_color.html#class-color-property-h) use H, S, and V in the range 0.0 to 1.0 Since we're switching colours out of 0,255 we should also switch hue out of 0,359. Now all of our r,g,b,a,h,s,v values are all in 0,1. --- modules/color.lua | 18 +++++++++--------- spec/color_spec.lua | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/modules/color.lua b/modules/color.lua index ac3852a..e78e9e7 100644 --- a/modules/color.lua +++ b/modules/color.lua @@ -30,7 +30,7 @@ local function hsv_to_color(hsv) return new(v, v, v, a) end - h = hsv[1] / 60 -- sector 0 to 5 + h = hsv[1] * 6 -- sector 0 to 5 i = math.floor(h) f = h - i -- factorial part of h @@ -89,10 +89,10 @@ local function color_to_hsv(c) h = 4 + ( r - g ) / delta -- magenta/cyan end - h = h * 60 -- degrees + h = h / 6 -- normalize from segment 0..5 if h < 0 then - h = h + 360 + h = h + 1 end return { h, s, v, a } @@ -132,17 +132,17 @@ function color.new(r, g, b, a) end --- Convert hue,saturation,value table to color object. --- @tparam table hsva {hue 0-359, saturation 0-1, value 0-1, alpha 0-1} +-- @tparam table hsva {hue 0-1, saturation 0-1, value 0-1, alpha 0-1} -- @treturn color out color.hsv_to_color_table = hsv_to_color --- Convert color to hue,saturation,value table -- @tparam color in --- @treturn table hsva {hue 0-359, saturation 0-1, value 0-1, alpha 0-1} +-- @treturn table hsva {hue 0-1, saturation 0-1, value 0-1, alpha 0-1} color.color_to_hsv_table = color_to_hsv --- Convert hue,saturation,value to color object. --- @tparam number h hue 0-359 +-- @tparam number h hue 0-1 -- @tparam number s saturation 0-1 -- @tparam number v value 0-1 -- @treturn color out @@ -151,7 +151,7 @@ function color.from_hsv(h, s, v) end --- Convert hue,saturation,value to color object. --- @tparam number h hue 0-359 +-- @tparam number h hue 0-1 -- @tparam number s saturation 0-1 -- @tparam number v value 0-1 -- @tparam number a alpha 0-1 @@ -266,11 +266,11 @@ end --- Set a color's hue (saturation, value, alpha unchanged) -- @tparam color to alter --- @tparam hue to set 0-359 +-- @tparam hue to set 0-1 -- @treturn color out function color.hue(col, hue) local c = color_to_hsv(col) - c[1] = (hue + 360) % 360 + c[1] = (hue + 1) % 1 return hsv_to_color(c) end diff --git a/spec/color_spec.lua b/spec/color_spec.lua index 20d9fde..7bbf21f 100644 --- a/spec/color_spec.lua +++ b/spec/color_spec.lua @@ -55,7 +55,7 @@ describe("color:", function() it("hsv -> rgb -> hsv", function() local hsv1 = { 0, 0.3, 0.8, 0.9 } - for h=0,359, 10 do + for h=0,1, 0.1 do hsv1[1] = h local cc = color.hsv_to_color_table(hsv1) local hsv2 = cc:color_to_hsv_table() @@ -83,10 +83,10 @@ describe("color:", function() -- hsv value conversion values from http://colorizer.org/ local c = color(122/255, 20/255, 122/255, 1) local hsv = c:color_to_hsv_table() - assert_is_approx_equal(hsv[1], 300) + assert_is_approx_equal(hsv[1], 300/360) assert_is_approx_equal(hsv[2], 0.8361) assert_is_approx_equal(hsv[3], 0.4784) - local r = c:hue(200) + local r = c:hue(200/360) assert_is_approx_equal(r[1], 20/255) assert_is_approx_equal(r[2], 88/255) assert_is_approx_equal(r[3], 122/255)