1
2
3
4
5
6
7
8
9
..
43
44
45
46
47
48
49
50
51
52
53
54
55
56
...
546
547
548
549
550
551
552
553
554
555
556
557
558
559
...
587
588
589
590
591
592
593
594
595
596
597
598
599
600
...
885
886
887
888
889
890
891
892
893
894
895
896
897
898
...
935
936
937
938
939
940
941
|
-- [ʞ] sirsem.lua
-- ~ lexu hale <lexi@hale.su>
-- ? utility library with functionality common to
-- cortav.lua and its extensions
-- from Ranuir "software utility"
-- > local ss = require 'sirsem.lua'
local ss
do -- pull ourselves up by our own bootstraps
................................................................................
local new = {}
for k, v in pairs(list) do
local nk,nv = fn(k,v)
new[nk or k] = nv or v
end
return new
end
function ss.kfilter(list, fn)
local new = {}
for k, v in pairs(list) do
if fn(k,v) then new[k] = v end
end
return new
................................................................................
function ss.declare(c)
local cls = setmetatable({
__name = c.ident;
}, {
__name = 'class';
__tostring = function() return c.ident or '(class)' end;
})
cls.__call = c.call
cls.__index = function(self, k)
if c.default and c.default[k] then
return c.default[k]
end
................................................................................
if c.cast.string then
cls.__tostring = c.cast.string
end
if c.cast.number then
cls.__tonumber = c.cast.number
end
end
cls.mk = function(...)
local val = setmetatable(c.mk and c.mk(...) or {}, cls)
if c.init then
for k,v in pairs(c.init) do
val[k] = v
end
................................................................................
end
else
me:react(sym)
end
end;
};
}
-- convenience buffer for holding strings under
-- construction, accumulating and compiling then in
-- as quick a way as lua permits
ss.strac = ss.declare {
ident = 'string-accumulator';
mk = function() return {
................................................................................
end;
wrap = function(self,a,b)
table.insert(self.strs, 1, a)
table.insert(self.strs, b)
end;
};
}
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
1
2
3
4
5
6
7
8
9
10
..
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
...
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
...
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
...
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
...
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
|
-- [ʞ] sirsem.lua
-- ~ lexi hale <lexi@hale.su>
-- glowpelt (hsl conversion)
-- ? utility library with functionality common to
-- cortav.lua and its extensions
-- from Ranuir "software utility"
-- > local ss = require 'sirsem.lua'
local ss
do -- pull ourselves up by our own bootstraps
................................................................................
local new = {}
for k, v in pairs(list) do
local nk,nv = fn(k,v)
new[nk or k] = nv or v
end
return new
end
function ss.tmap(fn, a, ...)
if a == nil then return end
return fn(a), ss.tmap(fn, ...)
end
function ss.kfilter(list, fn)
local new = {}
for k, v in pairs(list) do
if fn(k,v) then new[k] = v end
end
return new
................................................................................
function ss.declare(c)
local cls = setmetatable({
__name = c.ident;
}, {
__name = 'class';
__tostring = function() return c.ident or '(class)' end;
__index = c.cfns;
})
cls.__call = c.call
cls.__index = function(self, k)
if c.default and c.default[k] then
return c.default[k]
end
................................................................................
if c.cast.string then
cls.__tostring = c.cast.string
end
if c.cast.number then
cls.__tonumber = c.cast.number
end
end
if c.op then
cls.__add = c.op.sum
cls.__sub = c.op.sub
cls.__div = c.op.div
cls.__mul = c.op.mul
cls.__concat = c.op.cat
end
cls.mk = function(...)
local val = setmetatable(c.mk and c.mk(...) or {}, cls)
if c.init then
for k,v in pairs(c.init) do
val[k] = v
end
................................................................................
end
else
me:react(sym)
end
end;
};
}
function ss.math.clamp(v, l, h)
return math.max(math.min(v, h or 1), l or 0)
end
-- convenience buffer for holding strings under
-- construction, accumulating and compiling then in
-- as quick a way as lua permits
ss.strac = ss.declare {
ident = 'string-accumulator';
mk = function() return {
................................................................................
end;
wrap = function(self,a,b)
table.insert(self.strs, 1, a)
table.insert(self.strs, b)
end;
};
}
-- color class based on c.hale.su/sorcery's, hsl conversion
-- code written by glowpelt. TODO switch to LCH
local function clip(v, ...)
if v == nil then return end
return math.max(0,math.min(0xFF,math.floor(v))), clip(...)
end;
local function bytefrac(f, ...)
if f == nil then return end
return clip(f*0xFF), bytefrac(...)
end
ss.color = ss.declare {
ident = 'color';
mk = function(h,s,l,a) return {
hue = h or 0.0;
sat = s or 0.0;
lum = l or 0.0;
alpha = a or 1.0;
} end;
cfns = {
byteclip = clip;
bytefrac = bytefrac;
};
cast = {
string = function(self) return self:hex() end;
number = function(self) return self:u32() end;
};
op = {
sum = function(self, other)
if ss.color.is(other) then
local fac = ss.math.lerp(self.alpha, 1, other.alpha)
return self:blend(other, fac):warp(function(c)
c.alpha = ss.math.clamp(self.alpha+other.alpha)
end)
else -- color + number = brighter color
return self:warp(function(c)
c.lum = c.lum + other
end)
end
end;
mul = function(self, other)
if ss.color.is(other) then
ss.color.exn 'how the heck do you multiply in hsl anyway':throw()
else
return self:warp(function(c)
c.lum = c.lum * other
end)
end
end;
};
fns = {
tuple = function(self)
return self.hue, self.sat, self.lum, self.alpha
end;
warp = function(self, func)
local n = self:clone()
func(n)
return n
end;
blend = function(self, other, fac)
return ss.color(
ss.math.lerp(fac, self.hue, other.hue),
ss.math.lerp(fac, self.sat, other.sat),
ss.math.lerp(fac, self.lum, other.lum),
ss.math.lerp(fac, self.alpha, other.alpha))
end;
hex = function(self)
local r,g,b,a = bytefrac(self:rgb_t())
if self.alpha == 1 then a = nil end
return string.format('#'..string.rep('%02x',a and 4 or 3),
r,g,b,a)
end;
u32 = function(self)
local r,g,b,a = bytefrac(self:rgb_t())
return r<<24 | g << 16 | b << 8 | a
end;
bytes = function(self)
return { bytefrac(self:rgb_t()) }
end;
alt = function(self, fld, new)
if self[fld] then
return self:warp(function(c) c[fld]=new end)
else
ss.color.exn('no such field %s in color', fld):throw()
end
end;
rgb = function(self)
-- convenience function to get a standardized struct
local r,g,b,a = self:rgb_t()
return {
red = r;
green = g;
blue = b;
alpha = a;
}
end;
rgb_t = function(self)
-- returns rgba values as a tuple
local value = function(n1, n2, hue)
if hue > 360 then
hue = hue - 360
elseif hue < 0 then
hue = hue + 360
end
if hue < 60 then
return n1 + (n2 - n1) * hue/60
elseif hue < 180 then
return n2
elseif hue < 240 then
return n1 + (n2 - n1) * (240 - hue)/60
else
return n1
end
end
local h,s,l,alpha = self:tuple()
local m2
if l < 0.5 then
m2 = l * (1 + s)
else
m2 = l + s - l * s
end
local m1 = 2 * l - m2
if s == 0 then
-- Achromatic, there is no hue
-- In book this errors if hue is not undefined, but we set hue to 0 in this case, not nil or something, so
return l, l, l, alpha
else
-- Chromatic case, so there is a hue
return
value(m1, m2, h + 120),
value(m1, m2, h),
value(m1, m2, h - 120),
alpha
end
end;
};
};
ss.color.exn = ss.exnkind 'color error'
ss.cmdfmt = function(cmd, ...)
return string.format(cmd, ss.tmap(function(s)
if typeof(s) == 'string' then
return string.format("%q", s)
-- FIXME this is incredibly lazy and uses lua quoting, not
-- bourne shell quoting. it *will* cause problems if anything
-- exotic finds its way in and needs to be fixed.
-- TODO provide a proper popen in the C wrapper so wrapped
-- versions at least can launch programs in a sane and secure
-- way.
else
return s
end
end, ...))
end
|