summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--class.lua38
-rw-r--r--hsluv.lua347
-rw-r--r--main_old.lua86
3 files changed, 471 insertions, 0 deletions
diff --git a/class.lua b/class.lua
new file mode 100644
index 0000000..c9fb87b
--- /dev/null
+++ b/class.lua
@@ -0,0 +1,38 @@
+-- minimal class library
+--
+-- local Foo = class()
+-- function Foo.make(cls, x,y,z)
+-- return setmetatable({x=x,y=y,z=z},cls)
+-- end
+-- function Foo.whatever(self, ...)
+-- ...
+-- end
+-- ...
+-- local foo = Foo:make(...)
+-- foo:whatever(...)
+
+
+local function class()
+ local T = {}
+ T.__index = T
+ return T
+end
+
+local function extend(Base)
+ local T = {}
+ T.__index = T
+ for k,v in pairs(Base) do
+ if k:sub(1,2) == "__" and k~="__index" then
+ T[k]=v
+ end
+ end
+ setmetatable(T,{__index=Base})
+ return T
+end
+
+return setmetatable({
+ class=class,
+ extend=extend
+},{__call=class})
+
+
diff --git a/hsluv.lua b/hsluv.lua
new file mode 100644
index 0000000..1ff199a
--- /dev/null
+++ b/hsluv.lua
@@ -0,0 +1,347 @@
+--[[
+Lua implementation of HSLuv and HPLuv color spaces
+Homepage: http://www.hsluv.org/
+
+Copyright (C) 2019 Alexei Boronine
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+associated documentation files (the "Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
+following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial
+portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+]]
+
+hsluv = {}
+
+hexChars = "0123456789abcdef"
+
+
+distance_line_from_origin = function(line)
+ return math.abs(line.intercept) / math.sqrt((line.slope ^ 2) + 1)
+end
+
+length_of_ray_until_intersect = function(theta, line)
+ return line.intercept / (math.sin(theta) - line.slope * math.cos(theta))
+end
+
+hsluv.get_bounds = function(l)
+ local result = {};
+ local sub2;
+ local sub1 = ((l + 16) ^ 3) / 1560896;
+ if sub1 > hsluv.epsilon then
+ sub2 = sub1;
+ else
+ sub2 = l / hsluv.kappa;
+ end
+
+ for i = 1, 3 do
+ local m1 = hsluv.m[i][1];
+ local m2 = hsluv.m[i][2];
+ local m3 = hsluv.m[i][3];
+
+ for t = 0, 1 do
+ local top1 = (284517 * m1 - 94839 * m3) * sub2;
+ local top2 = (838422 * m3 + 769860 * m2 + 731718 * m1) * l * sub2 - 769860 * t * l;
+ local bottom = (632260 * m3 - 126452 * m2) * sub2 + 126452 * t;
+ table.insert(result, {
+ slope = top1 / bottom,
+ intercept = top2 / bottom
+ })
+ end;
+ end;
+ return result
+end
+
+hsluv.max_safe_chroma_for_l = function(l)
+ local bounds = hsluv.get_bounds(l);
+ local min = 1.7976931348623157e+308;
+
+ for i = 1, 6 do
+ local length = distance_line_from_origin(bounds[i]);
+ if length >= 0 then
+ min = math.min(min, length);
+ end;
+ end;
+ return min;
+end
+
+hsluv.max_safe_chroma_for_lh = function(l, h)
+ local hrad = h / 360 * math.pi * 2;
+ local bounds = hsluv.get_bounds(l);
+ local min = 1.7976931348623157e+308;
+
+ for i = 1, 6 do
+ local bound = bounds[i];
+ local length = length_of_ray_until_intersect(hrad, bound);
+ if length >= 0 then
+ min = math.min(min, length);
+ end;
+ end;
+ return min
+end
+
+hsluv.dot_product = function(a, b)
+ local sum = 0;
+ for i = 1, 3 do
+ sum = sum + a[i] * b[i];
+ end;
+ return sum
+end
+
+hsluv.from_linear = function(c)
+ if c <= 0.0031308 then
+ return 12.92 * c
+ else
+ return 1.055 * (c ^ 0.416666666666666685) - 0.055
+ end;
+end
+
+hsluv.to_linear = function(c)
+ if c > 0.04045 then
+ return ((c + 0.055) / 1.055) ^ 2.4
+ else
+ return c / 12.92
+ end;
+end
+
+hsluv.xyz_to_rgb = function(tuple)
+ return {
+ hsluv.from_linear(hsluv.dot_product(hsluv.m[1], tuple)),
+ hsluv.from_linear(hsluv.dot_product(hsluv.m[2], tuple)),
+ hsluv.from_linear(hsluv.dot_product(hsluv.m[3], tuple))
+ }
+end
+
+hsluv.rgb_to_xyz = function(tuple)
+ local rgbl = {
+ hsluv.to_linear(tuple[1]),
+ hsluv.to_linear(tuple[2]),
+ hsluv.to_linear(tuple[3])
+ };
+ return {
+ hsluv.dot_product(hsluv.minv[1], rgbl),
+ hsluv.dot_product(hsluv.minv[2], rgbl),
+ hsluv.dot_product(hsluv.minv[3], rgbl)
+ }
+end
+
+hsluv.y_to_l = function(Y)
+ if Y <= hsluv.epsilon then
+ return Y / hsluv.refY * hsluv.kappa
+ else
+ return 116 * ((Y / hsluv.refY) ^ 0.333333333333333315) - 16
+ end;
+end
+
+hsluv.l_to_y = function(L)
+ if L <= 8 then
+ return hsluv.refY * L / hsluv.kappa
+ else
+ return hsluv.refY * (((L + 16) / 116) ^ 3)
+ end;
+end
+
+hsluv.xyz_to_luv = function(tuple)
+ local X = tuple[1];
+ local Y = tuple[2];
+ local divider = X + 15 * Y + 3 * tuple[3];
+ local varU = 4 * X;
+ local varV = 9 * Y;
+ if divider ~= 0 then
+ varU = varU / divider;
+ varV = varV / divider;
+ else
+ varU = 0;
+ varV = 0;
+ end
+ local L = hsluv.y_to_l(Y);
+ if L == 0 then
+ return { 0, 0, 0 }
+ end;
+ return { L, 13 * L * (varU - hsluv.refU), 13 * L * (varV - hsluv.refV) }
+end
+
+hsluv.luv_to_xyz = function(tuple)
+ local L = tuple[1];
+ local U = tuple[2];
+ local V = tuple[3];
+ if L == 0 then
+ return { 0, 0, 0 }
+ end;
+ local varU = U / (13 * L) + hsluv.refU;
+ local varV = V / (13 * L) + hsluv.refV;
+ local Y = hsluv.l_to_y(L);
+ local X = 0 - (9 * Y * varU) / ((((varU - 4) * varV) - varU * varV));
+ return { X, Y, (9 * Y - 15 * varV * Y - varV * X) / (3 * varV) }
+end
+
+hsluv.luv_to_lch = function(tuple)
+ local L = tuple[1];
+ local U = tuple[2];
+ local V = tuple[3];
+ local C = math.sqrt(U * U + V * V);
+ local H
+ if C < 0.00000001 then
+ H = 0;
+ else
+ H = math.atan2(V, U) * 180.0 / 3.1415926535897932;
+ if H < 0 then
+ H = 360 + H;
+ end;
+ end;
+ return { L, C, H }
+end
+
+hsluv.lch_to_luv = function(tuple)
+ local L = tuple[1];
+ local C = tuple[2];
+ local Hrad = tuple[3] / 360.0 * 2 * math.pi;
+ return { L, math.cos(Hrad) * C, math.sin(Hrad) * C };
+end
+
+hsluv.hsluv_to_lch = function(tuple)
+ local H = tuple[1];
+ local S = tuple[2];
+ local L = tuple[3];
+ if L > 99.9999999 then
+ return { 100, 0, H }
+ end;
+ if L < 0.00000001 then
+ return { 0, 0, H }
+ end;
+ return { L, hsluv.max_safe_chroma_for_lh(L, H) / 100 * S, H }
+end
+
+hsluv.lch_to_hsluv = function(tuple)
+ local L = tuple[1];
+ local C = tuple[2];
+ local H = tuple[3];
+ local max_chroma = hsluv.max_safe_chroma_for_lh(L, H)
+ if L > 99.9999999 then
+ return { H, 0, 100 }
+ end;
+ if L < 0.00000001 then
+ return { H, 0, 0 }
+ end;
+
+ return { H, C / max_chroma * 100, L }
+end
+
+hsluv.hpluv_to_lch = function(tuple)
+ local H = tuple[1];
+ local S = tuple[2];
+ local L = tuple[3];
+ if L > 99.9999999 then
+ return { 100, 0, H }
+ end;
+ if L < 0.00000001 then
+ return { 0, 0, H }
+ end;
+ return { L, hsluv.max_safe_chroma_for_l(L) / 100 * S, H }
+end
+
+hsluv.lch_to_hpluv = function(tuple)
+ local L = tuple[1];
+ local C = tuple[2];
+ local H = tuple[3];
+ if L > 99.9999999 then
+ return { H, 0, 100 }
+ end;
+ if L < 0.00000001 then
+ return { H, 0, 0 }
+ end;
+ return { H, C / hsluv.max_safe_chroma_for_l(L) * 100, L }
+end
+
+hsluv.rgb_to_hex = function(tuple)
+ local h = "#";
+ for i = 1, 3 do
+ local c = math.floor(tuple[i] * 255 + 0.5);
+ local digit2 = math.fmod(c, 16);
+ local x = (c - digit2) / 16;
+ local digit1 = math.floor(x);
+ h = h .. string.sub(hexChars, digit1 + 1, digit1 + 1)
+ h = h .. string.sub(hexChars, digit2 + 1, digit2 + 1)
+ end;
+ return h
+end
+
+hsluv.hex_to_rgb = function(hex)
+ hex = string.lower(hex)
+ local ret = {}
+ for i = 0, 2 do
+ local char1 = string.sub(hex, i * 2 + 2, i * 2 + 2);
+ local char2 = string.sub(hex, i * 2 + 3, i * 2 + 3);
+ local digit1 = string.find(hexChars, char1) - 1
+ local digit2 = string.find(hexChars, char2) - 1
+ ret[i + 1] = (digit1 * 16 + digit2) / 255.0;
+ end;
+ return ret
+end
+
+hsluv.lch_to_rgb = function(tuple)
+ return hsluv.xyz_to_rgb(hsluv.luv_to_xyz(hsluv.lch_to_luv(tuple)))
+end
+
+hsluv.rgb_to_lch = function(tuple)
+ return hsluv.luv_to_lch(hsluv.xyz_to_luv(hsluv.rgb_to_xyz(tuple)))
+end
+
+hsluv.hsluv_to_rgb = function(tuple)
+ return hsluv.lch_to_rgb(hsluv.hsluv_to_lch(tuple))
+end
+
+hsluv.rgb_to_hsluv = function(tuple)
+ return hsluv.lch_to_hsluv(hsluv.rgb_to_lch(tuple))
+end
+
+hsluv.hpluv_to_rgb = function(tuple)
+ return hsluv.lch_to_rgb(hsluv.hpluv_to_lch(tuple))
+end
+
+hsluv.rgb_to_hpluv = function(tuple)
+ return hsluv.lch_to_hpluv(hsluv.rgb_to_lch(tuple))
+end
+
+hsluv.hsluv_to_hex = function(tuple)
+ return hsluv.rgb_to_hex(hsluv.hsluv_to_rgb(tuple))
+end
+
+hsluv.hpluv_to_hex = function(tuple)
+ return hsluv.rgb_to_hex(hsluv.hpluv_to_rgb(tuple))
+end
+
+hsluv.hex_to_hsluv = function(s)
+ return hsluv.rgb_to_hsluv(hsluv.hex_to_rgb(s))
+end
+
+hsluv.hex_to_hpluv = function(s)
+ return hsluv.rgb_to_hpluv(hsluv.hex_to_rgb(s))
+end
+
+hsluv.m = {
+ { 3.240969941904521, -1.537383177570093, -0.498610760293 },
+ { -0.96924363628087, 1.87596750150772, 0.041555057407175 },
+ { 0.055630079696993, -0.20397695888897, 1.056971514242878 }
+}
+hsluv.minv = {
+ { 0.41239079926595, 0.35758433938387, 0.18048078840183 },
+ { 0.21263900587151, 0.71516867876775, 0.072192315360733 },
+ { 0.019330818715591, 0.11919477979462, 0.95053215224966 }
+}
+hsluv.refY = 1.0
+hsluv.refU = 0.19783000664283
+hsluv.refV = 0.46831999493879
+hsluv.kappa = 903.2962962
+hsluv.epsilon = 0.0088564516
+
+return hsluv
diff --git a/main_old.lua b/main_old.lua
new file mode 100644
index 0000000..160a0d7
--- /dev/null
+++ b/main_old.lua
@@ -0,0 +1,86 @@
+local class = require 'class'
+local hsluv = require 'hsluv'
+
+local tau = 2*math.pi
+local ZOOM = 200
+local G = love.graphics
+
+local function write_at(text,x,y)
+ G.push()
+ G.translate(x,y)
+ G.scale(1/ZOOM)
+ G.print(text,0,0)
+ G.pop()
+end
+
+local Port = class()
+Port.R = 0.1
+function Port.make(cls, x,y, num)
+ return setmetatable({x=x,y=y,n=num,wires={}},cls)
+end
+function Port.draw(self, istate)
+ local c = {0,0,0}
+ if istate == 'hover' then
+ c[1] = 1
+ end
+ love.graphics.setColor(c)
+ G.circle('line',self.x,self.y,self.R)
+ write_at(self.n, self.x, self.y)
+end
+function Port.contains(self, px,py)
+ local d = math.sqrt((self.x - px)^2 + (self.y - py)^2)
+ return d <= self.R
+end
+
+local ports = {}
+local n = 10
+for i = 1,n do
+ local theta = tau * i/n
+ table.insert(ports, Port:make(math.cos(theta), math.sin(theta), i))
+end
+ports[3].wires = {ports[6]}
+ports[5].wires = {ports[7],ports[2]}
+for i = 1,15 do
+ local r1,r2 = math.random(10), math.random(10)
+ if r1 ~= r2 then
+ table.insert(ports[r1].wires, ports[r2])
+ end
+end
+
+
+local function catenary(x1,y1,x2,y2)
+ -- https://math.stackexchange.com/questions/3557767/how-to-construct-a-catenary-of-a-specified-length-through-two-specified-points
+
+end
+
+local function wire_color(n)
+ local phi = (1+math.sqrt(5))/2
+ local h = (360*phi*n)%360
+ return hsluv.hsluv_to_rgb({h, 80, 60})
+end
+
+local state = 'normal'
+
+
+function love.draw()
+ local W,H = G.getDimensions()
+ G.clear(1,1,1)
+ G.setColor(0,0,0)
+ G.origin()
+ G.setLineWidth(0.01)
+ G.translate(W/2,H/2)
+ G.scale(ZOOM)
+
+ local mx,my = G.inverseTransformPoint(love.mouse.getPosition())
+ local wn = 0
+ for i,p in ipairs(ports) do
+ local istate = p:contains(mx,my) and 'hover' or 'normal'
+ p:draw(istate)
+ for _,q in ipairs(p.wires) do
+ G.setColor(wire_color(wn))
+ wn = wn + 1
+ catenary(p.x,p.y,q.x,q.y)
+ end
+ end
+end
+