Fix line endings and spaces in *.lua scripts
This commit is contained in:
@@ -1,68 +1,68 @@
|
||||
-- bayer.lua : bayer matrix suppport.
|
||||
--
|
||||
-- Version: 02-jan-2017
|
||||
--
|
||||
-- Copyright 2016-2017 by Samuel Devulder
|
||||
--
|
||||
-- This program is free software; you can redistribute
|
||||
-- it and/or modify it under the terms of the GNU
|
||||
-- General Public License as published by the Free
|
||||
-- Software Foundation; version 2 of the License.
|
||||
-- See <http://www.gnu.org/licenses/>
|
||||
|
||||
if not bayer then
|
||||
bayer = {}
|
||||
|
||||
-- doubles a matrix rows and columns
|
||||
function bayer.double(matrix)
|
||||
local m,n=#matrix,#matrix[1]
|
||||
local r = {}
|
||||
for j=1,m*2 do
|
||||
local t = {}
|
||||
for i=1,n*2 do t[i]=0; end
|
||||
r[j] = t;
|
||||
end
|
||||
|
||||
-- 0 3
|
||||
-- 2 1
|
||||
for j=1,m do
|
||||
for i=1,n do
|
||||
local v = 4*matrix[j][i]
|
||||
r[m*0+j][n*0+i] = v-3
|
||||
r[m*1+j][n*1+i] = v-2
|
||||
r[m*1+j][n*0+i] = v-1
|
||||
r[m*0+j][n*1+i] = v-0
|
||||
end
|
||||
end
|
||||
|
||||
return r;
|
||||
end
|
||||
|
||||
-- returns a version of the matrix normalized into
|
||||
-- the 0-1 range
|
||||
function bayer.norm(matrix)
|
||||
local m,n=#matrix,#matrix[1]
|
||||
local max,ret = 0,{}
|
||||
for j=1,m do
|
||||
for i=1,n do
|
||||
max = math.max(max,matrix[j][i])
|
||||
end
|
||||
end
|
||||
-- max=max+1
|
||||
for j=1,m do
|
||||
ret[j] = {}
|
||||
for i=1,n do
|
||||
ret[j][i]=matrix[j][i]/max
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
-- returns a normalized order-n bayer matrix
|
||||
function bayer.make(n)
|
||||
local m = {{1}}
|
||||
while n>1 do n,m = n/2,bayer.double(m) end
|
||||
return bayer.norm(m)
|
||||
end
|
||||
|
||||
end -- Bayer
|
||||
-- bayer.lua : bayer matrix suppport.
|
||||
--
|
||||
-- Version: 02-jan-2017
|
||||
--
|
||||
-- Copyright 2016-2017 by Samuel Devulder
|
||||
--
|
||||
-- This program is free software; you can redistribute
|
||||
-- it and/or modify it under the terms of the GNU
|
||||
-- General Public License as published by the Free
|
||||
-- Software Foundation; version 2 of the License.
|
||||
-- See <http://www.gnu.org/licenses/>
|
||||
|
||||
if not bayer then
|
||||
bayer = {}
|
||||
|
||||
-- doubles a matrix rows and columns
|
||||
function bayer.double(matrix)
|
||||
local m,n=#matrix,#matrix[1]
|
||||
local r = {}
|
||||
for j=1,m*2 do
|
||||
local t = {}
|
||||
for i=1,n*2 do t[i]=0; end
|
||||
r[j] = t;
|
||||
end
|
||||
|
||||
-- 0 3
|
||||
-- 2 1
|
||||
for j=1,m do
|
||||
for i=1,n do
|
||||
local v = 4*matrix[j][i]
|
||||
r[m*0+j][n*0+i] = v-3
|
||||
r[m*1+j][n*1+i] = v-2
|
||||
r[m*1+j][n*0+i] = v-1
|
||||
r[m*0+j][n*1+i] = v-0
|
||||
end
|
||||
end
|
||||
|
||||
return r;
|
||||
end
|
||||
|
||||
-- returns a version of the matrix normalized into
|
||||
-- the 0-1 range
|
||||
function bayer.norm(matrix)
|
||||
local m,n=#matrix,#matrix[1]
|
||||
local max,ret = 0,{}
|
||||
for j=1,m do
|
||||
for i=1,n do
|
||||
max = math.max(max,matrix[j][i])
|
||||
end
|
||||
end
|
||||
-- max=max+1
|
||||
for j=1,m do
|
||||
ret[j] = {}
|
||||
for i=1,n do
|
||||
ret[j][i]=matrix[j][i]/max
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
-- returns a normalized order-n bayer matrix
|
||||
function bayer.make(n)
|
||||
local m = {{1}}
|
||||
while n>1 do n,m = n/2,bayer.double(m) end
|
||||
return bayer.norm(m)
|
||||
end
|
||||
|
||||
end -- Bayer
|
||||
|
||||
@@ -1,345 +1,345 @@
|
||||
-- color.lua : a color class capable of representing
|
||||
-- and manipulating colors in PC-space (gamma=2.2) or
|
||||
-- in linear space (gamma=1).
|
||||
--
|
||||
-- Version: 02-jan-2017
|
||||
--
|
||||
-- Copyright 2016-2017 by Samuel Devulder
|
||||
--
|
||||
-- This program is free software; you can redistribute
|
||||
-- it and/or modify it under the terms of the GNU
|
||||
-- General Public License as published by the Free
|
||||
-- Software Foundation; version 2 of the License.
|
||||
-- See <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
if not Color then
|
||||
Color = {ONE=255,NORMALIZE=.005}
|
||||
function Color:new(r,g,b)
|
||||
local o = {};
|
||||
o.r = type(r)=='number' and r or r and r.r or 0;
|
||||
o.g = type(g)=='number' and g or r and r.g or 0;
|
||||
o.b = type(b)=='number' and b or r and r.b or 0;
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end
|
||||
Color.black = Color:new(0,0,0)
|
||||
|
||||
function Color.clamp(v,...)
|
||||
if v then
|
||||
return v<0 and 0 or
|
||||
v>Color.ONE and Color.ONE or
|
||||
v,Color.clamp(...)
|
||||
end
|
||||
end
|
||||
|
||||
function Color:clone()
|
||||
return Color:new(self.r, self.g, self.b)
|
||||
end
|
||||
|
||||
function Color:tostring()
|
||||
return "(r=" .. self.r .. " g=" .. self.g .. " b=" .. self.b .. ")"
|
||||
end
|
||||
|
||||
function Color:HSV()
|
||||
local max=math.floor(.5+math.max(self.r,self.g,self.b))
|
||||
local min=math.floor(.5+math.min(self.r,self.g,self.b))
|
||||
|
||||
local H=(max<=min and 0 or
|
||||
max<=self.r and (self.g-self.b)/(max-min)+6 or
|
||||
max<=self.g and (self.b-self.r)/(max-min)+2 or
|
||||
max<=self.b and (self.r-self.g)/(max-min)+4)/6 % 1.0
|
||||
local S=(max==0 or max<=min) and 0 or 1-min/max
|
||||
local V=max/Color.ONE
|
||||
|
||||
return H,S,V
|
||||
end
|
||||
|
||||
function Color:intensity()
|
||||
return .3*self.r + .59*self.g + .11*self.b
|
||||
end
|
||||
|
||||
function Color:mul(val)
|
||||
self.r = self.r * val;
|
||||
self.g = self.g * val;
|
||||
self.b = self.b * val;
|
||||
return self;
|
||||
end
|
||||
|
||||
function Color:div(val)
|
||||
return self:mul(1/val);
|
||||
end
|
||||
|
||||
function Color:add(other)
|
||||
self.r = self.r + other.r;
|
||||
self.g = self.g + other.g;
|
||||
self.b = self.b + other.b;
|
||||
return self;
|
||||
end
|
||||
|
||||
function Color:sub(other)
|
||||
self.r = self.r - other.r;
|
||||
self.g = self.g - other.g;
|
||||
self.b = self.b - other.b;
|
||||
return self;
|
||||
end
|
||||
|
||||
function Color:dist2(other)
|
||||
return self:euclid_dist2(other)
|
||||
-- return Color.dE2000(self,other)^2
|
||||
-- return Color.dE2fast(self,other)
|
||||
end
|
||||
|
||||
function Color:euclid_dist2(other)
|
||||
return (self.r - other.r)^2 +
|
||||
(self.g - other.g)^2 +
|
||||
(self.b - other.b)^2
|
||||
end
|
||||
|
||||
function Color:floor()
|
||||
self.r = math.min(math.floor(self.r),Color.ONE);
|
||||
self.g = math.min(math.floor(self.g),Color.ONE);
|
||||
self.b = math.min(math.floor(self.b),Color.ONE);
|
||||
return self;
|
||||
end
|
||||
|
||||
function Color:toPC()
|
||||
local function f(val)
|
||||
val = val/Color.ONE
|
||||
-- if val<=0.018 then val = 4.5*val; else val = 1.099*(val ^ (1/2.2))-0.099; end
|
||||
|
||||
-- works much metter: https://fr.wikipedia.org/wiki/SRGB
|
||||
if val<=0.0031308 then val=12.92*val else val = 1.055*(val ^ (1/2.4))-0.055 end
|
||||
return val*Color.ONE
|
||||
end;
|
||||
self.r = f(self.r);
|
||||
self.g = f(self.g);
|
||||
self.b = f(self.b);
|
||||
return self;
|
||||
end
|
||||
|
||||
function Color:toLinear()
|
||||
local function f(val)
|
||||
val = val/Color.ONE
|
||||
-- if val<=0.081 then val = val/4.5; else val = ((val+0.099)/1.099)^2.2; end
|
||||
|
||||
-- works much metter: https://fr.wikipedia.org/wiki/SRGB#Transformation_inverse
|
||||
if val<=0.04045 then val = val/12.92 else val = ((val+0.055)/1.055)^2.4 end
|
||||
return val*Color.ONE
|
||||
end;
|
||||
self.r = f(self.r);
|
||||
self.g = f(self.g);
|
||||
self.b = f(self.b);
|
||||
return self;
|
||||
end
|
||||
|
||||
function Color:toRGB()
|
||||
return self.r, self.g, self.b
|
||||
end
|
||||
|
||||
-- return the Color @(x,y) on the original screen in linear space
|
||||
local screen_w, screen_h, _getLinearPictureColor = getpicturesize()
|
||||
function getLinearPictureColor(x,y)
|
||||
if _getLinearPictureColor==nil then
|
||||
_getLinearPictureColor = {}
|
||||
for i=0,255 do _getLinearPictureColor[i] = Color:new(getbackupcolor(i)):toLinear(); end
|
||||
if Color.NORMALIZE>0 then
|
||||
local histo = {}
|
||||
for i=0,255 do histo[i] = 0 end
|
||||
for y=0,screen_h-1 do
|
||||
for x=0,screen_w-1 do
|
||||
local r,g,b = getbackupcolor(getbackuppixel(x,y))
|
||||
histo[r] = histo[r]+1
|
||||
histo[g] = histo[g]+1
|
||||
histo[b] = histo[b]+1
|
||||
end
|
||||
end
|
||||
local acc,thr=0,Color.NORMALIZE*screen_h*screen_w*3
|
||||
local max
|
||||
for i=255,0,-1 do
|
||||
acc = acc + histo[i]
|
||||
if not max and acc>=thr then
|
||||
max = Color:new(i,i,i):toLinear().r
|
||||
end
|
||||
end
|
||||
for _,c in ipairs(_getLinearPictureColor) do
|
||||
c:mul(Color.ONE/max)
|
||||
c.r,c.g,c.b = Color.clamp(c.r,c.g,c.b)
|
||||
end
|
||||
end
|
||||
end
|
||||
return (x<0 or y<0 or x>=screen_w or y>=screen_h) and Color.black or _getLinearPictureColor[getbackuppixel(x,y)]
|
||||
end
|
||||
|
||||
-- http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
|
||||
function Color.RGBtoXYZ(R,G,B)
|
||||
return 0.4887180*R +0.3106803*G +0.2006017*B,
|
||||
0.1762044*R +0.8129847*G +0.0108109*B,
|
||||
0.0102048*G +0.9897952*B
|
||||
end
|
||||
|
||||
function Color.XYZtoRGB(X,Y,Z)
|
||||
return 2.3706743*X -0.9000405*Y -0.4706338*Z,
|
||||
-0.5138850*X +1.4253036*Y +0.0885814*Z,
|
||||
0.0052982*X -0.0146949*Y +1.0093968*Z
|
||||
end
|
||||
|
||||
-- https://fr.wikipedia.org/wiki/CIE_L*a*b*
|
||||
function Color.XYZtoCIELab(X,Y,Z)
|
||||
local function f(t)
|
||||
return t>0.00885645167 and t^(1/3)
|
||||
or 7.78703703704*t+0.13793103448
|
||||
end
|
||||
X,Y,Z=X/Color.ONE,Y/Color.ONE,Z/Color.ONE
|
||||
return 116*f(Y)-16,
|
||||
500*(f(X)-f(Y)),
|
||||
200*(f(Y)-f(Z))
|
||||
end
|
||||
function Color.CIEALabtoXYZ(L,a,b)
|
||||
local function f(t)
|
||||
return t>0.20689655172 and t^3
|
||||
or 0.12841854934*(t-0.13793103448)
|
||||
end
|
||||
local l=(L+16)/116
|
||||
return Color.ONE*f(l),
|
||||
Color.ONE*f(l+a/500),
|
||||
Color.ONE*f(l-b/200)
|
||||
end
|
||||
function Color:toLab()
|
||||
return Color.XYZtoCIELab(Color.RGBtoXYZ(self:toRGB()))
|
||||
end
|
||||
|
||||
-- http://www.brucelindbloom.com/Eqn_DeltaE_CIE2000.html
|
||||
function Color.dE1976(col1,col2)
|
||||
local L1,a1,b1 = col1:toLab()
|
||||
local L2,a2,b2 = col2:toLab()
|
||||
return ((L1-L2)^2+(a1-a2)^2+(b1-b2)^2)^.5
|
||||
end
|
||||
function Color.dE1994(col1,col2)
|
||||
local L1,a1,b1 = col1:toLab()
|
||||
local L2,a2,b2 = col2:toLab()
|
||||
|
||||
local k1,k2 = 0.045,0.015
|
||||
local kL,kC,kH = 1,1,1
|
||||
|
||||
local c1 = (a1^2 + b1^2)^.5
|
||||
local c2 = (a2^2 + b2^2)^.5
|
||||
|
||||
local dA = a1 - a2
|
||||
local dB = b1 - b2
|
||||
local dC = c1 - c2
|
||||
|
||||
local dH2 = dA^2 + dB^2 - dC^2
|
||||
local dH = dH2>0 and dH2^.5 or 0
|
||||
local dL = L1 - L2
|
||||
|
||||
local sL = 1
|
||||
local sC = 1 + k1*c1
|
||||
local sH = 1 + k2*c1
|
||||
|
||||
local vL = dL/(kL*sL)
|
||||
local vC = dC/(kC*sC)
|
||||
local vH = dH/(kH*sH)
|
||||
|
||||
return (vL^2 + vC^2 + vH^2)^.5
|
||||
end
|
||||
-- http://www.color.org/events/colorimetry/Melgosa_CIEDE2000_Workshop-July4.pdf
|
||||
-- https://en.wikipedia.org/wiki/Color_difference#CIEDE2000
|
||||
function Color.dE2000(col1,col2)
|
||||
local L1,a1,b1 = col1:toLab()
|
||||
local L2,a2,b2 = col2:toLab()
|
||||
|
||||
local kL,kC,kH = 1,1,1
|
||||
|
||||
local l_p = (L1 + L2)/2
|
||||
|
||||
function sqrt(x)
|
||||
return x^.5
|
||||
end
|
||||
function norm(x,y)
|
||||
return sqrt(x^2+y^2)
|
||||
end
|
||||
function mean(x,y)
|
||||
return (x+y)/2
|
||||
end
|
||||
local function atan2(a,b)
|
||||
local t=math.atan2(a,b)*180/math.pi
|
||||
return t<0 and t+360 or t
|
||||
end
|
||||
local function sin(x)
|
||||
return math.sin(x*math.pi/180)
|
||||
end
|
||||
local function cos(x)
|
||||
return math.cos(x*math.pi/180)
|
||||
end
|
||||
|
||||
local c1 = norm(a1,b1)
|
||||
local c2 = norm(a2,b2)
|
||||
local c_ = mean(c1,c2)
|
||||
|
||||
local G = 0.5*(1-sqrt(c_^7/(c_^7+25^7)))
|
||||
local a1p = a1*(1+G)
|
||||
local a2p = a2*(1+G)
|
||||
|
||||
local c1p = norm(a1p,b1)
|
||||
local c2p = norm(a2p,b2)
|
||||
local c_p = mean(c1p,c2p)
|
||||
|
||||
local h1p = atan2(b1,a1p)
|
||||
local h2p = atan2(b2,a2p)
|
||||
|
||||
local h_p = mean(h1p,h2p) +
|
||||
(math.abs(h1p - h2p)<=180 and 0 or
|
||||
h1p+h2p<360 and 180 or -180)
|
||||
|
||||
local T = 1 -
|
||||
0.17 * cos( h_p - 30) +
|
||||
0.24 * cos(2 * h_p ) +
|
||||
0.32 * cos(3 * h_p + 6) -
|
||||
0.20 * cos(4 * h_p - 63)
|
||||
|
||||
local dhp = h2p - h1p + (math.abs(h1p - h2p)<=180 and 0 or
|
||||
h2p<=h1p and 360 or
|
||||
-360)
|
||||
local dLp = L2 - L1
|
||||
local dCp = c2p - c1p
|
||||
local dHp = 2*sqrt(c1p*c2p)*sin(dhp/2)
|
||||
|
||||
|
||||
local sL = 1 + 0.015*(l_p - 50)^2/sqrt(20+(l_p-50)^2)
|
||||
local sC = 1 + 0.045*c_p
|
||||
local sH = 1 + 0.015*c_p*T
|
||||
|
||||
local d0 = 30*math.exp(-((h_p-275)/25)^2)
|
||||
|
||||
local rC = 2*sqrt(c_p^7/(c_p^7+25^7))
|
||||
local rT = -rC * sin(2*d0)
|
||||
|
||||
return sqrt( (dLp / (kL*sL))^2 +
|
||||
(dCp / (kC*sC))^2 +
|
||||
(dHp / (kH*sH))^2 +
|
||||
(dCp / (kC*sC))*(dHp / (kH*sH))*rT )
|
||||
end
|
||||
|
||||
function Color.dE2fast(col1,col2)
|
||||
-- http://www.compuphase.com/cmetric.htm#GAMMA
|
||||
local r1,g1,b1 = Color.clamp(col1:toRGB())
|
||||
local r2,g2,b2 = Color.clamp(col2:toRGB())
|
||||
|
||||
local rM = (r1+r2)/(Color.ONE*2)
|
||||
|
||||
return ((r1-r2)^2)*(2+rM) +
|
||||
((g1-g2)^2)*(4+1) +
|
||||
((b1-b2)^2)*(3-rM)
|
||||
end
|
||||
|
||||
function Color:hash(M)
|
||||
M=M or 256
|
||||
local m=(M-1)/Color.ONE
|
||||
local function f(x)
|
||||
return math.floor(.5+(x<0 and 0 or x>Color.ONE and Color.ONE or x)*m)
|
||||
end
|
||||
return f(self.r)+M*(f(self.g)+M*f(self.b))
|
||||
end
|
||||
end -- Color defined
|
||||
-- color.lua : a color class capable of representing
|
||||
-- and manipulating colors in PC-space (gamma=2.2) or
|
||||
-- in linear space (gamma=1).
|
||||
--
|
||||
-- Version: 02-jan-2017
|
||||
--
|
||||
-- Copyright 2016-2017 by Samuel Devulder
|
||||
--
|
||||
-- This program is free software; you can redistribute
|
||||
-- it and/or modify it under the terms of the GNU
|
||||
-- General Public License as published by the Free
|
||||
-- Software Foundation; version 2 of the License.
|
||||
-- See <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
if not Color then
|
||||
Color = {ONE=255,NORMALIZE=.005}
|
||||
function Color:new(r,g,b)
|
||||
local o = {};
|
||||
o.r = type(r)=='number' and r or r and r.r or 0;
|
||||
o.g = type(g)=='number' and g or r and r.g or 0;
|
||||
o.b = type(b)=='number' and b or r and r.b or 0;
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end
|
||||
Color.black = Color:new(0,0,0)
|
||||
|
||||
function Color.clamp(v,...)
|
||||
if v then
|
||||
return v<0 and 0 or
|
||||
v>Color.ONE and Color.ONE or
|
||||
v,Color.clamp(...)
|
||||
end
|
||||
end
|
||||
|
||||
function Color:clone()
|
||||
return Color:new(self.r, self.g, self.b)
|
||||
end
|
||||
|
||||
function Color:tostring()
|
||||
return "(r=" .. self.r .. " g=" .. self.g .. " b=" .. self.b .. ")"
|
||||
end
|
||||
|
||||
function Color:HSV()
|
||||
local max=math.floor(.5+math.max(self.r,self.g,self.b))
|
||||
local min=math.floor(.5+math.min(self.r,self.g,self.b))
|
||||
|
||||
local H=(max<=min and 0 or
|
||||
max<=self.r and (self.g-self.b)/(max-min)+6 or
|
||||
max<=self.g and (self.b-self.r)/(max-min)+2 or
|
||||
max<=self.b and (self.r-self.g)/(max-min)+4)/6 % 1.0
|
||||
local S=(max==0 or max<=min) and 0 or 1-min/max
|
||||
local V=max/Color.ONE
|
||||
|
||||
return H,S,V
|
||||
end
|
||||
|
||||
function Color:intensity()
|
||||
return .3*self.r + .59*self.g + .11*self.b
|
||||
end
|
||||
|
||||
function Color:mul(val)
|
||||
self.r = self.r * val;
|
||||
self.g = self.g * val;
|
||||
self.b = self.b * val;
|
||||
return self;
|
||||
end
|
||||
|
||||
function Color:div(val)
|
||||
return self:mul(1/val);
|
||||
end
|
||||
|
||||
function Color:add(other)
|
||||
self.r = self.r + other.r;
|
||||
self.g = self.g + other.g;
|
||||
self.b = self.b + other.b;
|
||||
return self;
|
||||
end
|
||||
|
||||
function Color:sub(other)
|
||||
self.r = self.r - other.r;
|
||||
self.g = self.g - other.g;
|
||||
self.b = self.b - other.b;
|
||||
return self;
|
||||
end
|
||||
|
||||
function Color:dist2(other)
|
||||
return self:euclid_dist2(other)
|
||||
-- return Color.dE2000(self,other)^2
|
||||
-- return Color.dE2fast(self,other)
|
||||
end
|
||||
|
||||
function Color:euclid_dist2(other)
|
||||
return (self.r - other.r)^2 +
|
||||
(self.g - other.g)^2 +
|
||||
(self.b - other.b)^2
|
||||
end
|
||||
|
||||
function Color:floor()
|
||||
self.r = math.min(math.floor(self.r),Color.ONE);
|
||||
self.g = math.min(math.floor(self.g),Color.ONE);
|
||||
self.b = math.min(math.floor(self.b),Color.ONE);
|
||||
return self;
|
||||
end
|
||||
|
||||
function Color:toPC()
|
||||
local function f(val)
|
||||
val = val/Color.ONE
|
||||
-- if val<=0.018 then val = 4.5*val; else val = 1.099*(val ^ (1/2.2))-0.099; end
|
||||
|
||||
-- works much metter: https://fr.wikipedia.org/wiki/SRGB
|
||||
if val<=0.0031308 then val=12.92*val else val = 1.055*(val ^ (1/2.4))-0.055 end
|
||||
return val*Color.ONE
|
||||
end;
|
||||
self.r = f(self.r);
|
||||
self.g = f(self.g);
|
||||
self.b = f(self.b);
|
||||
return self;
|
||||
end
|
||||
|
||||
function Color:toLinear()
|
||||
local function f(val)
|
||||
val = val/Color.ONE
|
||||
-- if val<=0.081 then val = val/4.5; else val = ((val+0.099)/1.099)^2.2; end
|
||||
|
||||
-- works much metter: https://fr.wikipedia.org/wiki/SRGB#Transformation_inverse
|
||||
if val<=0.04045 then val = val/12.92 else val = ((val+0.055)/1.055)^2.4 end
|
||||
return val*Color.ONE
|
||||
end;
|
||||
self.r = f(self.r);
|
||||
self.g = f(self.g);
|
||||
self.b = f(self.b);
|
||||
return self;
|
||||
end
|
||||
|
||||
function Color:toRGB()
|
||||
return self.r, self.g, self.b
|
||||
end
|
||||
|
||||
-- return the Color @(x,y) on the original screen in linear space
|
||||
local screen_w, screen_h, _getLinearPictureColor = getpicturesize()
|
||||
function getLinearPictureColor(x,y)
|
||||
if _getLinearPictureColor==nil then
|
||||
_getLinearPictureColor = {}
|
||||
for i=0,255 do _getLinearPictureColor[i] = Color:new(getbackupcolor(i)):toLinear(); end
|
||||
if Color.NORMALIZE>0 then
|
||||
local histo = {}
|
||||
for i=0,255 do histo[i] = 0 end
|
||||
for y=0,screen_h-1 do
|
||||
for x=0,screen_w-1 do
|
||||
local r,g,b = getbackupcolor(getbackuppixel(x,y))
|
||||
histo[r] = histo[r]+1
|
||||
histo[g] = histo[g]+1
|
||||
histo[b] = histo[b]+1
|
||||
end
|
||||
end
|
||||
local acc,thr=0,Color.NORMALIZE*screen_h*screen_w*3
|
||||
local max
|
||||
for i=255,0,-1 do
|
||||
acc = acc + histo[i]
|
||||
if not max and acc>=thr then
|
||||
max = Color:new(i,i,i):toLinear().r
|
||||
end
|
||||
end
|
||||
for _,c in ipairs(_getLinearPictureColor) do
|
||||
c:mul(Color.ONE/max)
|
||||
c.r,c.g,c.b = Color.clamp(c.r,c.g,c.b)
|
||||
end
|
||||
end
|
||||
end
|
||||
return (x<0 or y<0 or x>=screen_w or y>=screen_h) and Color.black or _getLinearPictureColor[getbackuppixel(x,y)]
|
||||
end
|
||||
|
||||
-- http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
|
||||
function Color.RGBtoXYZ(R,G,B)
|
||||
return 0.4887180*R +0.3106803*G +0.2006017*B,
|
||||
0.1762044*R +0.8129847*G +0.0108109*B,
|
||||
0.0102048*G +0.9897952*B
|
||||
end
|
||||
|
||||
function Color.XYZtoRGB(X,Y,Z)
|
||||
return 2.3706743*X -0.9000405*Y -0.4706338*Z,
|
||||
-0.5138850*X +1.4253036*Y +0.0885814*Z,
|
||||
0.0052982*X -0.0146949*Y +1.0093968*Z
|
||||
end
|
||||
|
||||
-- https://fr.wikipedia.org/wiki/CIE_L*a*b*
|
||||
function Color.XYZtoCIELab(X,Y,Z)
|
||||
local function f(t)
|
||||
return t>0.00885645167 and t^(1/3)
|
||||
or 7.78703703704*t+0.13793103448
|
||||
end
|
||||
X,Y,Z=X/Color.ONE,Y/Color.ONE,Z/Color.ONE
|
||||
return 116*f(Y)-16,
|
||||
500*(f(X)-f(Y)),
|
||||
200*(f(Y)-f(Z))
|
||||
end
|
||||
function Color.CIEALabtoXYZ(L,a,b)
|
||||
local function f(t)
|
||||
return t>0.20689655172 and t^3
|
||||
or 0.12841854934*(t-0.13793103448)
|
||||
end
|
||||
local l=(L+16)/116
|
||||
return Color.ONE*f(l),
|
||||
Color.ONE*f(l+a/500),
|
||||
Color.ONE*f(l-b/200)
|
||||
end
|
||||
function Color:toLab()
|
||||
return Color.XYZtoCIELab(Color.RGBtoXYZ(self:toRGB()))
|
||||
end
|
||||
|
||||
-- http://www.brucelindbloom.com/Eqn_DeltaE_CIE2000.html
|
||||
function Color.dE1976(col1,col2)
|
||||
local L1,a1,b1 = col1:toLab()
|
||||
local L2,a2,b2 = col2:toLab()
|
||||
return ((L1-L2)^2+(a1-a2)^2+(b1-b2)^2)^.5
|
||||
end
|
||||
function Color.dE1994(col1,col2)
|
||||
local L1,a1,b1 = col1:toLab()
|
||||
local L2,a2,b2 = col2:toLab()
|
||||
|
||||
local k1,k2 = 0.045,0.015
|
||||
local kL,kC,kH = 1,1,1
|
||||
|
||||
local c1 = (a1^2 + b1^2)^.5
|
||||
local c2 = (a2^2 + b2^2)^.5
|
||||
|
||||
local dA = a1 - a2
|
||||
local dB = b1 - b2
|
||||
local dC = c1 - c2
|
||||
|
||||
local dH2 = dA^2 + dB^2 - dC^2
|
||||
local dH = dH2>0 and dH2^.5 or 0
|
||||
local dL = L1 - L2
|
||||
|
||||
local sL = 1
|
||||
local sC = 1 + k1*c1
|
||||
local sH = 1 + k2*c1
|
||||
|
||||
local vL = dL/(kL*sL)
|
||||
local vC = dC/(kC*sC)
|
||||
local vH = dH/(kH*sH)
|
||||
|
||||
return (vL^2 + vC^2 + vH^2)^.5
|
||||
end
|
||||
-- http://www.color.org/events/colorimetry/Melgosa_CIEDE2000_Workshop-July4.pdf
|
||||
-- https://en.wikipedia.org/wiki/Color_difference#CIEDE2000
|
||||
function Color.dE2000(col1,col2)
|
||||
local L1,a1,b1 = col1:toLab()
|
||||
local L2,a2,b2 = col2:toLab()
|
||||
|
||||
local kL,kC,kH = 1,1,1
|
||||
|
||||
local l_p = (L1 + L2)/2
|
||||
|
||||
function sqrt(x)
|
||||
return x^.5
|
||||
end
|
||||
function norm(x,y)
|
||||
return sqrt(x^2+y^2)
|
||||
end
|
||||
function mean(x,y)
|
||||
return (x+y)/2
|
||||
end
|
||||
local function atan2(a,b)
|
||||
local t=math.atan2(a,b)*180/math.pi
|
||||
return t<0 and t+360 or t
|
||||
end
|
||||
local function sin(x)
|
||||
return math.sin(x*math.pi/180)
|
||||
end
|
||||
local function cos(x)
|
||||
return math.cos(x*math.pi/180)
|
||||
end
|
||||
|
||||
local c1 = norm(a1,b1)
|
||||
local c2 = norm(a2,b2)
|
||||
local c_ = mean(c1,c2)
|
||||
|
||||
local G = 0.5*(1-sqrt(c_^7/(c_^7+25^7)))
|
||||
local a1p = a1*(1+G)
|
||||
local a2p = a2*(1+G)
|
||||
|
||||
local c1p = norm(a1p,b1)
|
||||
local c2p = norm(a2p,b2)
|
||||
local c_p = mean(c1p,c2p)
|
||||
|
||||
local h1p = atan2(b1,a1p)
|
||||
local h2p = atan2(b2,a2p)
|
||||
|
||||
local h_p = mean(h1p,h2p) +
|
||||
(math.abs(h1p - h2p)<=180 and 0 or
|
||||
h1p+h2p<360 and 180 or -180)
|
||||
|
||||
local T = 1 -
|
||||
0.17 * cos( h_p - 30) +
|
||||
0.24 * cos(2 * h_p ) +
|
||||
0.32 * cos(3 * h_p + 6) -
|
||||
0.20 * cos(4 * h_p - 63)
|
||||
|
||||
local dhp = h2p - h1p + (math.abs(h1p - h2p)<=180 and 0 or
|
||||
h2p<=h1p and 360 or
|
||||
-360)
|
||||
local dLp = L2 - L1
|
||||
local dCp = c2p - c1p
|
||||
local dHp = 2*sqrt(c1p*c2p)*sin(dhp/2)
|
||||
|
||||
|
||||
local sL = 1 + 0.015*(l_p - 50)^2/sqrt(20+(l_p-50)^2)
|
||||
local sC = 1 + 0.045*c_p
|
||||
local sH = 1 + 0.015*c_p*T
|
||||
|
||||
local d0 = 30*math.exp(-((h_p-275)/25)^2)
|
||||
|
||||
local rC = 2*sqrt(c_p^7/(c_p^7+25^7))
|
||||
local rT = -rC * sin(2*d0)
|
||||
|
||||
return sqrt( (dLp / (kL*sL))^2 +
|
||||
(dCp / (kC*sC))^2 +
|
||||
(dHp / (kH*sH))^2 +
|
||||
(dCp / (kC*sC))*(dHp / (kH*sH))*rT )
|
||||
end
|
||||
|
||||
function Color.dE2fast(col1,col2)
|
||||
-- http://www.compuphase.com/cmetric.htm#GAMMA
|
||||
local r1,g1,b1 = Color.clamp(col1:toRGB())
|
||||
local r2,g2,b2 = Color.clamp(col2:toRGB())
|
||||
|
||||
local rM = (r1+r2)/(Color.ONE*2)
|
||||
|
||||
return ((r1-r2)^2)*(2+rM) +
|
||||
((g1-g2)^2)*(4+1) +
|
||||
((b1-b2)^2)*(3-rM)
|
||||
end
|
||||
|
||||
function Color:hash(M)
|
||||
M=M or 256
|
||||
local m=(M-1)/Color.ONE
|
||||
local function f(x)
|
||||
return math.floor(.5+(x<0 and 0 or x>Color.ONE and Color.ONE or x)*m)
|
||||
end
|
||||
return f(self.r)+M*(f(self.g)+M*f(self.b))
|
||||
end
|
||||
end -- Color defined
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,219 +1,219 @@
|
||||
-- convxhull.lua : support for computing the convex
|
||||
-- hull of a set of points.
|
||||
--
|
||||
-- inspired from: https://gist.github.com/anonymous/5184ba0bcab21d3dd19781efd3aae543
|
||||
--
|
||||
-- Version: 02-jan-2017
|
||||
--
|
||||
-- Copyright 2016-2017 by Samuel Devulder
|
||||
--
|
||||
-- This program is free software; you can redistribute
|
||||
-- it and/or modify it under the terms of the GNU
|
||||
-- General Public License as published by the Free
|
||||
-- Software Foundation; version 2 of the License.
|
||||
-- See <http://www.gnu.org/licenses/>
|
||||
|
||||
if not ConvexHull then
|
||||
|
||||
local function sub(u,v)
|
||||
return {u[1]-v[1],u[2]-v[2],u[3]-v[3]}
|
||||
end
|
||||
|
||||
local function mul(k,u)
|
||||
return {k*u[1],k*u[2],k*u[3]}
|
||||
end
|
||||
|
||||
local function cross(u,v)
|
||||
return {u[2]*v[3] - u[3]*v[2],
|
||||
u[3]*v[1] - u[1]*v[3],
|
||||
u[1]*v[2] - u[2]*v[1]}
|
||||
end
|
||||
|
||||
local function dot(u,v)
|
||||
return u[1]*v[1] + u[2]*v[2] + u[3]*v[3]
|
||||
end
|
||||
|
||||
local function unit(u)
|
||||
local d=dot(u,u)
|
||||
return d==0 and u or mul(1/d^.5, u)
|
||||
end
|
||||
|
||||
ConvexHull = {}
|
||||
|
||||
function ConvexHull:new(coordFct)
|
||||
local o = {
|
||||
points={},
|
||||
coord=coordFct
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end
|
||||
|
||||
function ConvexHull.coord(elt)
|
||||
return {elt[1],elt[2],elt[3]}
|
||||
end
|
||||
|
||||
function ConvexHull:vect(a,b)
|
||||
return sub(self.coord(b),self.coord(a))
|
||||
end
|
||||
|
||||
function ConvexHull:normal(face)
|
||||
local u=self:vect(face[1],face[2])
|
||||
local v=self:vect(face[1],face[3])
|
||||
return cross(u,v)
|
||||
end
|
||||
|
||||
function ConvexHull:printPoint(p)
|
||||
return '('..table.concat(self.coord(p),',')..')'
|
||||
end
|
||||
|
||||
function ConvexHull:printFace(F)
|
||||
return '['..self:printPoint(F[1])..' '..
|
||||
self:printPoint(F[2])..' '..
|
||||
self:printPoint(F[3])..']'
|
||||
end
|
||||
|
||||
function ConvexHull:seen(face,p)
|
||||
local N=self:normal(face)
|
||||
local P=self:vect(face[1],p)
|
||||
return dot(N,P)>=0
|
||||
end
|
||||
|
||||
function ConvexHull:bdry(faces)
|
||||
local code={n=0}
|
||||
function code.encode(pt,...)
|
||||
if pt then
|
||||
local k = code[pt]
|
||||
if not k then
|
||||
k = code.n+1
|
||||
code[k] = pt
|
||||
code[pt] = k
|
||||
code.n = k
|
||||
end
|
||||
local rest = code.encode(...)
|
||||
return rest and (k..','..rest) or ""..k
|
||||
end
|
||||
end
|
||||
function code.decode(str)
|
||||
local i = str:find(',')
|
||||
if i then
|
||||
local k = str:sub(1,i-1)
|
||||
return code[tonumber(k)],code.decode(str:sub(i+1))
|
||||
else
|
||||
return code[tonumber(str)]
|
||||
end
|
||||
end
|
||||
local set = {}
|
||||
local function add(...)
|
||||
set[code.encode(...)] = true
|
||||
end
|
||||
local function rem(...)
|
||||
set[code.encode(...)] = nil
|
||||
end
|
||||
local function keys()
|
||||
local r = {}
|
||||
for k in pairs(set) do
|
||||
r[{code.decode(k)}] = true
|
||||
end
|
||||
return r
|
||||
end
|
||||
for F in pairs(faces) do
|
||||
add(F[1],F[2])
|
||||
add(F[2],F[3])
|
||||
add(F[3],F[1])
|
||||
end
|
||||
for F in pairs(faces) do
|
||||
rem(F[1],F[3])
|
||||
rem(F[3],F[2])
|
||||
rem(F[2],F[1])
|
||||
end
|
||||
return keys()
|
||||
end
|
||||
|
||||
function ConvexHull:addPoint(p)
|
||||
-- first 3 points
|
||||
if self.points then
|
||||
if p==self.points[1] or p==self.points[2] then return end
|
||||
table.insert(self.points,p)
|
||||
|
||||
if #self.points==3 then
|
||||
self.hull={
|
||||
{self.points[1],self.points[2],self.points[3]},
|
||||
{self.points[1],self.points[3],self.points[2]}
|
||||
}
|
||||
self.points=nil
|
||||
end
|
||||
else
|
||||
local seenF,n = {},0
|
||||
for _,F in ipairs(self.hull) do
|
||||
if F[1]==p or F[2]==p or F[3]==p then return end
|
||||
if self:seen(F,p) then seenF[F]=true;n=n+1 end
|
||||
end
|
||||
|
||||
if n==#self.hull then
|
||||
-- if can see all faces, unsee ones looking "down"
|
||||
local N
|
||||
for F in pairs(seenF) do N=self:normal(F); break; end
|
||||
for F in pairs(seenF) do
|
||||
if dot(self:normal(F),N)<=0 then
|
||||
seenF[F] = nil
|
||||
n=n-1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- remove (old) seen faces
|
||||
local z=#self.hull
|
||||
for i=#self.hull,1,-1 do
|
||||
if seenF[self.hull[i]] then
|
||||
table.remove(self.hull,i)
|
||||
end
|
||||
end
|
||||
|
||||
-- insert new boundaries with seen faces
|
||||
for E in pairs(self:bdry(seenF)) do
|
||||
table.insert(self.hull,{E[1],E[2],p})
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function ConvexHull:verticesSet()
|
||||
local v = {}
|
||||
if self.hull then
|
||||
for _,F in ipairs(self.hull) do
|
||||
v[F[1]] = true
|
||||
v[F[2]] = true
|
||||
v[F[3]] = true
|
||||
end
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
function ConvexHull:verticesSize()
|
||||
local n = 0
|
||||
for _ in pairs(self:verticesSet()) do n=n+1 end
|
||||
return n
|
||||
end
|
||||
|
||||
function ConvexHull:distToFace(F,pt)
|
||||
local N=unit(self:normal(F))
|
||||
local P=self:vect(F[1],pt)
|
||||
return dot(N,P)
|
||||
end
|
||||
|
||||
function ConvexHull:distToHull(pt)
|
||||
local d
|
||||
for _,F in ipairs(self.hull) do
|
||||
local t = self:distToFace(F,pt)
|
||||
d = d==nil and t or
|
||||
(0<=t and t<d or
|
||||
0>=t and t>d) and t or
|
||||
d
|
||||
if d==0 then break end
|
||||
end
|
||||
return d
|
||||
end
|
||||
|
||||
end -- ConvexHull
|
||||
-- convxhull.lua : support for computing the convex
|
||||
-- hull of a set of points.
|
||||
--
|
||||
-- inspired from: https://gist.github.com/anonymous/5184ba0bcab21d3dd19781efd3aae543
|
||||
--
|
||||
-- Version: 02-jan-2017
|
||||
--
|
||||
-- Copyright 2016-2017 by Samuel Devulder
|
||||
--
|
||||
-- This program is free software; you can redistribute
|
||||
-- it and/or modify it under the terms of the GNU
|
||||
-- General Public License as published by the Free
|
||||
-- Software Foundation; version 2 of the License.
|
||||
-- See <http://www.gnu.org/licenses/>
|
||||
|
||||
if not ConvexHull then
|
||||
|
||||
local function sub(u,v)
|
||||
return {u[1]-v[1],u[2]-v[2],u[3]-v[3]}
|
||||
end
|
||||
|
||||
local function mul(k,u)
|
||||
return {k*u[1],k*u[2],k*u[3]}
|
||||
end
|
||||
|
||||
local function cross(u,v)
|
||||
return {u[2]*v[3] - u[3]*v[2],
|
||||
u[3]*v[1] - u[1]*v[3],
|
||||
u[1]*v[2] - u[2]*v[1]}
|
||||
end
|
||||
|
||||
local function dot(u,v)
|
||||
return u[1]*v[1] + u[2]*v[2] + u[3]*v[3]
|
||||
end
|
||||
|
||||
local function unit(u)
|
||||
local d=dot(u,u)
|
||||
return d==0 and u or mul(1/d^.5, u)
|
||||
end
|
||||
|
||||
ConvexHull = {}
|
||||
|
||||
function ConvexHull:new(coordFct)
|
||||
local o = {
|
||||
points={},
|
||||
coord=coordFct
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end
|
||||
|
||||
function ConvexHull.coord(elt)
|
||||
return {elt[1],elt[2],elt[3]}
|
||||
end
|
||||
|
||||
function ConvexHull:vect(a,b)
|
||||
return sub(self.coord(b),self.coord(a))
|
||||
end
|
||||
|
||||
function ConvexHull:normal(face)
|
||||
local u=self:vect(face[1],face[2])
|
||||
local v=self:vect(face[1],face[3])
|
||||
return cross(u,v)
|
||||
end
|
||||
|
||||
function ConvexHull:printPoint(p)
|
||||
return '('..table.concat(self.coord(p),',')..')'
|
||||
end
|
||||
|
||||
function ConvexHull:printFace(F)
|
||||
return '['..self:printPoint(F[1])..' '..
|
||||
self:printPoint(F[2])..' '..
|
||||
self:printPoint(F[3])..']'
|
||||
end
|
||||
|
||||
function ConvexHull:seen(face,p)
|
||||
local N=self:normal(face)
|
||||
local P=self:vect(face[1],p)
|
||||
return dot(N,P)>=0
|
||||
end
|
||||
|
||||
function ConvexHull:bdry(faces)
|
||||
local code={n=0}
|
||||
function code.encode(pt,...)
|
||||
if pt then
|
||||
local k = code[pt]
|
||||
if not k then
|
||||
k = code.n+1
|
||||
code[k] = pt
|
||||
code[pt] = k
|
||||
code.n = k
|
||||
end
|
||||
local rest = code.encode(...)
|
||||
return rest and (k..','..rest) or ""..k
|
||||
end
|
||||
end
|
||||
function code.decode(str)
|
||||
local i = str:find(',')
|
||||
if i then
|
||||
local k = str:sub(1,i-1)
|
||||
return code[tonumber(k)],code.decode(str:sub(i+1))
|
||||
else
|
||||
return code[tonumber(str)]
|
||||
end
|
||||
end
|
||||
local set = {}
|
||||
local function add(...)
|
||||
set[code.encode(...)] = true
|
||||
end
|
||||
local function rem(...)
|
||||
set[code.encode(...)] = nil
|
||||
end
|
||||
local function keys()
|
||||
local r = {}
|
||||
for k in pairs(set) do
|
||||
r[{code.decode(k)}] = true
|
||||
end
|
||||
return r
|
||||
end
|
||||
for F in pairs(faces) do
|
||||
add(F[1],F[2])
|
||||
add(F[2],F[3])
|
||||
add(F[3],F[1])
|
||||
end
|
||||
for F in pairs(faces) do
|
||||
rem(F[1],F[3])
|
||||
rem(F[3],F[2])
|
||||
rem(F[2],F[1])
|
||||
end
|
||||
return keys()
|
||||
end
|
||||
|
||||
function ConvexHull:addPoint(p)
|
||||
-- first 3 points
|
||||
if self.points then
|
||||
if p==self.points[1] or p==self.points[2] then return end
|
||||
table.insert(self.points,p)
|
||||
|
||||
if #self.points==3 then
|
||||
self.hull={
|
||||
{self.points[1],self.points[2],self.points[3]},
|
||||
{self.points[1],self.points[3],self.points[2]}
|
||||
}
|
||||
self.points=nil
|
||||
end
|
||||
else
|
||||
local seenF,n = {},0
|
||||
for _,F in ipairs(self.hull) do
|
||||
if F[1]==p or F[2]==p or F[3]==p then return end
|
||||
if self:seen(F,p) then seenF[F]=true;n=n+1 end
|
||||
end
|
||||
|
||||
if n==#self.hull then
|
||||
-- if can see all faces, unsee ones looking "down"
|
||||
local N
|
||||
for F in pairs(seenF) do N=self:normal(F); break; end
|
||||
for F in pairs(seenF) do
|
||||
if dot(self:normal(F),N)<=0 then
|
||||
seenF[F] = nil
|
||||
n=n-1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- remove (old) seen faces
|
||||
local z=#self.hull
|
||||
for i=#self.hull,1,-1 do
|
||||
if seenF[self.hull[i]] then
|
||||
table.remove(self.hull,i)
|
||||
end
|
||||
end
|
||||
|
||||
-- insert new boundaries with seen faces
|
||||
for E in pairs(self:bdry(seenF)) do
|
||||
table.insert(self.hull,{E[1],E[2],p})
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function ConvexHull:verticesSet()
|
||||
local v = {}
|
||||
if self.hull then
|
||||
for _,F in ipairs(self.hull) do
|
||||
v[F[1]] = true
|
||||
v[F[2]] = true
|
||||
v[F[3]] = true
|
||||
end
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
function ConvexHull:verticesSize()
|
||||
local n = 0
|
||||
for _ in pairs(self:verticesSet()) do n=n+1 end
|
||||
return n
|
||||
end
|
||||
|
||||
function ConvexHull:distToFace(F,pt)
|
||||
local N=unit(self:normal(F))
|
||||
local P=self:vect(F[1],pt)
|
||||
return dot(N,P)
|
||||
end
|
||||
|
||||
function ConvexHull:distToHull(pt)
|
||||
local d
|
||||
for _,F in ipairs(self.hull) do
|
||||
local t = self:distToFace(F,pt)
|
||||
d = d==nil and t or
|
||||
(0<=t and t<d or
|
||||
0>=t and t>d) and t or
|
||||
d
|
||||
if d==0 then break end
|
||||
end
|
||||
return d
|
||||
end
|
||||
|
||||
end -- ConvexHull
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,454 +1,454 @@
|
||||
-- thomson.lua : lots of utility for handling
|
||||
-- thomson screen.
|
||||
--
|
||||
-- Version: 02-jan-2017
|
||||
--
|
||||
-- Copyright 2016-2017 by Samuel Devulder
|
||||
--
|
||||
-- This program is free software; you can redistribute
|
||||
-- it and/or modify it under the terms of the GNU
|
||||
-- General Public License as published by the Free
|
||||
-- Software Foundation; version 2 of the License.
|
||||
-- See <http://www.gnu.org/licenses/>
|
||||
|
||||
if not thomson then
|
||||
|
||||
run("color.lua") -- optionnal
|
||||
|
||||
thomson = {optiMAP=true}
|
||||
|
||||
-- RAM banks
|
||||
thomson.ramA = {}
|
||||
thomson.ramB = {}
|
||||
|
||||
function thomson.clear()
|
||||
for i=1,8000 do
|
||||
thomson.ramA[i] = 0
|
||||
thomson.ramB[i] = 0
|
||||
end
|
||||
end
|
||||
|
||||
-- color levels
|
||||
thomson.levels = {
|
||||
-- in pc-space (0-255):
|
||||
pc = {0,100,127,142,163,179,191,203,215,223,231,239,
|
||||
243,247,251,255},
|
||||
-- in linear space (0-255):
|
||||
linear = {},
|
||||
-- maps pc-levels (0-255) to thomson levels (1-16)
|
||||
pc2to={},
|
||||
-- maps linear-levels (0-255) to thomson levels (1-16)
|
||||
linear2to={}
|
||||
};
|
||||
|
||||
-- pc space to linear space
|
||||
local function toLinear(val)
|
||||
-- use the version from Color library
|
||||
if not Color then
|
||||
val = val/255
|
||||
if val<=0.081 then
|
||||
val = val/4.5;
|
||||
else
|
||||
val = ((val+0.099)/1.099)^2.2;
|
||||
end
|
||||
val = val*255
|
||||
return val;
|
||||
else
|
||||
return Color:new(val,0,0):toLinear().r
|
||||
end
|
||||
end
|
||||
|
||||
for i=1,16 do
|
||||
thomson.levels.linear[i] = toLinear(thomson.levels.pc[i])
|
||||
end
|
||||
for i=0,255 do
|
||||
local r,cm,dm;
|
||||
r,cm,dm = toLinear(i),0,1e30
|
||||
for c,v in ipairs(thomson.levels.linear) do
|
||||
local d = math.abs(v-r);
|
||||
if d<dm then cm,dm = c,d; end
|
||||
end
|
||||
thomson.levels.pc2to[i] = cm;
|
||||
r,cm,dm = i,0,1e30
|
||||
for c,v in ipairs(thomson.levels.linear) do
|
||||
local d = math.abs(v-r);
|
||||
if d<dm then cm,dm = c,d; end
|
||||
end
|
||||
thomson.levels.linear2to[i] = cm;
|
||||
end
|
||||
|
||||
-- palette stuff
|
||||
function thomson.palette(i, pal)
|
||||
-- returns palette #i if pal is missing (nil)
|
||||
-- if pal is a number, sets palette #i
|
||||
-- if pal is an array, sets the palette #i, #i+1, ...
|
||||
if type(pal)=='table' then
|
||||
for j,v in ipairs(pal) do
|
||||
thomson.palette(i+j-1,v)
|
||||
end
|
||||
elseif pal and i>=0 and i<thomson._palette.max then
|
||||
thomson._palette[i+1] = pal
|
||||
elseif not pal and i>=0 and i<thomson._palette.max then
|
||||
return thomson._palette[i+1]
|
||||
end
|
||||
end;
|
||||
thomson._palette = {offset = 0, max=16}
|
||||
thomson.default_palette = {0,15,240,255,3840,3855,4080,4095,
|
||||
1911,826,931,938,2611,2618,3815,123}
|
||||
|
||||
-- border color
|
||||
function thomson.border(c)
|
||||
if c then
|
||||
thomson._border = c;
|
||||
else
|
||||
return thomson._border
|
||||
end
|
||||
end
|
||||
thomson.border(0)
|
||||
|
||||
-- helper to appen tables to tables
|
||||
function thomson._append(result, ...)
|
||||
for _,tab in ipairs({...}) do
|
||||
for _,v in ipairs(tab) do
|
||||
table.insert(result,v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- RLE compression of data into result
|
||||
function thomson._compress(result,data)
|
||||
local partial,p,pmax={},1,#data
|
||||
local function addCarToPartial(car)
|
||||
partial[2] = partial[2]+1
|
||||
partial[2+partial[2]] = car
|
||||
end
|
||||
while p<=pmax do
|
||||
local num,car = 1,data[p]
|
||||
while num<255 and p<pmax and data[p+1]==car do
|
||||
num,p = num+1,p+1
|
||||
end
|
||||
local default=true
|
||||
if partial[1] then
|
||||
-- 01 aa 01 bb ==> 00 02 aa bb
|
||||
if default and num==1 and partial[1]==1 then
|
||||
partial = {0,2,partial[2],car}
|
||||
default = false
|
||||
end
|
||||
-- 00 n xx xx xx 01 bb ==> 00 n+1 xx xx xx bb
|
||||
if default and num==1 and partial[1]==0 and partial[2]<255 then
|
||||
addCarToPartial(car)
|
||||
default = false
|
||||
end
|
||||
-- 00 n xx xx xx 02 bb ==> 00 n+2 xx xx xx bb bb (pas utile mais sert quand combiné à la regle ci-dessus)
|
||||
if default and num==2 and partial[1]==0 and partial[2]<254 then
|
||||
addCarToPartial(car)
|
||||
addCarToPartial(car)
|
||||
default = false
|
||||
end
|
||||
end
|
||||
if default then
|
||||
thomson._append(result, partial)
|
||||
partial = {num,car}
|
||||
end
|
||||
p=p+1
|
||||
end
|
||||
thomson._append(result, partial)
|
||||
return result
|
||||
end
|
||||
|
||||
-- save a map file corresponging to the current file
|
||||
-- if a map file already exist, a confirmation is
|
||||
-- prompted to the user
|
||||
local function save_current_file()
|
||||
local function exist(file)
|
||||
local f=io.open(file,'rb')
|
||||
if not f then return false else io.close(f); return true; end
|
||||
end
|
||||
local name,path = getfilename()
|
||||
local mapname = string.gsub(name,"%.%w*$","") .. ".map"
|
||||
local fullname = path .. '/' .. mapname
|
||||
local ok = not exist(fullname)
|
||||
if not ok then
|
||||
selectbox("Ovr " .. mapname .. "?", "Yes", function() ok = true; end, "No", function() ok = false; end)
|
||||
end
|
||||
if ok then thomson.savep(fullname) end
|
||||
end
|
||||
|
||||
-- saves the thomson screen into a MAP file
|
||||
function thomson.savep(name)
|
||||
if not name then return save_current_file() end
|
||||
|
||||
wait(0) -- allow for key handling
|
||||
local data = thomson._get_map_data()
|
||||
local tmp = {0, math.floor(#data/256), #data%256,0,0}
|
||||
thomson._append(tmp,data,{255,0,0,0,0})
|
||||
local function save(name, buf)
|
||||
local out = io.open(name,"wb")
|
||||
out:write(buf)
|
||||
out:close()
|
||||
end
|
||||
save(name, string.char(unpack(tmp)))
|
||||
|
||||
-- save raw data as well ?
|
||||
local moved, key, mx, my, mb = waitinput(0.01)
|
||||
if key==4123 then -- shift-ESC ==> save raw files as well
|
||||
save(name .. ".rama", string.char(unpack(thomson.ramA)))
|
||||
save(name .. ".ramb", string.char(unpack(thomson.ramB)))
|
||||
local pal = ""
|
||||
for i=0,15 do
|
||||
local val = thomson.palette(i)
|
||||
pal=pal..string.char(math.floor(val/256),val%256)
|
||||
end
|
||||
save(name .. ".pal", pal)
|
||||
messagebox('Saved MAP + RAMA/RAMB/PAL files.')
|
||||
end
|
||||
end
|
||||
waitbreak(0.01)
|
||||
|
||||
function thomson.info(...)
|
||||
local txt = ""
|
||||
for _,t in ipairs({...}) do txt = txt .. t end
|
||||
statusmessage(txt);
|
||||
if waitbreak(0)==1 then
|
||||
local ok=false
|
||||
selectbox("Abort ?", "Yes", function() ok = true end, "No", function() ok = false end)
|
||||
if ok then error('Operation aborted') end
|
||||
end
|
||||
end
|
||||
|
||||
-- copy ramA/B onto GrafX2 screen
|
||||
function thomson.updatescreen()
|
||||
-- back out
|
||||
for i=0,255 do
|
||||
setcolor(i,0,0,0)
|
||||
end
|
||||
-- refresh screen content
|
||||
clearpicture(thomson._palette.offset + thomson.border())
|
||||
for y=0,thomson.h-1 do
|
||||
for x=0,thomson.w-1 do
|
||||
local p = thomson.point(x,y)
|
||||
if p<0 then p=-p-1 end
|
||||
thomson._putpixel(x,y,thomson._palette.offset + p)
|
||||
end
|
||||
end
|
||||
-- refresh palette
|
||||
for i=1,thomson._palette.max do
|
||||
local v=thomson._palette[i]
|
||||
local r=v % 16
|
||||
local g=math.floor(v/16) % 16
|
||||
local b=math.floor(v/256) % 16
|
||||
setcolor(i+thomson._palette.offset-1,
|
||||
thomson.levels.pc[r+1],
|
||||
thomson.levels.pc[g+1],
|
||||
thomson.levels.pc[b+1])
|
||||
end
|
||||
updatescreen()
|
||||
end
|
||||
|
||||
-- bitmap 16 mode
|
||||
function thomson.setBM16()
|
||||
-- put a pixel onto real screen
|
||||
function thomson._putpixel(x,y,c)
|
||||
putpicturepixel(x*2+0,y,c)
|
||||
putpicturepixel(x*2+1,y,c)
|
||||
end
|
||||
-- put a pixel in thomson screen
|
||||
function thomson.pset(x,y,c)
|
||||
local bank = x%4<2 and thomson.ramA or thomson.ramB
|
||||
local offs = math.floor(x/4)+y*40+1
|
||||
if x%2==0 then
|
||||
bank[offs] = (bank[offs]%16)+c*16
|
||||
else
|
||||
bank[offs] = math.floor(bank[offs]/16)*16+c
|
||||
end
|
||||
-- c=c+thomson._palette.offset
|
||||
-- putpicturepixel(x*2+0,y,c)
|
||||
-- putpicturepixel(x*2+1,y,c)
|
||||
end
|
||||
-- get thomson pixel at (x,y)
|
||||
function thomson.point(x,y)
|
||||
local bank = x%4<2 and thomson.ramA or thomson.ramB
|
||||
local offs = math.floor(x/4)+y*40+1
|
||||
if x%2==0 then
|
||||
return math.floor(bank[offs]/16)
|
||||
else
|
||||
return bank[offs]%16
|
||||
end
|
||||
end
|
||||
-- return internal MAP file
|
||||
function thomson._get_map_data()
|
||||
local tmp = {}
|
||||
for x=1,40 do
|
||||
for y=x,x+7960,40 do
|
||||
table.insert(tmp, thomson.ramA[y])
|
||||
end
|
||||
for y=x,x+7960,40 do
|
||||
table.insert(tmp, thomson.ramB[y])
|
||||
end
|
||||
wait(0) -- allow for key handling
|
||||
end
|
||||
local pal = {}
|
||||
for i=1,16 do
|
||||
pal[2*i-1] = math.floor(thomson._palette[i]/256)
|
||||
pal[2*i+0] = thomson._palette[i]%256
|
||||
end
|
||||
-- build data
|
||||
local data={
|
||||
-- BM16
|
||||
0x40,
|
||||
-- ncols-1
|
||||
79,
|
||||
-- nlines-1
|
||||
24
|
||||
};
|
||||
thomson._compress(data, tmp)
|
||||
thomson._append(data,{0,0})
|
||||
-- padd to word
|
||||
if #data%2==1 then table.insert(data,0); end
|
||||
-- tosnap
|
||||
thomson._append(data,{0,128,0,thomson.border(),0,3})
|
||||
thomson._append(data, pal)
|
||||
thomson._append(data,{0xa5,0x5a})
|
||||
return data
|
||||
end
|
||||
|
||||
thomson.w = 160
|
||||
thomson.h = 200
|
||||
thomson.palette(0,thomson.default_palette)
|
||||
thomson.border(0)
|
||||
thomson.clear()
|
||||
end
|
||||
|
||||
-- mode MO5
|
||||
function thomson.setMO5()
|
||||
-- put a pixel onto real screen
|
||||
thomson._putpixel = putpicturepixel
|
||||
-- helpers
|
||||
local function bittst(val,mask)
|
||||
-- return bit32.btest(val,mask)
|
||||
return (val % (2*mask))>=mask;
|
||||
end
|
||||
local function bitset(val,mask)
|
||||
-- return bit32.bor(val, mask)
|
||||
return bittst(val,mask) and val or (val+mask)
|
||||
end
|
||||
local function bitclr(val,mask)
|
||||
-- return bit32.band(val,255-mask)
|
||||
return bittst(val,mask) and (val-mask) or val
|
||||
end
|
||||
-- put a pixel in thomson screen
|
||||
function thomson.pset(x,y,c)
|
||||
local offs = math.floor(x/8)+y*40+1
|
||||
local mask = 2^(7-(x%8))
|
||||
if c>=0 then
|
||||
thomson.ramB[offs] = (thomson.ramB[offs]%16)+c*16
|
||||
thomson.ramA[offs] = bitset(thomson.ramA[offs],mask)
|
||||
else
|
||||
c=-c-1
|
||||
thomson.ramB[offs] = math.floor(thomson.ramB[offs]/16)*16+c
|
||||
thomson.ramA[offs] = bitclr(thomson.ramA[offs],mask)
|
||||
end
|
||||
end
|
||||
-- get thomson pixel at (x,y)
|
||||
function thomson.point(x,y)
|
||||
local offs = math.floor(x/8)+y*40+1
|
||||
local mask = 2^(7-(x%8))
|
||||
if bittst(thomson.ramA[offs],mask) then
|
||||
return math.floor(thomson.ramB[offs]/16)
|
||||
else
|
||||
return -(thomson.ramB[offs]%16)-1
|
||||
end
|
||||
end
|
||||
-- convert color from MO5 to TO7 (MAP requires TO7 encoding)
|
||||
local function mo5to7(val)
|
||||
-- MO5: DCBA 4321
|
||||
-- __
|
||||
-- TO7: 4DCB A321
|
||||
local t=((val%16)>=8) and 0 or 128
|
||||
val = math.floor(val/16)*8 + (val%8)
|
||||
val = (val>=64 and val-64 or val+64) + t
|
||||
return val
|
||||
end
|
||||
-- return internal MAP file
|
||||
function thomson._get_map_data()
|
||||
-- create columnwise data
|
||||
local tmpA,tmpB={},{};
|
||||
for x=1,40 do
|
||||
for y=x,x+7960,40 do
|
||||
table.insert(tmpA, thomson.ramA[y])
|
||||
table.insert(tmpB, thomson.ramB[y])
|
||||
end
|
||||
wait(0) -- allow for key handling
|
||||
end
|
||||
if thomson.optiMAP then
|
||||
-- optimize
|
||||
for i=2,8000 do
|
||||
local c1,c2 = math.floor(tmpB[i-0]/16),tmpB[i-0]%16
|
||||
local d1,d2 = math.floor(tmpB[i-1]/16),tmpB[i-1]%16
|
||||
|
||||
if tmpA[i-1]==255-tmpA[i] or c1==d2 and c2==c1 then
|
||||
tmpA[i] = 255-tmpA[i]
|
||||
tmpB[i] = c2*16+c1
|
||||
elseif tmpA[i]==255 and c1==d1 or tmpA[i]==0 and c2==d2 then
|
||||
tmpB[i] = tmpB[i-1]
|
||||
end
|
||||
end
|
||||
else
|
||||
for i=1,8000 do
|
||||
local c1,c2 = math.floor(tmpB[i]/16),tmpB[i]%16
|
||||
|
||||
if tmpA[i]==255 or c1<c2 then
|
||||
tmpA[i] = 255-tmpA[i]
|
||||
tmpB[i] = c2*16+c1
|
||||
end
|
||||
end
|
||||
end
|
||||
-- convert into to7 encoding
|
||||
for i=1,#tmpB do tmpB[i] = mo5to7(tmpB[i]); end
|
||||
-- build data
|
||||
local data={
|
||||
-- BM40
|
||||
0x00,
|
||||
-- ncols-1
|
||||
39,
|
||||
-- nlines-1
|
||||
24
|
||||
};
|
||||
thomson._compress(data, tmpA); tmpA=nil;
|
||||
thomson._append(data,{0,0})
|
||||
thomson._compress(data, tmpB); tmpB=nil;
|
||||
thomson._append(data,{0,0})
|
||||
-- padd to word (for compatibility with basic)
|
||||
if #data%2==1 then table.insert(data,0); end
|
||||
|
||||
-- tosnap
|
||||
local orig_palette = true
|
||||
for i=0,15 do
|
||||
if thomson.default_palette[i+1]~=thomson.palette(i) then
|
||||
orig_palette = false
|
||||
break
|
||||
end
|
||||
end
|
||||
if not orig_palette then
|
||||
local pal = {}
|
||||
for i=0,15 do
|
||||
local v = thomson.palette(i)
|
||||
pal[2*i+1] = math.floor(v/256)
|
||||
pal[2*i+2] = v%256
|
||||
end
|
||||
thomson._append(data,{0,0,0,thomson.border(),0,0})
|
||||
thomson._append(data, pal)
|
||||
thomson._append(data,{0xa5,0x5a})
|
||||
end
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
thomson.w = 320
|
||||
thomson.h = 200
|
||||
thomson.palette(0,thomson.default_palette)
|
||||
thomson.border(0)
|
||||
thomson.clear()
|
||||
end
|
||||
|
||||
end -- thomson
|
||||
-- thomson.lua : lots of utility for handling
|
||||
-- thomson screen.
|
||||
--
|
||||
-- Version: 02-jan-2017
|
||||
--
|
||||
-- Copyright 2016-2017 by Samuel Devulder
|
||||
--
|
||||
-- This program is free software; you can redistribute
|
||||
-- it and/or modify it under the terms of the GNU
|
||||
-- General Public License as published by the Free
|
||||
-- Software Foundation; version 2 of the License.
|
||||
-- See <http://www.gnu.org/licenses/>
|
||||
|
||||
if not thomson then
|
||||
|
||||
run("color.lua") -- optionnal
|
||||
|
||||
thomson = {optiMAP=true}
|
||||
|
||||
-- RAM banks
|
||||
thomson.ramA = {}
|
||||
thomson.ramB = {}
|
||||
|
||||
function thomson.clear()
|
||||
for i=1,8000 do
|
||||
thomson.ramA[i] = 0
|
||||
thomson.ramB[i] = 0
|
||||
end
|
||||
end
|
||||
|
||||
-- color levels
|
||||
thomson.levels = {
|
||||
-- in pc-space (0-255):
|
||||
pc = {0,100,127,142,163,179,191,203,215,223,231,239,
|
||||
243,247,251,255},
|
||||
-- in linear space (0-255):
|
||||
linear = {},
|
||||
-- maps pc-levels (0-255) to thomson levels (1-16)
|
||||
pc2to={},
|
||||
-- maps linear-levels (0-255) to thomson levels (1-16)
|
||||
linear2to={}
|
||||
};
|
||||
|
||||
-- pc space to linear space
|
||||
local function toLinear(val)
|
||||
-- use the version from Color library
|
||||
if not Color then
|
||||
val = val/255
|
||||
if val<=0.081 then
|
||||
val = val/4.5;
|
||||
else
|
||||
val = ((val+0.099)/1.099)^2.2;
|
||||
end
|
||||
val = val*255
|
||||
return val;
|
||||
else
|
||||
return Color:new(val,0,0):toLinear().r
|
||||
end
|
||||
end
|
||||
|
||||
for i=1,16 do
|
||||
thomson.levels.linear[i] = toLinear(thomson.levels.pc[i])
|
||||
end
|
||||
for i=0,255 do
|
||||
local r,cm,dm;
|
||||
r,cm,dm = toLinear(i),0,1e30
|
||||
for c,v in ipairs(thomson.levels.linear) do
|
||||
local d = math.abs(v-r);
|
||||
if d<dm then cm,dm = c,d; end
|
||||
end
|
||||
thomson.levels.pc2to[i] = cm;
|
||||
r,cm,dm = i,0,1e30
|
||||
for c,v in ipairs(thomson.levels.linear) do
|
||||
local d = math.abs(v-r);
|
||||
if d<dm then cm,dm = c,d; end
|
||||
end
|
||||
thomson.levels.linear2to[i] = cm;
|
||||
end
|
||||
|
||||
-- palette stuff
|
||||
function thomson.palette(i, pal)
|
||||
-- returns palette #i if pal is missing (nil)
|
||||
-- if pal is a number, sets palette #i
|
||||
-- if pal is an array, sets the palette #i, #i+1, ...
|
||||
if type(pal)=='table' then
|
||||
for j,v in ipairs(pal) do
|
||||
thomson.palette(i+j-1,v)
|
||||
end
|
||||
elseif pal and i>=0 and i<thomson._palette.max then
|
||||
thomson._palette[i+1] = pal
|
||||
elseif not pal and i>=0 and i<thomson._palette.max then
|
||||
return thomson._palette[i+1]
|
||||
end
|
||||
end;
|
||||
thomson._palette = {offset = 0, max=16}
|
||||
thomson.default_palette = {0,15,240,255,3840,3855,4080,4095,
|
||||
1911,826,931,938,2611,2618,3815,123}
|
||||
|
||||
-- border color
|
||||
function thomson.border(c)
|
||||
if c then
|
||||
thomson._border = c;
|
||||
else
|
||||
return thomson._border
|
||||
end
|
||||
end
|
||||
thomson.border(0)
|
||||
|
||||
-- helper to appen tables to tables
|
||||
function thomson._append(result, ...)
|
||||
for _,tab in ipairs({...}) do
|
||||
for _,v in ipairs(tab) do
|
||||
table.insert(result,v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- RLE compression of data into result
|
||||
function thomson._compress(result,data)
|
||||
local partial,p,pmax={},1,#data
|
||||
local function addCarToPartial(car)
|
||||
partial[2] = partial[2]+1
|
||||
partial[2+partial[2]] = car
|
||||
end
|
||||
while p<=pmax do
|
||||
local num,car = 1,data[p]
|
||||
while num<255 and p<pmax and data[p+1]==car do
|
||||
num,p = num+1,p+1
|
||||
end
|
||||
local default=true
|
||||
if partial[1] then
|
||||
-- 01 aa 01 bb ==> 00 02 aa bb
|
||||
if default and num==1 and partial[1]==1 then
|
||||
partial = {0,2,partial[2],car}
|
||||
default = false
|
||||
end
|
||||
-- 00 n xx xx xx 01 bb ==> 00 n+1 xx xx xx bb
|
||||
if default and num==1 and partial[1]==0 and partial[2]<255 then
|
||||
addCarToPartial(car)
|
||||
default = false
|
||||
end
|
||||
-- 00 n xx xx xx 02 bb ==> 00 n+2 xx xx xx bb bb (pas utile mais sert quand combiné à la regle ci-dessus)
|
||||
if default and num==2 and partial[1]==0 and partial[2]<254 then
|
||||
addCarToPartial(car)
|
||||
addCarToPartial(car)
|
||||
default = false
|
||||
end
|
||||
end
|
||||
if default then
|
||||
thomson._append(result, partial)
|
||||
partial = {num,car}
|
||||
end
|
||||
p=p+1
|
||||
end
|
||||
thomson._append(result, partial)
|
||||
return result
|
||||
end
|
||||
|
||||
-- save a map file corresponging to the current file
|
||||
-- if a map file already exist, a confirmation is
|
||||
-- prompted to the user
|
||||
local function save_current_file()
|
||||
local function exist(file)
|
||||
local f=io.open(file,'rb')
|
||||
if not f then return false else io.close(f); return true; end
|
||||
end
|
||||
local name,path = getfilename()
|
||||
local mapname = string.gsub(name,"%.%w*$","") .. ".map"
|
||||
local fullname = path .. '/' .. mapname
|
||||
local ok = not exist(fullname)
|
||||
if not ok then
|
||||
selectbox("Ovr " .. mapname .. "?", "Yes", function() ok = true; end, "No", function() ok = false; end)
|
||||
end
|
||||
if ok then thomson.savep(fullname) end
|
||||
end
|
||||
|
||||
-- saves the thomson screen into a MAP file
|
||||
function thomson.savep(name)
|
||||
if not name then return save_current_file() end
|
||||
|
||||
wait(0) -- allow for key handling
|
||||
local data = thomson._get_map_data()
|
||||
local tmp = {0, math.floor(#data/256), #data%256,0,0}
|
||||
thomson._append(tmp,data,{255,0,0,0,0})
|
||||
local function save(name, buf)
|
||||
local out = io.open(name,"wb")
|
||||
out:write(buf)
|
||||
out:close()
|
||||
end
|
||||
save(name, string.char(unpack(tmp)))
|
||||
|
||||
-- save raw data as well ?
|
||||
local moved, key, mx, my, mb = waitinput(0.01)
|
||||
if key==4123 then -- shift-ESC ==> save raw files as well
|
||||
save(name .. ".rama", string.char(unpack(thomson.ramA)))
|
||||
save(name .. ".ramb", string.char(unpack(thomson.ramB)))
|
||||
local pal = ""
|
||||
for i=0,15 do
|
||||
local val = thomson.palette(i)
|
||||
pal=pal..string.char(math.floor(val/256),val%256)
|
||||
end
|
||||
save(name .. ".pal", pal)
|
||||
messagebox('Saved MAP + RAMA/RAMB/PAL files.')
|
||||
end
|
||||
end
|
||||
waitbreak(0.01)
|
||||
|
||||
function thomson.info(...)
|
||||
local txt = ""
|
||||
for _,t in ipairs({...}) do txt = txt .. t end
|
||||
statusmessage(txt);
|
||||
if waitbreak(0)==1 then
|
||||
local ok=false
|
||||
selectbox("Abort ?", "Yes", function() ok = true end, "No", function() ok = false end)
|
||||
if ok then error('Operation aborted') end
|
||||
end
|
||||
end
|
||||
|
||||
-- copy ramA/B onto GrafX2 screen
|
||||
function thomson.updatescreen()
|
||||
-- back out
|
||||
for i=0,255 do
|
||||
setcolor(i,0,0,0)
|
||||
end
|
||||
-- refresh screen content
|
||||
clearpicture(thomson._palette.offset + thomson.border())
|
||||
for y=0,thomson.h-1 do
|
||||
for x=0,thomson.w-1 do
|
||||
local p = thomson.point(x,y)
|
||||
if p<0 then p=-p-1 end
|
||||
thomson._putpixel(x,y,thomson._palette.offset + p)
|
||||
end
|
||||
end
|
||||
-- refresh palette
|
||||
for i=1,thomson._palette.max do
|
||||
local v=thomson._palette[i]
|
||||
local r=v % 16
|
||||
local g=math.floor(v/16) % 16
|
||||
local b=math.floor(v/256) % 16
|
||||
setcolor(i+thomson._palette.offset-1,
|
||||
thomson.levels.pc[r+1],
|
||||
thomson.levels.pc[g+1],
|
||||
thomson.levels.pc[b+1])
|
||||
end
|
||||
updatescreen()
|
||||
end
|
||||
|
||||
-- bitmap 16 mode
|
||||
function thomson.setBM16()
|
||||
-- put a pixel onto real screen
|
||||
function thomson._putpixel(x,y,c)
|
||||
putpicturepixel(x*2+0,y,c)
|
||||
putpicturepixel(x*2+1,y,c)
|
||||
end
|
||||
-- put a pixel in thomson screen
|
||||
function thomson.pset(x,y,c)
|
||||
local bank = x%4<2 and thomson.ramA or thomson.ramB
|
||||
local offs = math.floor(x/4)+y*40+1
|
||||
if x%2==0 then
|
||||
bank[offs] = (bank[offs]%16)+c*16
|
||||
else
|
||||
bank[offs] = math.floor(bank[offs]/16)*16+c
|
||||
end
|
||||
-- c=c+thomson._palette.offset
|
||||
-- putpicturepixel(x*2+0,y,c)
|
||||
-- putpicturepixel(x*2+1,y,c)
|
||||
end
|
||||
-- get thomson pixel at (x,y)
|
||||
function thomson.point(x,y)
|
||||
local bank = x%4<2 and thomson.ramA or thomson.ramB
|
||||
local offs = math.floor(x/4)+y*40+1
|
||||
if x%2==0 then
|
||||
return math.floor(bank[offs]/16)
|
||||
else
|
||||
return bank[offs]%16
|
||||
end
|
||||
end
|
||||
-- return internal MAP file
|
||||
function thomson._get_map_data()
|
||||
local tmp = {}
|
||||
for x=1,40 do
|
||||
for y=x,x+7960,40 do
|
||||
table.insert(tmp, thomson.ramA[y])
|
||||
end
|
||||
for y=x,x+7960,40 do
|
||||
table.insert(tmp, thomson.ramB[y])
|
||||
end
|
||||
wait(0) -- allow for key handling
|
||||
end
|
||||
local pal = {}
|
||||
for i=1,16 do
|
||||
pal[2*i-1] = math.floor(thomson._palette[i]/256)
|
||||
pal[2*i+0] = thomson._palette[i]%256
|
||||
end
|
||||
-- build data
|
||||
local data={
|
||||
-- BM16
|
||||
0x40,
|
||||
-- ncols-1
|
||||
79,
|
||||
-- nlines-1
|
||||
24
|
||||
};
|
||||
thomson._compress(data, tmp)
|
||||
thomson._append(data,{0,0})
|
||||
-- padd to word
|
||||
if #data%2==1 then table.insert(data,0); end
|
||||
-- tosnap
|
||||
thomson._append(data,{0,128,0,thomson.border(),0,3})
|
||||
thomson._append(data, pal)
|
||||
thomson._append(data,{0xa5,0x5a})
|
||||
return data
|
||||
end
|
||||
|
||||
thomson.w = 160
|
||||
thomson.h = 200
|
||||
thomson.palette(0,thomson.default_palette)
|
||||
thomson.border(0)
|
||||
thomson.clear()
|
||||
end
|
||||
|
||||
-- mode MO5
|
||||
function thomson.setMO5()
|
||||
-- put a pixel onto real screen
|
||||
thomson._putpixel = putpicturepixel
|
||||
-- helpers
|
||||
local function bittst(val,mask)
|
||||
-- return bit32.btest(val,mask)
|
||||
return (val % (2*mask))>=mask;
|
||||
end
|
||||
local function bitset(val,mask)
|
||||
-- return bit32.bor(val, mask)
|
||||
return bittst(val,mask) and val or (val+mask)
|
||||
end
|
||||
local function bitclr(val,mask)
|
||||
-- return bit32.band(val,255-mask)
|
||||
return bittst(val,mask) and (val-mask) or val
|
||||
end
|
||||
-- put a pixel in thomson screen
|
||||
function thomson.pset(x,y,c)
|
||||
local offs = math.floor(x/8)+y*40+1
|
||||
local mask = 2^(7-(x%8))
|
||||
if c>=0 then
|
||||
thomson.ramB[offs] = (thomson.ramB[offs]%16)+c*16
|
||||
thomson.ramA[offs] = bitset(thomson.ramA[offs],mask)
|
||||
else
|
||||
c=-c-1
|
||||
thomson.ramB[offs] = math.floor(thomson.ramB[offs]/16)*16+c
|
||||
thomson.ramA[offs] = bitclr(thomson.ramA[offs],mask)
|
||||
end
|
||||
end
|
||||
-- get thomson pixel at (x,y)
|
||||
function thomson.point(x,y)
|
||||
local offs = math.floor(x/8)+y*40+1
|
||||
local mask = 2^(7-(x%8))
|
||||
if bittst(thomson.ramA[offs],mask) then
|
||||
return math.floor(thomson.ramB[offs]/16)
|
||||
else
|
||||
return -(thomson.ramB[offs]%16)-1
|
||||
end
|
||||
end
|
||||
-- convert color from MO5 to TO7 (MAP requires TO7 encoding)
|
||||
local function mo5to7(val)
|
||||
-- MO5: DCBA 4321
|
||||
-- __
|
||||
-- TO7: 4DCB A321
|
||||
local t=((val%16)>=8) and 0 or 128
|
||||
val = math.floor(val/16)*8 + (val%8)
|
||||
val = (val>=64 and val-64 or val+64) + t
|
||||
return val
|
||||
end
|
||||
-- return internal MAP file
|
||||
function thomson._get_map_data()
|
||||
-- create columnwise data
|
||||
local tmpA,tmpB={},{};
|
||||
for x=1,40 do
|
||||
for y=x,x+7960,40 do
|
||||
table.insert(tmpA, thomson.ramA[y])
|
||||
table.insert(tmpB, thomson.ramB[y])
|
||||
end
|
||||
wait(0) -- allow for key handling
|
||||
end
|
||||
if thomson.optiMAP then
|
||||
-- optimize
|
||||
for i=2,8000 do
|
||||
local c1,c2 = math.floor(tmpB[i-0]/16),tmpB[i-0]%16
|
||||
local d1,d2 = math.floor(tmpB[i-1]/16),tmpB[i-1]%16
|
||||
|
||||
if tmpA[i-1]==255-tmpA[i] or c1==d2 and c2==c1 then
|
||||
tmpA[i] = 255-tmpA[i]
|
||||
tmpB[i] = c2*16+c1
|
||||
elseif tmpA[i]==255 and c1==d1 or tmpA[i]==0 and c2==d2 then
|
||||
tmpB[i] = tmpB[i-1]
|
||||
end
|
||||
end
|
||||
else
|
||||
for i=1,8000 do
|
||||
local c1,c2 = math.floor(tmpB[i]/16),tmpB[i]%16
|
||||
|
||||
if tmpA[i]==255 or c1<c2 then
|
||||
tmpA[i] = 255-tmpA[i]
|
||||
tmpB[i] = c2*16+c1
|
||||
end
|
||||
end
|
||||
end
|
||||
-- convert into to7 encoding
|
||||
for i=1,#tmpB do tmpB[i] = mo5to7(tmpB[i]); end
|
||||
-- build data
|
||||
local data={
|
||||
-- BM40
|
||||
0x00,
|
||||
-- ncols-1
|
||||
39,
|
||||
-- nlines-1
|
||||
24
|
||||
};
|
||||
thomson._compress(data, tmpA); tmpA=nil;
|
||||
thomson._append(data,{0,0})
|
||||
thomson._compress(data, tmpB); tmpB=nil;
|
||||
thomson._append(data,{0,0})
|
||||
-- padd to word (for compatibility with basic)
|
||||
if #data%2==1 then table.insert(data,0); end
|
||||
|
||||
-- tosnap
|
||||
local orig_palette = true
|
||||
for i=0,15 do
|
||||
if thomson.default_palette[i+1]~=thomson.palette(i) then
|
||||
orig_palette = false
|
||||
break
|
||||
end
|
||||
end
|
||||
if not orig_palette then
|
||||
local pal = {}
|
||||
for i=0,15 do
|
||||
local v = thomson.palette(i)
|
||||
pal[2*i+1] = math.floor(v/256)
|
||||
pal[2*i+2] = v%256
|
||||
end
|
||||
thomson._append(data,{0,0,0,thomson.border(),0,0})
|
||||
thomson._append(data, pal)
|
||||
thomson._append(data,{0xa5,0x5a})
|
||||
end
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
thomson.w = 320
|
||||
thomson.h = 200
|
||||
thomson.palette(0,thomson.default_palette)
|
||||
thomson.border(0)
|
||||
thomson.clear()
|
||||
end
|
||||
|
||||
end -- thomson
|
||||
|
||||
Reference in New Issue
Block a user