Fix line endings and spaces in *.lua scripts

This commit is contained in:
Thomas Bernard 2018-06-21 15:27:09 +02:00
parent 6d08223660
commit 673d3836d2
24 changed files with 3824 additions and 3830 deletions

View File

@ -1,33 +1,31 @@
-- Test LUA inputbox -- Test LUA inputbox
-- this script tests the inputbox -- this script tests the inputbox
w, h = getbrushsize() w, h = getbrushsize()
--[[ --[[
messagebox( messagebox(
"Forecolor: " .. getforecolor() .. "\n" .. "Forecolor: " .. getforecolor() .. "\n" ..
"Backcolor: " .. getbackcolor() .. "\n" .. "Backcolor: " .. getbackcolor() .. "\n" ..
"Transparent color: " .. gettranscolor() .. "\n" .. "Transparent color: " .. gettranscolor() .. "\n" ..
"Brush dimensions: " .. w .. "x" .. h "Brush dimensions: " .. w .. "x" .. h
) )
]] ]]
ok, w, h = inputbox("Modify brush", ok, w, h = inputbox("Modify brush",
"RGB", 1, 0, 1, -1, "RGB", 1, 0, 1, -1,
"HSV", 0, 0, 1, -1, "HSV", 0, 0, 1, -1,
"HSL", 0, 0, 1, -1, "HSL", 0, 0, 1, -1,
"Width", w, -900.0,900.0, 3, "Width", w, -900.0,900.0, 3,
"Height", h, -900.0,900.0, 4, "Height", h, -900.0,900.0, 4,
"X Flip", 0, 0, 1, 0, "X Flip", 0, 0, 1, 0,
"Y Flip", 0, 0, 1, 0, "Y Flip", 0, 0, 1, 0,
"Degrees",1, 0, 1, -2, "Degrees",1, 0, 1, -2,
"Radians",0, 0, 1, -2 "Radians",0, 0, 1, -2
); );
if ok == true then if ok == true then
messagebox( messagebox(
"w: " .. w .. "\n" .. "w: " .. w .. "\n" ..
"h: " .. h .. "\n" "h: " .. h .. "\n"
) )
end end

View File

@ -1,66 +1,65 @@
local counter=0; local counter=0;
local printcounter = function () local printcounter = function ()
windowprint(10,54, string.format("% .3d", counter)); windowprint(10,54, string.format("% .3d", counter));
end; end;
windowopen(100,150, "Dialogtest"); windowopen(100,150, "Dialogtest");
windowbutton(6, 18, 54, 14, "Close", 27); -- 1, shortcut=ESC windowbutton(6, 18, 54, 14, "Close", 27); -- 1, shortcut=ESC
windowrepeatbutton(6, 38, 14, 14, "+"); -- 2 windowrepeatbutton(6, 38, 14, 14, "+"); -- 2
windowrepeatbutton(26, 38, 14, 14, "-"); -- 3 windowrepeatbutton(26, 38, 14, 14, "-"); -- 3
windowbutton(6, 70, 54, 14, "Help"); -- 4 windowbutton(6, 70, 54, 14, "Help"); -- 4
windowinput(6, 88, 10); windowinput(6, 88, 10);
printcounter(); printcounter();
repeat repeat
local button, button2, key = windowdodialog(); local button, button2, key = windowdodialog();
if button == 2 then -- "+" if button == 2 then -- "+"
counter=counter+1; counter=counter+1;
printcounter(); printcounter();
end end
if button == 3 then -- "-" if button == 3 then -- "-"
counter=counter-1; counter=counter-1;
printcounter(); printcounter();
end end
if button == 4 then -- "Help" if button == 4 then -- "Help"
messagebox("Help screen"); messagebox("Help screen");
end end
until key == 27 or button == 1; until key == 27 or button == 1;
windowclose(); windowclose();
-- messagebox(tostring(button) .. " " .. tostring(button2)); -- messagebox(tostring(button) .. " " .. tostring(button2));
---- Open_window(149,118,"Grid"); ---- Open_window(149,118,"Grid");
-- Display_cursor(); -- Display_cursor();
-- Hide_cursor(); -- Hide_cursor();
---- Close_window(); ---- Close_window();
---- Update_window_area(0,0,Window_width, Window_height); ---- Update_window_area(0,0,Window_width, Window_height);
---- clicked_button=Window_clicked_button(); ---- clicked_button=Window_clicked_button();
-- --
-- -- standard button -- -- standard button
---- Window_set_normal_button(12,92,51,14,"Cancel",0,1,KEY_ESC); -- 1 ---- Window_set_normal_button(12,92,51,14,"Cancel",0,1,KEY_ESC); -- 1
-- -- repeatable button (while held) -- -- repeatable button (while held)
---- Window_set_repeatable_button(202,43,13,11,"-",0,1,SDLK_LAST); -- 8 ---- Window_set_repeatable_button(202,43,13,11,"-",0,1,SDLK_LAST); -- 8
-- -- text input -- -- text input
-- Window_set_input_button(29,24,3); -- 3 -- Window_set_input_button(29,24,3); -- 3
-- Window_input_content(input_x_button,str); -- Window_input_content(input_x_button,str);
-- Readline(31,26,str,3,INPUT_TYPE_INTEGER); -- Readline(31,26,str,3,INPUT_TYPE_INTEGER);
-- --
-- -- dropdown -- -- dropdown
-- Window_set_dropdown_button(216, 158, 84,14,84,"Preset...", 0,0,1,RIGHT_SIDE|LEFT_SIDE,1); -- Window_set_dropdown_button(216, 158, 84,14,84,"Preset...", 0,0,1,RIGHT_SIDE|LEFT_SIDE,1);
-- Window_dropdown_clear_items(Button); -- Window_dropdown_clear_items(Button);
-- Window_dropdown_add_item(Button,0,"Set"); -- Window_dropdown_add_item(Button,0,"Set");
-- --
-- -- vertical scroller -- -- vertical scroller
-- mix_scroller = Window_set_scroller_button(31,20,84,256,1,Main_backups->Pages->Gradients->Range[Current_gradient].Mix); -- mix_scroller = Window_set_scroller_button(31,20,84,256,1,Main_backups->Pages->Gradients->Range[Current_gradient].Mix);
-- Window_draw_slider(mix_scroller); -- Window_draw_slider(mix_scroller);
-- --
-- -- display -- -- display
---- Print_in_window(11,26, "X:",MC_Dark,MC_Light); ---- Print_in_window(11,26, "X:",MC_Dark,MC_Light);
---- Print_in_window_limited(Button->Pos_X+3+10,Button->Pos_Y+2,Config.Bookmark_label[bookmark_number],8,MC_Black,MC_Light); ---- Print_in_window_limited(Button->Pos_X+3+10,Button->Pos_Y+2,Config.Bookmark_label[bookmark_number],8,MC_Black,MC_Light);
-- Window_display_frame_in( 6, 21,110, 52); -- Window_display_frame_in( 6, 21,110, 52);
-- Window_display_frame(6,17,130,37); -- Window_display_frame(6,17,130,37);
-- Window_rectangle(panel->Pos_X, panel->Pos_Y, panel->Width, panel->Height+1, MC_Light); -- Window_rectangle(panel->Pos_X, panel->Pos_Y, panel->Width, panel->Height+1, MC_Light);

View File

@ -1,48 +1,47 @@
-- --
-- test of GUI library -- test of GUI library
-- --
run("libs/gui.lua") run("libs/gui.lua")
local counter = gui.label{x=10, y=54, value=0, format="% .3d"} local counter = gui.label{x=10, y=54, value=0, format="% .3d"}
local form = gui.dialog{ local form = gui.dialog{
title="Dialogtest", title="Dialogtest",
w=100, w=100,
h=150, h=150,
counter, counter,
gui.button{ label="+", gui.button{ label="+",
x=6, y=38, w=14, h=14, repeatable=true, click=function() x=6, y=38, w=14, h=14, repeatable=true, click=function()
counter.value=counter.value+1; counter.value=counter.value+1;
end}, end},
gui.button{ label="-", gui.button{ label="-",
x=26, y=38, w=14, h=14, repeatable=true, click=function() x=26, y=38, w=14, h=14, repeatable=true, click=function()
counter.value=counter.value-1; counter.value=counter.value-1;
end}, end},
gui.button{ label="Help", gui.button{ label="Help",
x=6, y=70, w=54, h=14, click=function() x=6, y=70, w=54, h=14, click=function()
messagebox("Help screen"); messagebox("Help screen");
end}, end},
gui.button{ label="Close", gui.button{ label="Close",
x=6, y=18, w=54, h=14, key=27, click=function() x=6, y=18, w=54, h=14, key=27, click=function()
return true; -- causes closing return true; -- causes closing
end}, end},
gui.textbox{ gui.textbox{
x=6, y=90, nbchar=8, decimal=1, x=6, y=90, nbchar=8, decimal=1,
min=450, max=1450, maxchar=8, value = 1234, min=450, max=1450, maxchar=8, value = 1234,
change=function() change=function()
-- do nothing -- do nothing
end end
}, },
gui.textbox{ gui.textbox{
x=6, y=104, nbchar=10, maxchar=20, value = "test" x=6, y=104, nbchar=10, maxchar=20, value = "test"
}, },
gui.textbox{ gui.textbox{
x=6, y=118, nbchar=8, decimal=0, min=0, x=6, y=118, nbchar=8, decimal=0, min=0,
maxchar=8, value = 456, maxchar=8, value = 456,
change=function() change=function()
-- do nothing -- do nothing
end end
}, },
} }
form:run() form:run()

View File

@ -1,225 +1,225 @@
-- --
-- Event-driven GUI library -- Event-driven GUI library
-- --
-- --
gui = { gui = {
-- --
-- colors -- colors
-- --
black = 0, black = 0,
dark = 1, dark = 1,
light = 2, light = 2,
white = 3, white = 3,
-- "do nothing" function. Used as default callback -- "do nothing" function. Used as default callback
donothing = function(self) donothing = function(self)
end, end,
-- Metatable that hides the field "value" behind a property, -- Metatable that hides the field "value" behind a property,
-- and calls render() after it's set. -- and calls render() after it's set.
propvalue = { propvalue = {
__newindex = function(self, index, value) __newindex = function(self, index, value)
if index == "value" then if index == "value" then
self._value = value self._value = value
-- extra processing -- extra processing
self:render() self:render()
else else
rawset(self, index, value) rawset(self, index, value)
end end
end, end,
__index = function(self, index ) __index = function(self, index )
if index == "value" then if index == "value" then
return self._value return self._value
else else
return rawget( self, index ) return rawget( self, index )
end end
end end
}, },
-- --
-- dialog() -- dialog()
-- --
dialog = function(args) dialog = function(args)
local dia = { local dia = {
title = args.title, title = args.title,
w = args.w, w = args.w,
h = args.h, h = args.h,
-- --
widgets = {}, widgets = {},
-- an indexed array, starting at 1. Used for calling the relevant -- an indexed array, starting at 1. Used for calling the relevant
-- callback when a numbered control is clicked. -- callback when a numbered control is clicked.
callbacks = {}, callbacks = {},
-- --
-- dialog.run() -- -- dialog.run() --
-- --
run = function(self) run = function(self)
windowopen(self.w,self.h, self.title or ""); windowopen(self.w,self.h, self.title or "");
-- examine all elements -- examine all elements
for _,widget in ipairs(self.widgets) do for _,widget in ipairs(self.widgets) do
widget:create() widget:create()
end end
repeat repeat
local button, button2, key = windowdodialog(); local button, button2, key = windowdodialog();
if button > 0 then if button > 0 then
local c = self.callbacks[button] local c = self.callbacks[button]
if c ~= nil then if c ~= nil then
-- run the callback -- run the callback
local retvalue = c:click() local retvalue = c:click()
-- stop the form if it returns non-nil -- stop the form if it returns non-nil
if retvalue ~= nil then if retvalue ~= nil then
windowclose(); windowclose();
return retvalue; return retvalue;
end end
end end
end end
until key == 27; until key == 27;
windowclose(); windowclose();
end end
} }
local id = 1; local id = 1;
-- examine all elements -- examine all elements
for _,value in ipairs(args) do for _,value in ipairs(args) do
-- all arguments that are tables are assumed to be widgets -- all arguments that are tables are assumed to be widgets
if type(value)=="table" then if type(value)=="table" then
table.insert(dia.widgets, value) table.insert(dia.widgets, value)
-- clickable widgets take up an auto-numbered id -- clickable widgets take up an auto-numbered id
if (value.click) then if (value.click) then
dia.callbacks[id] = value dia.callbacks[id] = value
id=id+1 id=id+1
end end
end end
end end
return dia; return dia;
end, end,
-- --
-- button() -- button()
-- --
button = function(args) button = function(args)
local but = { local but = {
x = args.x, x = args.x,
y = args.y, y = args.y,
w = args.w, w = args.w,
h = args.h, h = args.h,
key = args.key, key = args.key,
label = args.label, label = args.label,
click = args.click or gui.donothing, click = args.click or gui.donothing,
create = args.repeatable and function(self) create = args.repeatable and function(self)
windowrepeatbutton(self.x, self.y, self.w, self.h, self.label, self.key or -1); windowrepeatbutton(self.x, self.y, self.w, self.h, self.label, self.key or -1);
end end
or function(self) or function(self)
windowbutton(self.x, self.y, self.w, self.h, self.label, self.key or -1); windowbutton(self.x, self.y, self.w, self.h, self.label, self.key or -1);
end end
} }
return but; return but;
end, end,
-- --
-- label() -- label()
-- --
label = function(args) label = function(args)
local lbl = { local lbl = {
x = args.x, x = args.x,
y = args.y, y = args.y,
_value = args.value, _value = args.value,
format = args.format, format = args.format,
fg = args.fg or gui.black, fg = args.fg or gui.black,
bg = args.bg or gui.light, bg = args.bg or gui.light,
render = function(self) render = function(self)
if type(self.format) then if type(self.format) then
windowprint(self.x, self.y, string.format(self.format, self._value), self.fg, self.bg); windowprint(self.x, self.y, string.format(self.format, self._value), self.fg, self.bg);
else else
windowprint(self.x, self.y, self._value, self.fg, self.bg); windowprint(self.x, self.y, self._value, self.fg, self.bg);
end end
end, end,
} }
lbl.create = lbl.render lbl.create = lbl.render
setmetatable(lbl, gui.propvalue) setmetatable(lbl, gui.propvalue)
return lbl; return lbl;
end, end,
-- --
-- textbox -- textbox
-- --
textbox = function(args) textbox = function(args)
local txtbox = { local txtbox = {
x = args.x, x = args.x,
y = args.y, y = args.y,
nbchar = args.nbchar, -- visible size in characters nbchar = args.nbchar, -- visible size in characters
--format = args.format, -- numeric, decimal, path --format = args.format, -- numeric, decimal, path
decimal = args.decimal or 0, decimal = args.decimal or 0,
min = args.min, min = args.min,
max = args.max, max = args.max,
maxchar = args.maxchar, -- internal size maxchar = args.maxchar, -- internal size
_value = args.value, _value = args.value,
change = args.change or gui.donothing, change = args.change or gui.donothing,
--fg = args.fg or gui.black, --fg = args.fg or gui.black,
--bg = args.bg or gui.light, --bg = args.bg or gui.light,
create = function(self) create = function(self)
windowinput(self.x, self.y, self.nbchar) windowinput(self.x, self.y, self.nbchar)
self:render() self:render()
end, end,
render = function(self) render = function(self)
local val = tostring(self._value) local val = tostring(self._value)
if string.len(val) < self.nbchar then if string.len(val) < self.nbchar then
val = string.rep(" ",self.nbchar - string.len(val)) .. val; val = string.rep(" ",self.nbchar - string.len(val)) .. val;
elseif string.len(val) > self.nbchar then elseif string.len(val) > self.nbchar then
val = string.sub(val, 1, self.nbchar-1) .. gui.char.ellipsis val = string.sub(val, 1, self.nbchar-1) .. gui.char.ellipsis
end end
windowprint(self.x, self.y, val, gui.black, gui.light); windowprint(self.x, self.y, val, gui.black, gui.light);
end, end,
click = function(self) click = function(self)
local inputtype local inputtype
if (type(self._value) == "number" and ((self.min ~= nil and self.min<0) or self.decimal > 0)) then if (type(self._value) == "number" and ((self.min ~= nil and self.min<0) or self.decimal > 0)) then
inputtype = 3 -- entry as double inputtype = 3 -- entry as double
elseif (type(self._value) == "number") then elseif (type(self._value) == "number") then
inputtype = 1 -- entry as unsigned int inputtype = 1 -- entry as unsigned int
else else
inputtype = 0 -- entry as string inputtype = 0 -- entry as string
end end
local accept, val = windowreadline(self.x, self.y, self._value, self.nbchar, self.maxchar, self.decimal, inputtype); local accept, val = windowreadline(self.x, self.y, self._value, self.nbchar, self.maxchar, self.decimal, inputtype);
if accept then if accept then
if (inputtype == 1 or inputtype == 3) then if (inputtype == 1 or inputtype == 3) then
val = tonumber(val) val = tonumber(val)
-- round the decimal places -- round the decimal places
val = gui.round(val, self.decimal) val = gui.round(val, self.decimal)
end end
if (self.min ~= nil and val < self.min) then if (self.min ~= nil and val < self.min) then
val = self.min val = self.min
end end
if (self.max ~= nil and val > self.max) then if (self.max ~= nil and val > self.max) then
val = self.max val = self.max
end end
self._value = val self._value = val
end end
self:render() self:render()
end end
} }
setmetatable(txtbox, gui.propvalue) setmetatable(txtbox, gui.propvalue)
return txtbox; return txtbox;
end end
} }
gui.round = function(val, ipt) gui.round = function(val, ipt)
local mult = 10^ipt local mult = 10^ipt
return math.floor(val * mult + 0.5) / mult return math.floor(val * mult + 0.5) / mult
end end
-- Character constants. May be useful in screens -- Character constants. May be useful in screens
gui.char = { gui.char = {
ellipsis = string.char(133), -- ... ellipsis = string.char(133), -- ...
arrowup = string.char(24), arrowup = string.char(24),
arrowdown = string.char(25), arrowdown = string.char(25),
arrowleft = string.char(27), arrowleft = string.char(27),
arrowright = string.char(26), arrowright = string.char(26),
vertical = string.char(18), -- double-ended arrow vertical = string.char(18), -- double-ended arrow
horizontal = string.char(29) -- double-ended arrow horizontal = string.char(29) -- double-ended arrow
} }

View File

@ -1,46 +1,45 @@
--ANIM: Sprite Animator v0.15 --ANIM: Sprite Animator v0.15
--Spare page holds data - Plays on current --Spare page holds data - Plays on current
--by Richard Fhager --by Richard Fhager
run("../libs/memory.lua") run("../libs/memory.lua")
arg=memory.load({XS=16,YS=16,SPACE=1,FRAMES=8,XOFF=0,YOFF=0,FPS=10}) arg=memory.load({XS=16,YS=16,SPACE=1,FRAMES=8,XOFF=0,YOFF=0,FPS=10})
OK, XS, YS, SPACE, FRAMES, XOFF, YOFF, FPS = inputbox("Sprite-Sheet Animator", OK, XS, YS, SPACE, FRAMES, XOFF, YOFF, FPS = inputbox("Sprite-Sheet Animator",
"Sprite X-size", arg.XS, 1, 256,0, "Sprite X-size", arg.XS, 1, 256,0,
"Sprite Y-size", arg.YS, 1, 256,0, "Sprite Y-size", arg.YS, 1, 256,0,
"Spacing", arg.SPACE, 0, 32,0, "Spacing", arg.SPACE, 0, 32,0,
"# of Frames", arg.FRAMES,2, 100,0, "# of Frames", arg.FRAMES,2, 100,0,
"X-offset", arg.XOFF, 0, 800,0, "X-offset", arg.XOFF, 0, 800,0,
"Y-offset", arg.YOFF, 0, 800,0, "Y-offset", arg.YOFF, 0, 800,0,
"Play Speed (FPS)",arg.FPS, 1, 60,0 "Play Speed (FPS)",arg.FPS, 1, 60,0
); );
if OK == true then if OK == true then
memory.save({XS=XS,YS=YS,SPACE=SPACE,FRAMES=FRAMES,XOFF=XOFF,YOFF=YOFF,FPS=FPS}) memory.save({XS=XS,YS=YS,SPACE=SPACE,FRAMES=FRAMES,XOFF=XOFF,YOFF=YOFF,FPS=FPS})
MAXPLAYS = 100 MAXPLAYS = 100
w,h = getpicturesize() w,h = getpicturesize()
OX = w / 2 - XS/2 OX = w / 2 - XS/2
OY = h / 2 - YS/2 OY = h / 2 - YS/2
for play = 1, MAXPLAYS, 1 do for play = 1, MAXPLAYS, 1 do
for f = 0, FRAMES-1, 1 do for f = 0, FRAMES-1, 1 do
for y = 0, YS-1, 1 do for y = 0, YS-1, 1 do
for x = 0, XS-1, 1 do for x = 0, XS-1, 1 do
sx = x + XOFF + f * (XS + SPACE) sx = x + XOFF + f * (XS + SPACE)
sy = y + YOFF sy = y + YOFF
putpicturepixel(OX+x, OY+y, getsparepicturepixel(sx, sy)) putpicturepixel(OX+x, OY+y, getsparepicturepixel(sx, sy))
end end
end end
updatescreen(); if (waitbreak(1/FPS)==1) then return; end updatescreen(); if (waitbreak(1/FPS)==1) then return; end
end end
end -- plays end -- plays
end --OK end --OK

View File

@ -1,138 +1,138 @@
-- Persistence library: -- Persistence library:
-- Memorize data for current function -- Memorize data for current function
-- memory.save(tab) and tab=memory.load() -- memory.save(tab) and tab=memory.load()
-- --
-- The data will be stored in file called -- The data will be stored in file called
-- <calling_function_name>.dat -- <calling_function_name>.dat
-- in the lua directory -- in the lua directory
-- --
-- Example 1: -- Example 1:
-- --
-- -- Load initial values or set defaults -- -- Load initial values or set defaults
-- arg = memory.load({picX=320,picY=200,scale=0}) -- arg = memory.load({picX=320,picY=200,scale=0})
-- -- Run an inputbox -- -- Run an inputbox
-- OK,arg.picX,arg.picY,arg.scale = inputbox("Image Size")", -- OK,arg.picX,arg.picY,arg.scale = inputbox("Image Size")",
-- "Width", arg.picX, 1,2048,0, -- "Width", arg.picX, 1,2048,0,
-- "Height", arg.picY, 1,2048,0, -- "Height", arg.picY, 1,2048,0,
-- "Scale", arg.scale, 0,1,0); -- "Scale", arg.scale, 0,1,0);
-- if OK == true then -- if OK == true then
-- -- Save the selected values -- -- Save the selected values
-- memory.save(arg) -- memory.save(arg)
-- end -- end
-- Example 2: -- Example 2:
-- --
-- -- Load initial values or set defaults -- -- Load initial values or set defaults
-- arg = memory.load({x=320,y=200,scale=0}) -- arg = memory.load({x=320,y=200,scale=0})
-- picX=arg.x -- picX=arg.x
-- picY=arg.y -- picY=arg.y
-- scale=arg.scale -- scale=arg.scale
-- -- Run an inputbox -- -- Run an inputbox
-- OK,picX,picY,scale = inputbox("Image Size")", -- OK,picX,picY,scale = inputbox("Image Size")",
-- "Width", picX, 1,2048,0, -- "Width", picX, 1,2048,0,
-- "Height", picY, 1,2048,0, -- "Height", picY, 1,2048,0,
-- "Scale", scale, 0,1,0); -- "Scale", scale, 0,1,0);
-- if OK == true then -- if OK == true then
-- -- Save the selected values -- -- Save the selected values
-- memory.save({x=picX,y=picY,scale=scale}) -- memory.save({x=picX,y=picY,scale=scale})
-- end -- end
memory = memory =
{ {
serialize = function(o) serialize = function(o)
if type(o) == "number" then if type(o) == "number" then
return tostring(o) return tostring(o)
elseif type(o) == "string" then elseif type(o) == "string" then
return string.format("%q", o) return string.format("%q", o)
--elseif type(o) == "table" then --elseif type(o) == "table" then
-- io.write("{\n") -- io.write("{\n")
-- for k,v in pairs(o) do -- for k,v in pairs(o) do
-- io.write(" ", k, " = ") -- io.write(" ", k, " = ")
-- memory.serialize(v) -- memory.serialize(v)
-- io.write(",\n") -- io.write(",\n")
-- end -- end
-- io.write("}\n") -- io.write("}\n")
else else
error("cannot serialize a " .. type(o)) error("cannot serialize a " .. type(o))
end end
end; end;
-- Return a string identifying the calling function. -- Return a string identifying the calling function.
-- Pass 1 for parent, 2 for grandparent etc. -- Pass 1 for parent, 2 for grandparent etc.
callername = function(level) callername = function(level)
local w local w
local last_slash local last_slash
local info = debug.getinfo(level+1,"Sn") local info = debug.getinfo(level+1,"Sn")
local caller=tostring(info.name) local caller=tostring(info.name)
-- Function name if possible -- Function name if possible
if (caller~="nil") then if (caller~="nil") then
return caller return caller
end end
-- Otherwise, get file name, without extension -- Otherwise, get file name, without extension
-- Get part after directory name -- Get part after directory name
last_slash=0 last_slash=0
while true do while true do
local pos = string.find(info.source, "/", last_slash+1) local pos = string.find(info.source, "/", last_slash+1)
if (pos==nil) then break end if (pos==nil) then break end
last_slash=pos last_slash=pos
end end
while true do while true do
local pos = string.find(info.source, "\\", last_slash+1) local pos = string.find(info.source, "\\", last_slash+1)
if (pos==nil) then break end if (pos==nil) then break end
last_slash=pos last_slash=pos
end end
caller=string.sub(info.source, last_slash+1) caller=string.sub(info.source, last_slash+1)
-- Remove file extension -- Remove file extension
if (string.sub(caller,-4, -1)==".lua") then if (string.sub(caller,-4, -1)==".lua") then
caller=string.sub(caller, 1, -5) caller=string.sub(caller, 1, -5)
end end
return caller return caller
end; end;
-- Memorize some parameters. -- Memorize some parameters.
save = function(o) save = function(o)
local caller=memory.callername(2) local caller=memory.callername(2)
--for k, v in pairs(o) do --for k, v in pairs(o) do
-- messagebox(tostring(k)) -- messagebox(tostring(k))
-- messagebox(tostring(v)) -- messagebox(tostring(v))
--end --end
local f, e = io.open(caller..".dat", "w"); local f, e = io.open(caller..".dat", "w");
if (f ~= nil) then if (f ~= nil) then
f:write("Entry {\n") f:write("Entry {\n")
for k, v in pairs(o) do for k, v in pairs(o) do
if (type(v)=="number") then if (type(v)=="number") then
f:write(" "..k.."="..memory["serialize"](v)..",\n") f:write(" "..k.."="..memory["serialize"](v)..",\n")
end end
end end
f:write("}\n") f:write("}\n")
f:close() f:close()
end end
end; end;
-- Recover some saved parameters. -- Recover some saved parameters.
load = function(o) load = function(o)
local caller=memory.callername(2) local caller=memory.callername(2)
local i local i
function Entry (b) function Entry (b)
-- Adds (or replaces) values in arg with those from b -- Adds (or replaces) values in arg with those from b
for k, v in pairs(b) do for k, v in pairs(b) do
o[k]=v o[k]=v
end end
end end
local f = (loadfile(caller..".dat")) local f = (loadfile(caller..".dat"))
if (f ~= nil) then if (f ~= nil) then
f() f()
end end
return o return o
end; end;
} }
return memory return memory

View File

@ -1,141 +1,141 @@
-- ostro_zx.lua : converts a color image into a -- ostro_zx.lua : converts a color image into a
-- ZX-like image (8+8 fixed colors with color clash) -- ZX-like image (8+8 fixed colors with color clash)
-- using Ostromoukhov's error diffusion algorithm. -- using Ostromoukhov's error diffusion algorithm.
-- --
-- Version: 03/21/2017 -- Version: 03/21/2017
-- --
-- Copyright 2016-2017 by Samuel Devulder -- Copyright 2016-2017 by Samuel Devulder
-- --
-- This program is free software; you can redistribute -- This program is free software; you can redistribute
-- it and/or modify it under the terms of the GNU -- it and/or modify it under the terms of the GNU
-- General Public License as published by the Free -- General Public License as published by the Free
-- Software Foundation; version 2 of the License. -- Software Foundation; version 2 of the License.
-- See <http://www.gnu.org/licenses/> -- See <http://www.gnu.org/licenses/>
run('../../thomson/lib/ostromoukhov.lua') run('../../thomson/lib/ostromoukhov.lua')
-- get screen size -- get screen size
local screen_w, screen_h = getpicturesize() local screen_w, screen_h = getpicturesize()
OtherDither = {} OtherDither = {}
function OtherDither:new(a) function OtherDither:new(a)
local o = { -- default ZX values local o = { -- default ZX values
-- width of the screen -- width of the screen
width=a and a.width or 256, width=a and a.width or 256,
-- height of the screen -- height of the screen
height=a and a.height or 192, height=a and a.height or 192,
-- size of Nx1 clash size -- size of Nx1 clash size
clash_size=a and a.clash_size or 8, clash_size=a and a.clash_size or 8,
-- normalize the picture levels (like in imagemagick) -- normalize the picture levels (like in imagemagick)
normalize=a and a.normalize or 0.005, normalize=a and a.normalize or 0.005,
-- put a pixel -- put a pixel
pset=a and a.pset or function(self,x,y,c) pset=a and a.pset or function(self,x,y,c)
if c<0 then c=-c-1 end if c<0 then c=-c-1 end
self.screen[x+y*self.width] = c self.screen[x+y*self.width] = c
end, end,
-- init gfx data -- init gfx data
setGfx=a and a.setGfx or function(self) setGfx=a and a.setGfx or function(self)
self.screen={} self.screen={}
end, end,
-- update gfx to screen -- update gfx to screen
updatescreen=a and a.updatescreen or function(self) updatescreen=a and a.updatescreen or function(self)
for i=0,255 do setcolor(i,0,0,0) end for i=0,255 do setcolor(i,0,0,0) end
for y=0,self.height-1 do for y=0,self.height-1 do
for x=0,self.width-1 do for x=0,self.width-1 do
putpicturepixel(x,y,self.screen[x+y*self.width] or 0) putpicturepixel(x,y,self.screen[x+y*self.width] or 0)
end end
end end
-- refresh palette -- refresh palette
for i,v in ipairs(self.pal) do for i,v in ipairs(self.pal) do
local r=v % 16 local r=v % 16
local g=math.floor(v/16) % 16 local g=math.floor(v/16) % 16
local b=math.floor(v/256) % 16 local b=math.floor(v/256) % 16
setcolor(i+thomson._palette.offset-1, setcolor(i+thomson._palette.offset-1,
thomson.levels.pc[r+1], thomson.levels.pc[r+1],
thomson.levels.pc[g+1], thomson.levels.pc[g+1],
thomson.levels.pc[b+1]) thomson.levels.pc[b+1])
end end
updatescreen() updatescreen()
end, end,
-- palette with thomson ordering (to use thomson's -- palette with thomson ordering (to use thomson's
-- lib support) -- lib support)
pal= a and a.pal or { pal= a and a.pal or {
0x000,0xF00,0x00F,0xF0F,0x0F0,0xFF0,0x0FF,0xFFF, 0x000,0xF00,0x00F,0xF0F,0x0F0,0xFF0,0x0FF,0xFFF,
0x000,0x200,0x002,0x202,0x020,0x220,0x022,0x222 0x000,0x200,0x002,0x202,0x020,0x220,0x022,0x222
} }
} }
setmetatable(o, self) setmetatable(o, self)
self.__index = self self.__index = self
return o return o
end end
-- Converts ZX coordinates (0-255,0-191) into screen coordinates -- Converts ZX coordinates (0-255,0-191) into screen coordinates
function OtherDither:to_screen(x,y) function OtherDither:to_screen(x,y)
local i,j; local i,j;
if screen_w/screen_h < self.width/self.height then if screen_w/screen_h < self.width/self.height then
i = x*screen_h/self.height i = x*screen_h/self.height
j = y*screen_h/self.height j = y*screen_h/self.height
else else
i = x*screen_w/self.width i = x*screen_w/self.width
j = y*screen_w/self.width j = y*screen_w/self.width
end end
return math.floor(i), math.floor(j) return math.floor(i), math.floor(j)
end end
-- return the Color @(x,y) in linear space (0-255) -- return the Color @(x,y) in linear space (0-255)
-- corresonding to the other platform screen -- corresonding to the other platform screen
OtherDither._getLinearPixel = {} -- cache OtherDither._getLinearPixel = {} -- cache
function OtherDither:getLinearPixel(x,y) function OtherDither:getLinearPixel(x,y)
local k=x+y*self.width local k=x+y*self.width
local p = self._getLinearPixel and self._getLinearPixel[k] local p = self._getLinearPixel and self._getLinearPixel[k]
if not p then if not p then
local x1,y1 = self:to_screen(x,y) local x1,y1 = self:to_screen(x,y)
local x2,y2 = self:to_screen(x+1,y+1) local x2,y2 = self:to_screen(x+1,y+1)
if x2==x1 then x2=x1+1 end if x2==x1 then x2=x1+1 end
if y2==y1 then y2=y1+1 end if y2==y1 then y2=y1+1 end
p = Color:new(0,0,0) p = Color:new(0,0,0)
for j=y1,y2-1 do for j=y1,y2-1 do
for i=x1,x2-1 do for i=x1,x2-1 do
p:add(getLinearPictureColor(i,j)) p:add(getLinearPictureColor(i,j))
end end
end end
p:div((y2-y1)*(x2-x1)) --:floor() p:div((y2-y1)*(x2-x1)) --:floor()
if self._getLinearPixel then if self._getLinearPixel then
self._getLinearPixel[k]=p self._getLinearPixel[k]=p
end end
end end
return self._getLinearPixel and p:clone() or p return self._getLinearPixel and p:clone() or p
end end
function OtherDither:ccAcceptCouple(c1,c2) function OtherDither:ccAcceptCouple(c1,c2)
-- bright colors can't mix with dimmed ones -- bright colors can't mix with dimmed ones
return c1~=c2 and ((c1<=8 and c2<=8) or (c1>8 and c2>8)) return c1~=c2 and ((c1<=8 and c2<=8) or (c1>8 and c2>8))
end end
function OtherDither:dither() function OtherDither:dither()
local NORMALIZE=Color.NORMALIZE local NORMALIZE=Color.NORMALIZE
Color.NORMALIZE=self.normalize Color.NORMALIZE=self.normalize
local dither=OstroDither:new(self.pal) local dither=OstroDither:new(self.pal)
dither.ccAcceptCouple = function(dither,c1,c2) return self:ccAcceptCouple(c1,c2) end dither.ccAcceptCouple = function(dither,c1,c2) return self:ccAcceptCouple(c1,c2) end
dither.clash_size = self.clash_size dither.clash_size = self.clash_size
dither.attenuation = .9 dither.attenuation = .9
self:setGfx() self:setGfx()
dither:ccDither(self.width,self.height, dither:ccDither(self.width,self.height,
function(x,y) return self:getLinearPixel(x,y) end, function(x,y) return self:getLinearPixel(x,y) end,
function(x,y,c) self:pset(x,y,c) end, function(x,y,c) self:pset(x,y,c) end,
true, true,
function(y) function(y)
thomson.info("Converting...", thomson.info("Converting...",
math.floor(y*100/self.height),"%") math.floor(y*100/self.height),"%")
end,true) end,true)
-- refresh screen -- refresh screen
setpicturesize(self.width,self.height) setpicturesize(self.width,self.height)
self:updatescreen() self:updatescreen()
finalizepicture() finalizepicture()
Color.NORMALIZE=NORMALIZE Color.NORMALIZE=NORMALIZE
end end

View File

@ -1,27 +1,27 @@
-- ostro_zx.lua : converts a color image into a -- ostro_zx.lua : converts a color image into a
-- Oric image (8+8 fixed colors with color clash) -- Oric image (8+8 fixed colors with color clash)
-- using Ostromoukhov's error diffusion algorithm. -- using Ostromoukhov's error diffusion algorithm.
-- --
-- Version: 03/21/2017 -- Version: 03/21/2017
-- --
-- Copyright 2016-2017 by Samuel Devulder -- Copyright 2016-2017 by Samuel Devulder
-- --
-- This program is free software; you can redistribute -- This program is free software; you can redistribute
-- it and/or modify it under the terms of the GNU -- it and/or modify it under the terms of the GNU
-- General Public License as published by the Free -- General Public License as published by the Free
-- Software Foundation; version 2 of the License. -- Software Foundation; version 2 of the License.
-- See <http://www.gnu.org/licenses/> -- See <http://www.gnu.org/licenses/>
run('lib/ostro_other.lua') run('lib/ostro_other.lua')
OtherDither:new{ OtherDither:new{
width=240, width=240,
height=200, height=200,
clash_size=6, clash_size=6,
pal={0x000,0x00F,0x0F0,0x0FF,0xF00,0xF0F,0xFF0,0xFFF}, pal={0x000,0x00F,0x0F0,0x0FF,0xF00,0xF0F,0xFF0,0xFFF},
pset=function(self,x,y,c) pset=function(self,x,y,c)
if x<6 then c=0 end if x<6 then c=0 end
if c<0 then c=-c-1 end if c<0 then c=-c-1 end
self.screen[x+y*self.width] = c self.screen[x+y*self.width] = c
end end
}:dither() }:dither()

View File

@ -1,17 +1,17 @@
-- ostro_zx.lua : converts a color image into a -- ostro_zx.lua : converts a color image into a
-- ZX image (8+8 fixed colors with color clash) -- ZX image (8+8 fixed colors with color clash)
-- using Ostromoukhov's error diffusion algorithm. -- using Ostromoukhov's error diffusion algorithm.
-- --
-- Version: 03/21/2017 -- Version: 03/21/2017
-- --
-- Copyright 2016-2017 by Samuel Devulder -- Copyright 2016-2017 by Samuel Devulder
-- --
-- This program is free software; you can redistribute -- This program is free software; you can redistribute
-- it and/or modify it under the terms of the GNU -- it and/or modify it under the terms of the GNU
-- General Public License as published by the Free -- General Public License as published by the Free
-- Software Foundation; version 2 of the License. -- Software Foundation; version 2 of the License.
-- See <http://www.gnu.org/licenses/> -- See <http://www.gnu.org/licenses/>
run('lib/ostro_other.lua') run('lib/ostro_other.lua')
OtherDither:new{width=256,height=192,clash_size=8}:dither() OtherDither:new{width=256,height=192,clash_size=8}:dither()

View File

@ -1,220 +1,219 @@
-- bayer4_mo5.lua : converts an image into TO7/70-MO5 -- bayer4_mo5.lua : converts an image into TO7/70-MO5
-- mode for thomson machines (MO6,TO8,TO9,TO9+) -- mode for thomson machines (MO6,TO8,TO9,TO9+)
-- using special bayer matrix that fits well with -- using special bayer matrix that fits well with
-- color clashes. -- color clashes.
-- --
-- Version: 02-jan-2017 -- Version: 02-jan-2017
-- --
-- Copyright 2016-2017 by Samuel Devulder -- Copyright 2016-2017 by Samuel Devulder
-- --
-- This program is free software; you can redistribute -- This program is free software; you can redistribute
-- it and/or modify it under the terms of the GNU -- it and/or modify it under the terms of the GNU
-- General Public License as published by the Free -- General Public License as published by the Free
-- Software Foundation; version 2 of the License. -- Software Foundation; version 2 of the License.
-- See <http://www.gnu.org/licenses/> -- See <http://www.gnu.org/licenses/>
-- get screen size -- get screen size
local screen_w, screen_h = getpicturesize() local screen_w, screen_h = getpicturesize()
run("lib/thomson.lua") run("lib/thomson.lua")
run("lib/color.lua") run("lib/color.lua")
run("lib/bayer.lua") run("lib/bayer.lua")
-- Converts thomson coordinates (0-319,0-199) into screen coordinates -- Converts thomson coordinates (0-319,0-199) into screen coordinates
local function thom2screen(x,y) local function thom2screen(x,y)
local i,j; local i,j;
if screen_w/screen_h < 1.6 then if screen_w/screen_h < 1.6 then
i = x*screen_h/200 i = x*screen_h/200
j = y*screen_h/200 j = y*screen_h/200
else else
i = x*screen_w/320 i = x*screen_w/320
j = y*screen_w/320 j = y*screen_w/320
end end
return math.floor(i), math.floor(j) return math.floor(i), math.floor(j)
end end
-- return the pixel @(x,y) in normalized linear space (0-1) -- return the pixel @(x,y) in normalized linear space (0-1)
-- corresonding to the thomson screen (x in 0-319, y in 0-199) -- corresonding to the thomson screen (x in 0-319, y in 0-199)
local function getLinearPixel(x,y) local function getLinearPixel(x,y)
local x1,y1 = thom2screen(x,y) local x1,y1 = thom2screen(x,y)
local x2,y2 = thom2screen(x+1,y+1) local x2,y2 = thom2screen(x+1,y+1)
if x2==x1 then x2=x1+1 end if x2==x1 then x2=x1+1 end
if y2==y1 then y2=y1+1 end if y2==y1 then y2=y1+1 end
local p,i,j = Color:new(0,0,0); local p,i,j = Color:new(0,0,0);
for i=x1,x2-1 do for i=x1,x2-1 do
for j=y1,y2-1 do for j=y1,y2-1 do
p:add(getLinearPictureColor(i,j)) p:add(getLinearPictureColor(i,j))
end end
end end
p:div((y2-y1)*(x2-x1)*Color.ONE) p:div((y2-y1)*(x2-x1)*Color.ONE)
return p return p
end end
local dither = bayer.norm(bayer.double(bayer.double({{1,2},{3,4}}))) local dither = bayer.norm(bayer.double(bayer.double({{1,2},{3,4}})))
local dx,dy=#dither,#dither[1] local dx,dy=#dither,#dither[1]
-- get thomson palette pixel (linear, 0-1 range) -- get thomson palette pixel (linear, 0-1 range)
local linearPalette = {} local linearPalette = {}
function linearPalette.get(i) function linearPalette.get(i)
local p = linearPalette[i] local p = linearPalette[i]
if not p then if not p then
local pal = thomson.palette(i-1) local pal = thomson.palette(i-1)
local b=math.floor(pal/256) local b=math.floor(pal/256)
local g=math.floor(pal/16)%16 local g=math.floor(pal/16)%16
local r=pal%16 local r=pal%16
p = Color:new(thomson.levels.linear[r+1], p = Color:new(thomson.levels.linear[r+1],
thomson.levels.linear[g+1], thomson.levels.linear[g+1],
thomson.levels.linear[b+1]):div(Color.ONE) thomson.levels.linear[b+1]):div(Color.ONE)
linearPalette[i] = p linearPalette[i] = p
end end
return p:clone() return p:clone()
end end
-- distance between two colors -- distance between two colors
local distance = {} local distance = {}
function distance.between(c1,c2) function distance.between(c1,c2)
local k = c1..','..c2 local k = c1..','..c2
local d = distance[k] local d = distance[k]
if false and not d then if false and not d then
d = linearPalette.get(c1):euclid_dist2(linearPalette.get(c2)) d = linearPalette.get(c1):euclid_dist2(linearPalette.get(c2))
distance[k] = d distance[k] = d
end end
if not d then if not d then
local x = linearPalette.get(c1):sub(linearPalette.get(c2)) local x = linearPalette.get(c1):sub(linearPalette.get(c2))
local c,c1,c2,c3=1.8,8,11,8 local c,c1,c2,c3=1.8,8,11,8
local f = function(c,x) return math.abs(x)*c end local f = function(c,x) return math.abs(x)*c end
d = f(c1,x.r)^c + f(c2,x.g)^c + f(c3,x.b)^c d = f(c1,x.r)^c + f(c2,x.g)^c + f(c3,x.b)^c
distance[k] = d distance[k] = d
end end
return d return d
end end
-- compute a set of best couples for a given histogram -- compute a set of best couples for a given histogram
local best_couple = {n=0} local best_couple = {n=0}
function best_couple.get(h) function best_couple.get(h)
local k = (((h[1]or 0)*8+(h[2]or 0))*8+(h[3]or 0))*8+(h[4]or 0) local k = (((h[1]or 0)*8+(h[2]or 0))*8+(h[3]or 0))*8+(h[4]or 0)
.. ',' .. (((h[5]or 0)*8+(h[6]or 0))*8+(h[7]or 0))*8+(h[8]or 0) .. ',' .. (((h[5]or 0)*8+(h[6]or 0))*8+(h[7]or 0))*8+(h[8]or 0)
local best_found = best_couple[k] local best_found = best_couple[k]
if not best_found then if not best_found then
local dm=1000000 local dm=1000000
for i=1,15 do for i=1,15 do
for j=i+1,16 do for j=i+1,16 do
local d=0 local d=0
for p,n in pairs(h) do for p,n in pairs(h) do
local d1,d2=distance.between(p,i),distance.between(p,j) local d1,d2=distance.between(p,i),distance.between(p,j)
d = d + n*(d1<d2 and d1 or d2) d = d + n*(d1<d2 and d1 or d2)
if d>dm then break; end if d>dm then break; end
end end
if d< dm then dm,best_found=d,{} end if d< dm then dm,best_found=d,{} end
if d<=dm then table.insert(best_found, {c1=i,c2=j}) end if d<=dm then table.insert(best_found, {c1=i,c2=j}) end
end end
end end
if best_couple.n>10000 then if best_couple.n>10000 then
-- keep memory usage low -- keep memory usage low
best_couple = {n=0, get=best_couple.get} best_couple = {n=0, get=best_couple.get}
end end
best_couple[k] = best_found best_couple[k] = best_found
best_couple.n = best_couple.n+1 best_couple.n = best_couple.n+1
end end
return best_found return best_found
end end
-- TO7/70 MO5 mode -- TO7/70 MO5 mode
thomson.setMO5() thomson.setMO5()
-- convert picture -- convert picture
local err1,err2 = {},{} local err1,err2 = {},{}
local coefs = {0,0.6,0} local coefs = {0,0.6,0}
for x=-1,320 do for x=-1,320 do
err1[x] = Color:new(0,0,0) err1[x] = Color:new(0,0,0)
err2[x] = Color:new(0,0,0) err2[x] = Color:new(0,0,0)
end end
for y = 0,199 do for y = 0,199 do
err1,err2 = err2,err1 err1,err2 = err2,err1
for x=-1,320 do err2[x]:mul(0) end for x=-1,320 do err2[x]:mul(0) end
for x = 0,319,8 do for x = 0,319,8 do
local h,q = {},{} -- histo, expected color local h,q = {},{} -- histo, expected color
for z=x,x+7 do for z=x,x+7 do
local d=dither[1+(y%dx)][1+(z%dx)] local d=dither[1+(y%dx)][1+(z%dx)]
local p=getLinearPixel(z,y):add(err1[z]) local p=getLinearPixel(z,y):add(err1[z])
local c=((p.r>d) and 1 or 0) + local c=((p.r>d) and 1 or 0) +
((p.g>d) and 2 or 0) + ((p.g>d) and 2 or 0) +
((p.b>d) and 4 or 0) + 1 -- theorical color ((p.b>d) and 4 or 0) + 1 -- theorical color
table.insert(q,c) table.insert(q,c)
h[c] = (h[c] or 0)+1 h[c] = (h[c] or 0)+1
end end
local c1,c2 local c1,c2
for c,_ in pairs(h) do for c,_ in pairs(h) do
if c1==nil then c1=c if c1==nil then c1=c
elseif c2==nil then c2=c elseif c2==nil then c2=c
else c1=nil; break; end else c1=nil; break; end
end end
if c1~=nil then if c1~=nil then
c2 = c2 or c1 c2 = c2 or c1
else else
-- get best possible couples of colors -- get best possible couples of colors
local best_found = best_couple.get(h) local best_found = best_couple.get(h)
if #best_found==1 then if #best_found==1 then
c1,c2 = best_found[1].c1,best_found[1].c2 c1,c2 = best_found[1].c1,best_found[1].c2
else else
-- keep the best of the best depending on max solvable color clashes -- keep the best of the best depending on max solvable color clashes
function clamp(v) return v<0 and -v or v>1 and v-1 or 0 end function clamp(v) return v<0 and -v or v>1 and v-1 or 0 end
local dm=10000000 local dm=10000000
for _,couple in ipairs(best_found) do for _,couple in ipairs(best_found) do
local d=0 local d=0
for k=1,8 do for k=1,8 do
local q=q[k] local q=q[k]
local p=distance.between(q,couple.c1)<distance.between(q,couple.c2) and couple.c1 or couple.c2 local p=distance.between(q,couple.c1)<distance.between(q,couple.c2) and couple.c1 or couple.c2
-- error between expected and best -- error between expected and best
local e=linearPalette.get(q):sub(linearPalette.get(p)):mul(coefs[1]) local e=linearPalette.get(q):sub(linearPalette.get(p)):mul(coefs[1])
local z=getLinearPixel(x+k-1,y+1):add(e) local z=getLinearPixel(x+k-1,y+1):add(e)
d = d + clamp(z.r) + clamp(z.g) + clamp(z.b) d = d + clamp(z.r) + clamp(z.g) + clamp(z.b)
end end
if d<=dm then dm,c1,c2=d,couple.c1,couple.c2 end if d<=dm then dm,c1,c2=d,couple.c1,couple.c2 end
end end
end end
end end
-- thomson.pset(x,y,c1-1) -- thomson.pset(x,y,c1-1)
-- thomson.pset(x,y,-c2) -- thomson.pset(x,y,-c2)
for k=0,7 do for k=0,7 do
local z=x+k local z=x+k
local q=q[k+1] local q=q[k+1]
local p=distance.between(q,c1)<distance.between(q,c2) and c1 or c2 local p=distance.between(q,c1)<distance.between(q,c2) and c1 or c2
local d=linearPalette.get(q):sub(linearPalette.get(p)) local d=linearPalette.get(q):sub(linearPalette.get(p))
err2[z]:add(d:mul(coefs[1])) err2[z]:add(d:mul(coefs[1]))
thomson.pset(z,y,p==c1 and c1-1 or -c2) thomson.pset(z,y,p==c1 and c1-1 or -c2)
end end
end end
thomson.info("Converting...",math.floor(y/2),"%") thomson.info("Converting...",math.floor(y/2),"%")
end end
-- refresh screen -- refresh screen
setpicturesize(320,200) setpicturesize(320,200)
thomson.updatescreen() thomson.updatescreen()
finalizepicture() finalizepicture()
-- save picture -- save picture
do do
local function exist(file) local function exist(file)
local f=io.open(file,'rb') local f=io.open(file,'rb')
if not f then return false else io.close(f); return true; end if not f then return false else io.close(f); return true; end
end end
local name,path = getfilename() local name,path = getfilename()
local mapname = string.gsub(name,"%.%w*$","") .. ".map" local mapname = string.gsub(name,"%.%w*$","") .. ".map"
local fullname = path .. '/' .. mapname local fullname = path .. '/' .. mapname
local ok = not exist(fullname) local ok = not exist(fullname)
if not ok then if not ok then
selectbox("Ovr " .. mapname .. "?", "Yes", function() ok = true; end, "No", function() ok = false; end) selectbox("Ovr " .. mapname .. "?", "Yes", function() ok = true; end, "No", function() ok = false; end)
end end
if ok then thomson.savep(fullname) end if ok then thomson.savep(fullname) end
end end

View File

@ -1,299 +1,299 @@
-- bayer4_to8.lua : converts an image into BM16 -- bayer4_to8.lua : converts an image into BM16
-- mode for thomson machines (MO6,TO8,TO9,TO9+) -- mode for thomson machines (MO6,TO8,TO9,TO9+)
-- using bayer matrix and a special palette. -- using bayer matrix and a special palette.
-- --
-- Version: 02-jan-2017 -- Version: 02-jan-2017
-- --
-- Copyright 2016-2017 by Samuel Devulder -- Copyright 2016-2017 by Samuel Devulder
-- --
-- This program is free software; you can redistribute -- This program is free software; you can redistribute
-- it and/or modify it under the terms of the GNU -- it and/or modify it under the terms of the GNU
-- General Public License as published by the Free -- General Public License as published by the Free
-- Software Foundation; version 2 of the License. -- Software Foundation; version 2 of the License.
-- See <http://www.gnu.org/licenses/> -- See <http://www.gnu.org/licenses/>
-- This is my first code in lua, so excuse any bad -- This is my first code in lua, so excuse any bad
-- coding practice. -- coding practice.
-- use a zig zag. If false (recommended value), this gives -- use a zig zag. If false (recommended value), this gives
-- a raster look and feel -- a raster look and feel
local with_zig_zag = with_zig_zag or false local with_zig_zag = with_zig_zag or false
-- debug: displays histograms -- debug: displays histograms
local debug = false local debug = false
-- enhance luminosity since our mode divide it by two -- enhance luminosity since our mode divide it by two
local enhance_lum = enhance_lum or true local enhance_lum = enhance_lum or true
-- use fixed levels (default=false, give better result) -- use fixed levels (default=false, give better result)
local fixed_levels = fixed_levels or false local fixed_levels = fixed_levels or false
-- use void-and-cluster 8x8 matrix (default=false) -- use void-and-cluster 8x8 matrix (default=false)
local use_vac = use_vac or false local use_vac = use_vac or false
-- get screen size -- get screen size
local screen_w, screen_h = getpicturesize() local screen_w, screen_h = getpicturesize()
run("lib/thomson.lua") run("lib/thomson.lua")
run("lib/color.lua") run("lib/color.lua")
run("lib/bayer.lua") run("lib/bayer.lua")
-- Converts thomson coordinates (0-159,0-99) into screen coordinates -- Converts thomson coordinates (0-159,0-99) into screen coordinates
local function thom2screen(x,y) local function thom2screen(x,y)
local i,j; local i,j;
if screen_w/screen_h < 1.6 then if screen_w/screen_h < 1.6 then
i = x*screen_h/100 i = x*screen_h/100
j = y*screen_h/100 j = y*screen_h/100
else else
i = x*screen_w/160 i = x*screen_w/160
j = y*screen_w/160 j = y*screen_w/160
end end
return math.floor(i), math.floor(j) return math.floor(i), math.floor(j)
end end
-- return the pixel @(x,y) in linear space corresonding to the thomson screen (x in 0-159, y in 0-99) -- return the pixel @(x,y) in linear space corresonding to the thomson screen (x in 0-159, y in 0-99)
local function getLinearPixel(x,y) local function getLinearPixel(x,y)
local x1,y1 = thom2screen(x,y) local x1,y1 = thom2screen(x,y)
local x2,y2 = thom2screen(x+1,y+1) local x2,y2 = thom2screen(x+1,y+1)
if x2==x1 then x2=x1+1 end if x2==x1 then x2=x1+1 end
if y2==y1 then y2=y1+1 end if y2==y1 then y2=y1+1 end
local p,i,j = Color:new(0,0,0); local p,i,j = Color:new(0,0,0);
for i=x1,x2-1 do for i=x1,x2-1 do
for j=y1,y2-1 do for j=y1,y2-1 do
p:add(getLinearPictureColor(i,j)) p:add(getLinearPictureColor(i,j))
end end
end end
return p:div((y2-y1)*(x2-x1)):floor() return p:div((y2-y1)*(x2-x1)):floor()
end end
--[[ make a bayer matrix --[[ make a bayer matrix
function bayer(matrix) function bayer(matrix)
local m,n=#matrix,#matrix[1] local m,n=#matrix,#matrix[1]
local r,i,j = {} local r,i,j = {}
for j=1,m*2 do for j=1,m*2 do
local t = {} local t = {}
for i=1,n*2 do t[i]=0; end for i=1,n*2 do t[i]=0; end
r[j] = t; r[j] = t;
end end
-- 0 3 -- 0 3
-- 2 1 -- 2 1
for j=1,m do for j=1,m do
for i=1,n do for i=1,n do
local v = 4*matrix[j][i] local v = 4*matrix[j][i]
r[m*0+j][n*0+i] = v-3 r[m*0+j][n*0+i] = v-3
r[m*1+j][n*1+i] = v-2 r[m*1+j][n*1+i] = v-2
r[m*1+j][n*0+i] = v-1 r[m*1+j][n*0+i] = v-1
r[m*0+j][n*1+i] = v-0 r[m*0+j][n*1+i] = v-0
end end
end end
return r; return r;
end end
--]] --]]
-- dither matrix -- dither matrix
local dither = bayer.make(4) local dither = bayer.make(4)
if use_vac then if use_vac then
-- vac8: looks like FS -- vac8: looks like FS
dither = bayer.norm{ dither = bayer.norm{
{35,57,19,55,7,51,4,21}, {35,57,19,55,7,51,4,21},
{29,6,41,27,37,17,59,45}, {29,6,41,27,37,17,59,45},
{61,15,53,12,62,25,33,9}, {61,15,53,12,62,25,33,9},
{23,39,31,49,2,47,13,43}, {23,39,31,49,2,47,13,43},
{3,52,8,22,36,58,20,56}, {3,52,8,22,36,58,20,56},
{38,18,60,46,30,5,42,28}, {38,18,60,46,30,5,42,28},
{63,26,34,11,64,16,54,10}, {63,26,34,11,64,16,54,10},
{14,48,1,44,24,40,32,50} {14,48,1,44,24,40,32,50}
} }
end end
-- get color statistics -- get color statistics
local stat = {}; local stat = {};
function stat:clear() function stat:clear()
self.r = {} self.r = {}
self.g = {} self.g = {}
self.b = {} self.b = {}
for i=1,16 do self.r[i] = 0; self.g[i] = 0; self.b[i] = 0; end for i=1,16 do self.r[i] = 0; self.g[i] = 0; self.b[i] = 0; end
end end
function stat:update(px) function stat:update(px)
local pc2to = thomson.levels.pc2to local pc2to = thomson.levels.pc2to
local r,g,b=pc2to[px.r], pc2to[px.g], pc2to[px.b]; local r,g,b=pc2to[px.r], pc2to[px.g], pc2to[px.b];
self.r[r] = self.r[r] + 1; self.r[r] = self.r[r] + 1;
self.g[g] = self.g[g] + 1; self.g[g] = self.g[g] + 1;
self.b[b] = self.b[b] + 1; self.b[b] = self.b[b] + 1;
end end
function stat:coversThr(perc) function stat:coversThr(perc)
local function f(stat) local function f(stat)
local t=-stat[1] local t=-stat[1]
for i,n in ipairs(stat) do t=t+n end for i,n in ipairs(stat) do t=t+n end
local thr = t*perc; t=-stat[1] local thr = t*perc; t=-stat[1]
for i,n in ipairs(stat) do for i,n in ipairs(stat) do
t=t+n t=t+n
if t>=thr then return i end if t>=thr then return i end
end end
return 0 return 0
end end
return f(self.r),f(self.g),f(self.b) return f(self.r),f(self.g),f(self.b)
end end
stat:clear(); stat:clear();
for y = 0,99 do for y = 0,99 do
for x = 0,159 do for x = 0,159 do
stat:update(getLinearPixel(x,y)) stat:update(getLinearPixel(x,y))
end end
thomson.info("Collecting stats...",y,"%") thomson.info("Collecting stats...",y,"%")
end end
-- enhance luminosity since our mode divide it by two -- enhance luminosity since our mode divide it by two
local gain = 1 local gain = 1
if enhance_lum then if enhance_lum then
-- findout level that covers 98% of all non-black pixels -- findout level that covers 98% of all non-black pixels
local max = math.max(stat:coversThr(.98)) local max = math.max(stat:coversThr(.98))
gain = math.min(2,255/thomson.levels.linear[max]) gain = math.min(2,255/thomson.levels.linear[max])
if gain>1 then if gain>1 then
-- redo stat with enhanced levels -- redo stat with enhanced levels
-- messagebox('gain '..gain..' '..table.concat({stat:coversThr(.98)},',')) -- messagebox('gain '..gain..' '..table.concat({stat:coversThr(.98)},','))
stat:clear(); stat:clear();
for y = 0,99 do for y = 0,99 do
for x = 0,159 do for x = 0,159 do
stat:update(getLinearPixel(x,y):mul(gain):floor()) stat:update(getLinearPixel(x,y):mul(gain):floor())
end end
thomson.info("Enhancing levels..",y,"%") thomson.info("Enhancing levels..",y,"%")
end end
end end
end end
-- find regularly spaced levels in thomson space -- find regularly spaced levels in thomson space
local levels = {} local levels = {}
function levels.compute(name, stat, num) function levels.compute(name, stat, num)
local tot, max = -stat[1],0; local tot, max = -stat[1],0;
for _,t in ipairs(stat) do for _,t in ipairs(stat) do
max = math.max(t,max) max = math.max(t,max)
tot = tot + t tot = tot + t
end end
local acc,full=-stat[1],0 local acc,full=-stat[1],0
for i,t in ipairs(stat) do for i,t in ipairs(stat) do
acc = acc + t acc = acc + t
if acc>tot*.98 then if acc>tot*.98 then
full=thomson.levels.linear[i] full=thomson.levels.linear[i]
break break
end end
end end
-- sanity -- sanity
if fixed_levels or full==0 then full=255 end if fixed_levels or full==0 then full=255 end
local res = {1}; num = num-1 local res = {1}; num = num-1
for i=1,num do for i=1,num do
local p = math.floor(full*i/num) local p = math.floor(full*i/num)
local q = thomson.levels.linear2to[p] local q = thomson.levels.linear2to[p]
if q==res[i] and q<16 then q=q+1 end if q==res[i] and q<16 then q=q+1 end
if not fixed_levels and i<num then if not fixed_levels and i<num then
if q>res[i]+1 and stat[q-1]>stat[q] then q=q-1 end if q>res[i]+1 and stat[q-1]>stat[q] then q=q-1 end
if q>res[i]+1 and stat[q-1]>stat[q] then q=q-1 end if q>res[i]+1 and stat[q-1]>stat[q] then q=q-1 end
-- 3 corrections? no need... -- 3 corrections? no need...
-- if q>res[i]+1 and stat[q-1]>stat[q] then q=q-1 end -- if q>res[i]+1 and stat[q-1]>stat[q] then q=q-1 end
end end
res[1+i] = q res[1+i] = q
end end
-- debug -- debug
if debug then if debug then
local txt = "" local txt = ""
for _,i in ipairs(res) do for _,i in ipairs(res) do
txt = txt .. i .. " " txt = txt .. i .. " "
end end
for i,t in ipairs(stat) do for i,t in ipairs(stat) do
txt = txt .. "\n" .. string.format("%s%2d:%3d%% ", name, i, math.floor(100*t/(tot+stat[1]))) .. string.rep('X', math.floor(23*t/max)) txt = txt .. "\n" .. string.format("%s%2d:%3d%% ", name, i, math.floor(100*t/(tot+stat[1]))) .. string.rep('X', math.floor(23*t/max))
end end
messagebox(txt) messagebox(txt)
end end
return res return res
end end
function levels.computeAll(stat) function levels.computeAll(stat)
levels.grn = levels.compute("GRN",stat.g,5) levels.grn = levels.compute("GRN",stat.g,5)
levels.red = levels.compute("RED",stat.r,4) levels.red = levels.compute("RED",stat.r,4)
levels.blu = levels.compute("BLU",stat.b,3) levels.blu = levels.compute("BLU",stat.b,3)
end end
levels.computeAll(stat) levels.computeAll(stat)
-- put a pixel at (x,y) with dithering -- put a pixel at (x,y) with dithering
local function pset(x,y,px) local function pset(x,y,px)
local thr = dither[1+(y % #dither)][1+(x % #dither[1])] local thr = dither[1+(y % #dither)][1+(x % #dither[1])]
local function dither(val,thr,lvls) local function dither(val,thr,lvls)
local i=#lvls local i=#lvls
local a,b = thomson.levels.linear[lvls[i]],1e30 local a,b = thomson.levels.linear[lvls[i]],1e30
while i>1 and val<a do while i>1 and val<a do
i=i-1; i=i-1;
a,b=thomson.levels.linear[lvls[i]],a; a,b=thomson.levels.linear[lvls[i]],a;
end end
return i + ((val-a)>=thr*(b-a) and 0 or -1) return i + ((val-a)>=thr*(b-a) and 0 or -1)
end end
local r = dither(px.r, thr, levels.red); local r = dither(px.r, thr, levels.red);
local g = dither(px.g, thr, levels.grn); local g = dither(px.g, thr, levels.grn);
local b = dither(px.b, thr, levels.blu); local b = dither(px.b, thr, levels.blu);
local i = r + b*4 local i = r + b*4
local j = g==0 and 0 or (11 + g) local j = g==0 and 0 or (11 + g)
if with_zig_zag and x%2==1 then if with_zig_zag and x%2==1 then
thomson.pset(x,y*2+0,j) thomson.pset(x,y*2+0,j)
thomson.pset(x,y*2+1,i) thomson.pset(x,y*2+1,i)
else else
thomson.pset(x,y*2+0,i) thomson.pset(x,y*2+0,i)
thomson.pset(x,y*2+1,j) thomson.pset(x,y*2+1,j)
end end
end end
-- BM16 mode -- BM16 mode
thomson.setBM16() thomson.setBM16()
-- define palette -- define palette
for i=0,15 do for i=0,15 do
local r,g,b=0,0,0 local r,g,b=0,0,0
if i<12 then if i<12 then
-- r = bit32.band(i,3) -- r = bit32.band(i,3)
-- b = bit32.rshift(i,2) -- b = bit32.rshift(i,2)
b = math.floor(i/4) b = math.floor(i/4)
r = i-4*b r = i-4*b
else else
g = i-11 g = i-11
end end
r,g,b=levels.red[r+1],levels.grn[g+1],levels.blu[b+1] r,g,b=levels.red[r+1],levels.grn[g+1],levels.blu[b+1]
thomson.palette(i,b*256+g*16+r-273) thomson.palette(i,b*256+g*16+r-273)
end end
-- convert picture -- convert picture
for y = 0,99 do for y = 0,99 do
for x = 0,159 do for x = 0,159 do
pset(x,y, getLinearPixel(x,y):mul(gain):floor()) pset(x,y, getLinearPixel(x,y):mul(gain):floor())
end end
thomson.info("Converting...",y,"%") thomson.info("Converting...",y,"%")
end end
-- refresh screen -- refresh screen
setpicturesize(320,200) setpicturesize(320,200)
thomson.updatescreen() thomson.updatescreen()
finalizepicture() finalizepicture()
-- save picture -- save picture
do do
local function exist(file) local function exist(file)
local f=io.open(file,'rb') local f=io.open(file,'rb')
if not f then return false else io.close(f); return true; end if not f then return false else io.close(f); return true; end
end end
local name,path = getfilename() local name,path = getfilename()
local mapname = string.gsub(name,"%.%w*$","") .. ".map" local mapname = string.gsub(name,"%.%w*$","") .. ".map"
local fullname = path .. '/' .. mapname local fullname = path .. '/' .. mapname
-- fullname = 'D:/tmp/toto.map' -- fullname = 'D:/tmp/toto.map'
local ok = not exist(fullname) local ok = not exist(fullname)
if not ok then if not ok then
selectbox("Ovr " .. mapname .. "?", "Yes", function() ok = true; end, "No", function() ok = false; end) selectbox("Ovr " .. mapname .. "?", "Yes", function() ok = true; end, "No", function() ok = false; end)
end end
if ok then thomson.savep(fullname) end if ok then thomson.savep(fullname) end
end end

View File

@ -1,68 +1,68 @@
-- bayer.lua : bayer matrix suppport. -- bayer.lua : bayer matrix suppport.
-- --
-- Version: 02-jan-2017 -- Version: 02-jan-2017
-- --
-- Copyright 2016-2017 by Samuel Devulder -- Copyright 2016-2017 by Samuel Devulder
-- --
-- This program is free software; you can redistribute -- This program is free software; you can redistribute
-- it and/or modify it under the terms of the GNU -- it and/or modify it under the terms of the GNU
-- General Public License as published by the Free -- General Public License as published by the Free
-- Software Foundation; version 2 of the License. -- Software Foundation; version 2 of the License.
-- See <http://www.gnu.org/licenses/> -- See <http://www.gnu.org/licenses/>
if not bayer then if not bayer then
bayer = {} bayer = {}
-- doubles a matrix rows and columns -- doubles a matrix rows and columns
function bayer.double(matrix) function bayer.double(matrix)
local m,n=#matrix,#matrix[1] local m,n=#matrix,#matrix[1]
local r = {} local r = {}
for j=1,m*2 do for j=1,m*2 do
local t = {} local t = {}
for i=1,n*2 do t[i]=0; end for i=1,n*2 do t[i]=0; end
r[j] = t; r[j] = t;
end end
-- 0 3 -- 0 3
-- 2 1 -- 2 1
for j=1,m do for j=1,m do
for i=1,n do for i=1,n do
local v = 4*matrix[j][i] local v = 4*matrix[j][i]
r[m*0+j][n*0+i] = v-3 r[m*0+j][n*0+i] = v-3
r[m*1+j][n*1+i] = v-2 r[m*1+j][n*1+i] = v-2
r[m*1+j][n*0+i] = v-1 r[m*1+j][n*0+i] = v-1
r[m*0+j][n*1+i] = v-0 r[m*0+j][n*1+i] = v-0
end end
end end
return r; return r;
end end
-- returns a version of the matrix normalized into -- returns a version of the matrix normalized into
-- the 0-1 range -- the 0-1 range
function bayer.norm(matrix) function bayer.norm(matrix)
local m,n=#matrix,#matrix[1] local m,n=#matrix,#matrix[1]
local max,ret = 0,{} local max,ret = 0,{}
for j=1,m do for j=1,m do
for i=1,n do for i=1,n do
max = math.max(max,matrix[j][i]) max = math.max(max,matrix[j][i])
end end
end end
-- max=max+1 -- max=max+1
for j=1,m do for j=1,m do
ret[j] = {} ret[j] = {}
for i=1,n do for i=1,n do
ret[j][i]=matrix[j][i]/max ret[j][i]=matrix[j][i]/max
end end
end end
return ret return ret
end end
-- returns a normalized order-n bayer matrix -- returns a normalized order-n bayer matrix
function bayer.make(n) function bayer.make(n)
local m = {{1}} local m = {{1}}
while n>1 do n,m = n/2,bayer.double(m) end while n>1 do n,m = n/2,bayer.double(m) end
return bayer.norm(m) return bayer.norm(m)
end end
end -- Bayer end -- Bayer

View File

@ -1,345 +1,345 @@
-- color.lua : a color class capable of representing -- color.lua : a color class capable of representing
-- and manipulating colors in PC-space (gamma=2.2) or -- and manipulating colors in PC-space (gamma=2.2) or
-- in linear space (gamma=1). -- in linear space (gamma=1).
-- --
-- Version: 02-jan-2017 -- Version: 02-jan-2017
-- --
-- Copyright 2016-2017 by Samuel Devulder -- Copyright 2016-2017 by Samuel Devulder
-- --
-- This program is free software; you can redistribute -- This program is free software; you can redistribute
-- it and/or modify it under the terms of the GNU -- it and/or modify it under the terms of the GNU
-- General Public License as published by the Free -- General Public License as published by the Free
-- Software Foundation; version 2 of the License. -- Software Foundation; version 2 of the License.
-- See <http://www.gnu.org/licenses/> -- See <http://www.gnu.org/licenses/>
if not Color then if not Color then
Color = {ONE=255,NORMALIZE=.005} Color = {ONE=255,NORMALIZE=.005}
function Color:new(r,g,b) function Color:new(r,g,b)
local o = {}; local o = {};
o.r = type(r)=='number' and r or r and r.r or 0; 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.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; o.b = type(b)=='number' and b or r and r.b or 0;
setmetatable(o, self) setmetatable(o, self)
self.__index = self self.__index = self
return o return o
end end
Color.black = Color:new(0,0,0) Color.black = Color:new(0,0,0)
function Color.clamp(v,...) function Color.clamp(v,...)
if v then if v then
return v<0 and 0 or return v<0 and 0 or
v>Color.ONE and Color.ONE or v>Color.ONE and Color.ONE or
v,Color.clamp(...) v,Color.clamp(...)
end end
end end
function Color:clone() function Color:clone()
return Color:new(self.r, self.g, self.b) return Color:new(self.r, self.g, self.b)
end end
function Color:tostring() function Color:tostring()
return "(r=" .. self.r .. " g=" .. self.g .. " b=" .. self.b .. ")" return "(r=" .. self.r .. " g=" .. self.g .. " b=" .. self.b .. ")"
end end
function Color:HSV() function Color:HSV()
local max=math.floor(.5+math.max(self.r,self.g,self.b)) 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 min=math.floor(.5+math.min(self.r,self.g,self.b))
local H=(max<=min and 0 or local H=(max<=min and 0 or
max<=self.r and (self.g-self.b)/(max-min)+6 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.g and (self.b-self.r)/(max-min)+2 or
max<=self.b and (self.r-self.g)/(max-min)+4)/6 % 1.0 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 S=(max==0 or max<=min) and 0 or 1-min/max
local V=max/Color.ONE local V=max/Color.ONE
return H,S,V return H,S,V
end end
function Color:intensity() function Color:intensity()
return .3*self.r + .59*self.g + .11*self.b return .3*self.r + .59*self.g + .11*self.b
end end
function Color:mul(val) function Color:mul(val)
self.r = self.r * val; self.r = self.r * val;
self.g = self.g * val; self.g = self.g * val;
self.b = self.b * val; self.b = self.b * val;
return self; return self;
end end
function Color:div(val) function Color:div(val)
return self:mul(1/val); return self:mul(1/val);
end end
function Color:add(other) function Color:add(other)
self.r = self.r + other.r; self.r = self.r + other.r;
self.g = self.g + other.g; self.g = self.g + other.g;
self.b = self.b + other.b; self.b = self.b + other.b;
return self; return self;
end end
function Color:sub(other) function Color:sub(other)
self.r = self.r - other.r; self.r = self.r - other.r;
self.g = self.g - other.g; self.g = self.g - other.g;
self.b = self.b - other.b; self.b = self.b - other.b;
return self; return self;
end end
function Color:dist2(other) function Color:dist2(other)
return self:euclid_dist2(other) return self:euclid_dist2(other)
-- return Color.dE2000(self,other)^2 -- return Color.dE2000(self,other)^2
-- return Color.dE2fast(self,other) -- return Color.dE2fast(self,other)
end end
function Color:euclid_dist2(other) function Color:euclid_dist2(other)
return (self.r - other.r)^2 + return (self.r - other.r)^2 +
(self.g - other.g)^2 + (self.g - other.g)^2 +
(self.b - other.b)^2 (self.b - other.b)^2
end end
function Color:floor() function Color:floor()
self.r = math.min(math.floor(self.r),Color.ONE); self.r = math.min(math.floor(self.r),Color.ONE);
self.g = math.min(math.floor(self.g),Color.ONE); self.g = math.min(math.floor(self.g),Color.ONE);
self.b = math.min(math.floor(self.b),Color.ONE); self.b = math.min(math.floor(self.b),Color.ONE);
return self; return self;
end end
function Color:toPC() function Color:toPC()
local function f(val) local function f(val)
val = val/Color.ONE val = val/Color.ONE
-- if val<=0.018 then val = 4.5*val; else val = 1.099*(val ^ (1/2.2))-0.099; end -- 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 -- 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 if val<=0.0031308 then val=12.92*val else val = 1.055*(val ^ (1/2.4))-0.055 end
return val*Color.ONE return val*Color.ONE
end; end;
self.r = f(self.r); self.r = f(self.r);
self.g = f(self.g); self.g = f(self.g);
self.b = f(self.b); self.b = f(self.b);
return self; return self;
end end
function Color:toLinear() function Color:toLinear()
local function f(val) local function f(val)
val = val/Color.ONE val = val/Color.ONE
-- if val<=0.081 then val = val/4.5; else val = ((val+0.099)/1.099)^2.2; end -- 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 -- 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 if val<=0.04045 then val = val/12.92 else val = ((val+0.055)/1.055)^2.4 end
return val*Color.ONE return val*Color.ONE
end; end;
self.r = f(self.r); self.r = f(self.r);
self.g = f(self.g); self.g = f(self.g);
self.b = f(self.b); self.b = f(self.b);
return self; return self;
end end
function Color:toRGB() function Color:toRGB()
return self.r, self.g, self.b return self.r, self.g, self.b
end end
-- return the Color @(x,y) on the original screen in linear space -- return the Color @(x,y) on the original screen in linear space
local screen_w, screen_h, _getLinearPictureColor = getpicturesize() local screen_w, screen_h, _getLinearPictureColor = getpicturesize()
function getLinearPictureColor(x,y) function getLinearPictureColor(x,y)
if _getLinearPictureColor==nil then if _getLinearPictureColor==nil then
_getLinearPictureColor = {} _getLinearPictureColor = {}
for i=0,255 do _getLinearPictureColor[i] = Color:new(getbackupcolor(i)):toLinear(); end for i=0,255 do _getLinearPictureColor[i] = Color:new(getbackupcolor(i)):toLinear(); end
if Color.NORMALIZE>0 then if Color.NORMALIZE>0 then
local histo = {} local histo = {}
for i=0,255 do histo[i] = 0 end for i=0,255 do histo[i] = 0 end
for y=0,screen_h-1 do for y=0,screen_h-1 do
for x=0,screen_w-1 do for x=0,screen_w-1 do
local r,g,b = getbackupcolor(getbackuppixel(x,y)) local r,g,b = getbackupcolor(getbackuppixel(x,y))
histo[r] = histo[r]+1 histo[r] = histo[r]+1
histo[g] = histo[g]+1 histo[g] = histo[g]+1
histo[b] = histo[b]+1 histo[b] = histo[b]+1
end end
end end
local acc,thr=0,Color.NORMALIZE*screen_h*screen_w*3 local acc,thr=0,Color.NORMALIZE*screen_h*screen_w*3
local max local max
for i=255,0,-1 do for i=255,0,-1 do
acc = acc + histo[i] acc = acc + histo[i]
if not max and acc>=thr then if not max and acc>=thr then
max = Color:new(i,i,i):toLinear().r max = Color:new(i,i,i):toLinear().r
end end
end end
for _,c in ipairs(_getLinearPictureColor) do for _,c in ipairs(_getLinearPictureColor) do
c:mul(Color.ONE/max) c:mul(Color.ONE/max)
c.r,c.g,c.b = Color.clamp(c.r,c.g,c.b) c.r,c.g,c.b = Color.clamp(c.r,c.g,c.b)
end end
end 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)] return (x<0 or y<0 or x>=screen_w or y>=screen_h) and Color.black or _getLinearPictureColor[getbackuppixel(x,y)]
end end
-- http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html -- http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
function Color.RGBtoXYZ(R,G,B) function Color.RGBtoXYZ(R,G,B)
return 0.4887180*R +0.3106803*G +0.2006017*B, return 0.4887180*R +0.3106803*G +0.2006017*B,
0.1762044*R +0.8129847*G +0.0108109*B, 0.1762044*R +0.8129847*G +0.0108109*B,
0.0102048*G +0.9897952*B 0.0102048*G +0.9897952*B
end end
function Color.XYZtoRGB(X,Y,Z) function Color.XYZtoRGB(X,Y,Z)
return 2.3706743*X -0.9000405*Y -0.4706338*Z, return 2.3706743*X -0.9000405*Y -0.4706338*Z,
-0.5138850*X +1.4253036*Y +0.0885814*Z, -0.5138850*X +1.4253036*Y +0.0885814*Z,
0.0052982*X -0.0146949*Y +1.0093968*Z 0.0052982*X -0.0146949*Y +1.0093968*Z
end end
-- https://fr.wikipedia.org/wiki/CIE_L*a*b* -- https://fr.wikipedia.org/wiki/CIE_L*a*b*
function Color.XYZtoCIELab(X,Y,Z) function Color.XYZtoCIELab(X,Y,Z)
local function f(t) local function f(t)
return t>0.00885645167 and t^(1/3) return t>0.00885645167 and t^(1/3)
or 7.78703703704*t+0.13793103448 or 7.78703703704*t+0.13793103448
end end
X,Y,Z=X/Color.ONE,Y/Color.ONE,Z/Color.ONE X,Y,Z=X/Color.ONE,Y/Color.ONE,Z/Color.ONE
return 116*f(Y)-16, return 116*f(Y)-16,
500*(f(X)-f(Y)), 500*(f(X)-f(Y)),
200*(f(Y)-f(Z)) 200*(f(Y)-f(Z))
end end
function Color.CIEALabtoXYZ(L,a,b) function Color.CIEALabtoXYZ(L,a,b)
local function f(t) local function f(t)
return t>0.20689655172 and t^3 return t>0.20689655172 and t^3
or 0.12841854934*(t-0.13793103448) or 0.12841854934*(t-0.13793103448)
end end
local l=(L+16)/116 local l=(L+16)/116
return Color.ONE*f(l), return Color.ONE*f(l),
Color.ONE*f(l+a/500), Color.ONE*f(l+a/500),
Color.ONE*f(l-b/200) Color.ONE*f(l-b/200)
end end
function Color:toLab() function Color:toLab()
return Color.XYZtoCIELab(Color.RGBtoXYZ(self:toRGB())) return Color.XYZtoCIELab(Color.RGBtoXYZ(self:toRGB()))
end end
-- http://www.brucelindbloom.com/Eqn_DeltaE_CIE2000.html -- http://www.brucelindbloom.com/Eqn_DeltaE_CIE2000.html
function Color.dE1976(col1,col2) function Color.dE1976(col1,col2)
local L1,a1,b1 = col1:toLab() local L1,a1,b1 = col1:toLab()
local L2,a2,b2 = col2:toLab() local L2,a2,b2 = col2:toLab()
return ((L1-L2)^2+(a1-a2)^2+(b1-b2)^2)^.5 return ((L1-L2)^2+(a1-a2)^2+(b1-b2)^2)^.5
end end
function Color.dE1994(col1,col2) function Color.dE1994(col1,col2)
local L1,a1,b1 = col1:toLab() local L1,a1,b1 = col1:toLab()
local L2,a2,b2 = col2:toLab() local L2,a2,b2 = col2:toLab()
local k1,k2 = 0.045,0.015 local k1,k2 = 0.045,0.015
local kL,kC,kH = 1,1,1 local kL,kC,kH = 1,1,1
local c1 = (a1^2 + b1^2)^.5 local c1 = (a1^2 + b1^2)^.5
local c2 = (a2^2 + b2^2)^.5 local c2 = (a2^2 + b2^2)^.5
local dA = a1 - a2 local dA = a1 - a2
local dB = b1 - b2 local dB = b1 - b2
local dC = c1 - c2 local dC = c1 - c2
local dH2 = dA^2 + dB^2 - dC^2 local dH2 = dA^2 + dB^2 - dC^2
local dH = dH2>0 and dH2^.5 or 0 local dH = dH2>0 and dH2^.5 or 0
local dL = L1 - L2 local dL = L1 - L2
local sL = 1 local sL = 1
local sC = 1 + k1*c1 local sC = 1 + k1*c1
local sH = 1 + k2*c1 local sH = 1 + k2*c1
local vL = dL/(kL*sL) local vL = dL/(kL*sL)
local vC = dC/(kC*sC) local vC = dC/(kC*sC)
local vH = dH/(kH*sH) local vH = dH/(kH*sH)
return (vL^2 + vC^2 + vH^2)^.5 return (vL^2 + vC^2 + vH^2)^.5
end end
-- http://www.color.org/events/colorimetry/Melgosa_CIEDE2000_Workshop-July4.pdf -- http://www.color.org/events/colorimetry/Melgosa_CIEDE2000_Workshop-July4.pdf
-- https://en.wikipedia.org/wiki/Color_difference#CIEDE2000 -- https://en.wikipedia.org/wiki/Color_difference#CIEDE2000
function Color.dE2000(col1,col2) function Color.dE2000(col1,col2)
local L1,a1,b1 = col1:toLab() local L1,a1,b1 = col1:toLab()
local L2,a2,b2 = col2:toLab() local L2,a2,b2 = col2:toLab()
local kL,kC,kH = 1,1,1 local kL,kC,kH = 1,1,1
local l_p = (L1 + L2)/2 local l_p = (L1 + L2)/2
function sqrt(x) function sqrt(x)
return x^.5 return x^.5
end end
function norm(x,y) function norm(x,y)
return sqrt(x^2+y^2) return sqrt(x^2+y^2)
end end
function mean(x,y) function mean(x,y)
return (x+y)/2 return (x+y)/2
end end
local function atan2(a,b) local function atan2(a,b)
local t=math.atan2(a,b)*180/math.pi local t=math.atan2(a,b)*180/math.pi
return t<0 and t+360 or t return t<0 and t+360 or t
end end
local function sin(x) local function sin(x)
return math.sin(x*math.pi/180) return math.sin(x*math.pi/180)
end end
local function cos(x) local function cos(x)
return math.cos(x*math.pi/180) return math.cos(x*math.pi/180)
end end
local c1 = norm(a1,b1) local c1 = norm(a1,b1)
local c2 = norm(a2,b2) local c2 = norm(a2,b2)
local c_ = mean(c1,c2) local c_ = mean(c1,c2)
local G = 0.5*(1-sqrt(c_^7/(c_^7+25^7))) local G = 0.5*(1-sqrt(c_^7/(c_^7+25^7)))
local a1p = a1*(1+G) local a1p = a1*(1+G)
local a2p = a2*(1+G) local a2p = a2*(1+G)
local c1p = norm(a1p,b1) local c1p = norm(a1p,b1)
local c2p = norm(a2p,b2) local c2p = norm(a2p,b2)
local c_p = mean(c1p,c2p) local c_p = mean(c1p,c2p)
local h1p = atan2(b1,a1p) local h1p = atan2(b1,a1p)
local h2p = atan2(b2,a2p) local h2p = atan2(b2,a2p)
local h_p = mean(h1p,h2p) + local h_p = mean(h1p,h2p) +
(math.abs(h1p - h2p)<=180 and 0 or (math.abs(h1p - h2p)<=180 and 0 or
h1p+h2p<360 and 180 or -180) h1p+h2p<360 and 180 or -180)
local T = 1 - local T = 1 -
0.17 * cos( h_p - 30) + 0.17 * cos( h_p - 30) +
0.24 * cos(2 * h_p ) + 0.24 * cos(2 * h_p ) +
0.32 * cos(3 * h_p + 6) - 0.32 * cos(3 * h_p + 6) -
0.20 * cos(4 * h_p - 63) 0.20 * cos(4 * h_p - 63)
local dhp = h2p - h1p + (math.abs(h1p - h2p)<=180 and 0 or local dhp = h2p - h1p + (math.abs(h1p - h2p)<=180 and 0 or
h2p<=h1p and 360 or h2p<=h1p and 360 or
-360) -360)
local dLp = L2 - L1 local dLp = L2 - L1
local dCp = c2p - c1p local dCp = c2p - c1p
local dHp = 2*sqrt(c1p*c2p)*sin(dhp/2) 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 sL = 1 + 0.015*(l_p - 50)^2/sqrt(20+(l_p-50)^2)
local sC = 1 + 0.045*c_p local sC = 1 + 0.045*c_p
local sH = 1 + 0.015*c_p*T local sH = 1 + 0.015*c_p*T
local d0 = 30*math.exp(-((h_p-275)/25)^2) local d0 = 30*math.exp(-((h_p-275)/25)^2)
local rC = 2*sqrt(c_p^7/(c_p^7+25^7)) local rC = 2*sqrt(c_p^7/(c_p^7+25^7))
local rT = -rC * sin(2*d0) local rT = -rC * sin(2*d0)
return sqrt( (dLp / (kL*sL))^2 + return sqrt( (dLp / (kL*sL))^2 +
(dCp / (kC*sC))^2 + (dCp / (kC*sC))^2 +
(dHp / (kH*sH))^2 + (dHp / (kH*sH))^2 +
(dCp / (kC*sC))*(dHp / (kH*sH))*rT ) (dCp / (kC*sC))*(dHp / (kH*sH))*rT )
end end
function Color.dE2fast(col1,col2) function Color.dE2fast(col1,col2)
-- http://www.compuphase.com/cmetric.htm#GAMMA -- http://www.compuphase.com/cmetric.htm#GAMMA
local r1,g1,b1 = Color.clamp(col1:toRGB()) local r1,g1,b1 = Color.clamp(col1:toRGB())
local r2,g2,b2 = Color.clamp(col2:toRGB()) local r2,g2,b2 = Color.clamp(col2:toRGB())
local rM = (r1+r2)/(Color.ONE*2) local rM = (r1+r2)/(Color.ONE*2)
return ((r1-r2)^2)*(2+rM) + return ((r1-r2)^2)*(2+rM) +
((g1-g2)^2)*(4+1) + ((g1-g2)^2)*(4+1) +
((b1-b2)^2)*(3-rM) ((b1-b2)^2)*(3-rM)
end end
function Color:hash(M) function Color:hash(M)
M=M or 256 M=M or 256
local m=(M-1)/Color.ONE local m=(M-1)/Color.ONE
local function f(x) local function f(x)
return math.floor(.5+(x<0 and 0 or x>Color.ONE and Color.ONE or x)*m) return math.floor(.5+(x<0 and 0 or x>Color.ONE and Color.ONE or x)*m)
end end
return f(self.r)+M*(f(self.g)+M*f(self.b)) return f(self.r)+M*(f(self.g)+M*f(self.b))
end end
end -- Color defined end -- Color defined

View File

@ -1,219 +1,219 @@
-- convxhull.lua : support for computing the convex -- convxhull.lua : support for computing the convex
-- hull of a set of points. -- hull of a set of points.
-- --
-- inspired from: https://gist.github.com/anonymous/5184ba0bcab21d3dd19781efd3aae543 -- inspired from: https://gist.github.com/anonymous/5184ba0bcab21d3dd19781efd3aae543
-- --
-- Version: 02-jan-2017 -- Version: 02-jan-2017
-- --
-- Copyright 2016-2017 by Samuel Devulder -- Copyright 2016-2017 by Samuel Devulder
-- --
-- This program is free software; you can redistribute -- This program is free software; you can redistribute
-- it and/or modify it under the terms of the GNU -- it and/or modify it under the terms of the GNU
-- General Public License as published by the Free -- General Public License as published by the Free
-- Software Foundation; version 2 of the License. -- Software Foundation; version 2 of the License.
-- See <http://www.gnu.org/licenses/> -- See <http://www.gnu.org/licenses/>
if not ConvexHull then if not ConvexHull then
local function sub(u,v) local function sub(u,v)
return {u[1]-v[1],u[2]-v[2],u[3]-v[3]} return {u[1]-v[1],u[2]-v[2],u[3]-v[3]}
end end
local function mul(k,u) local function mul(k,u)
return {k*u[1],k*u[2],k*u[3]} return {k*u[1],k*u[2],k*u[3]}
end end
local function cross(u,v) local function cross(u,v)
return {u[2]*v[3] - u[3]*v[2], return {u[2]*v[3] - u[3]*v[2],
u[3]*v[1] - u[1]*v[3], u[3]*v[1] - u[1]*v[3],
u[1]*v[2] - u[2]*v[1]} u[1]*v[2] - u[2]*v[1]}
end end
local function dot(u,v) local function dot(u,v)
return u[1]*v[1] + u[2]*v[2] + u[3]*v[3] return u[1]*v[1] + u[2]*v[2] + u[3]*v[3]
end end
local function unit(u) local function unit(u)
local d=dot(u,u) local d=dot(u,u)
return d==0 and u or mul(1/d^.5, u) return d==0 and u or mul(1/d^.5, u)
end end
ConvexHull = {} ConvexHull = {}
function ConvexHull:new(coordFct) function ConvexHull:new(coordFct)
local o = { local o = {
points={}, points={},
coord=coordFct coord=coordFct
} }
setmetatable(o, self) setmetatable(o, self)
self.__index = self self.__index = self
return o return o
end end
function ConvexHull.coord(elt) function ConvexHull.coord(elt)
return {elt[1],elt[2],elt[3]} return {elt[1],elt[2],elt[3]}
end end
function ConvexHull:vect(a,b) function ConvexHull:vect(a,b)
return sub(self.coord(b),self.coord(a)) return sub(self.coord(b),self.coord(a))
end end
function ConvexHull:normal(face) function ConvexHull:normal(face)
local u=self:vect(face[1],face[2]) local u=self:vect(face[1],face[2])
local v=self:vect(face[1],face[3]) local v=self:vect(face[1],face[3])
return cross(u,v) return cross(u,v)
end end
function ConvexHull:printPoint(p) function ConvexHull:printPoint(p)
return '('..table.concat(self.coord(p),',')..')' return '('..table.concat(self.coord(p),',')..')'
end end
function ConvexHull:printFace(F) function ConvexHull:printFace(F)
return '['..self:printPoint(F[1])..' '.. return '['..self:printPoint(F[1])..' '..
self:printPoint(F[2])..' '.. self:printPoint(F[2])..' '..
self:printPoint(F[3])..']' self:printPoint(F[3])..']'
end end
function ConvexHull:seen(face,p) function ConvexHull:seen(face,p)
local N=self:normal(face) local N=self:normal(face)
local P=self:vect(face[1],p) local P=self:vect(face[1],p)
return dot(N,P)>=0 return dot(N,P)>=0
end end
function ConvexHull:bdry(faces) function ConvexHull:bdry(faces)
local code={n=0} local code={n=0}
function code.encode(pt,...) function code.encode(pt,...)
if pt then if pt then
local k = code[pt] local k = code[pt]
if not k then if not k then
k = code.n+1 k = code.n+1
code[k] = pt code[k] = pt
code[pt] = k code[pt] = k
code.n = k code.n = k
end end
local rest = code.encode(...) local rest = code.encode(...)
return rest and (k..','..rest) or ""..k return rest and (k..','..rest) or ""..k
end end
end end
function code.decode(str) function code.decode(str)
local i = str:find(',') local i = str:find(',')
if i then if i then
local k = str:sub(1,i-1) local k = str:sub(1,i-1)
return code[tonumber(k)],code.decode(str:sub(i+1)) return code[tonumber(k)],code.decode(str:sub(i+1))
else else
return code[tonumber(str)] return code[tonumber(str)]
end end
end end
local set = {} local set = {}
local function add(...) local function add(...)
set[code.encode(...)] = true set[code.encode(...)] = true
end end
local function rem(...) local function rem(...)
set[code.encode(...)] = nil set[code.encode(...)] = nil
end end
local function keys() local function keys()
local r = {} local r = {}
for k in pairs(set) do for k in pairs(set) do
r[{code.decode(k)}] = true r[{code.decode(k)}] = true
end end
return r return r
end end
for F in pairs(faces) do for F in pairs(faces) do
add(F[1],F[2]) add(F[1],F[2])
add(F[2],F[3]) add(F[2],F[3])
add(F[3],F[1]) add(F[3],F[1])
end end
for F in pairs(faces) do for F in pairs(faces) do
rem(F[1],F[3]) rem(F[1],F[3])
rem(F[3],F[2]) rem(F[3],F[2])
rem(F[2],F[1]) rem(F[2],F[1])
end end
return keys() return keys()
end end
function ConvexHull:addPoint(p) function ConvexHull:addPoint(p)
-- first 3 points -- first 3 points
if self.points then if self.points then
if p==self.points[1] or p==self.points[2] then return end if p==self.points[1] or p==self.points[2] then return end
table.insert(self.points,p) table.insert(self.points,p)
if #self.points==3 then if #self.points==3 then
self.hull={ self.hull={
{self.points[1],self.points[2],self.points[3]}, {self.points[1],self.points[2],self.points[3]},
{self.points[1],self.points[3],self.points[2]} {self.points[1],self.points[3],self.points[2]}
} }
self.points=nil self.points=nil
end end
else else
local seenF,n = {},0 local seenF,n = {},0
for _,F in ipairs(self.hull) do for _,F in ipairs(self.hull) do
if F[1]==p or F[2]==p or F[3]==p then return end 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 if self:seen(F,p) then seenF[F]=true;n=n+1 end
end end
if n==#self.hull then if n==#self.hull then
-- if can see all faces, unsee ones looking "down" -- if can see all faces, unsee ones looking "down"
local N local N
for F in pairs(seenF) do N=self:normal(F); break; end for F in pairs(seenF) do N=self:normal(F); break; end
for F in pairs(seenF) do for F in pairs(seenF) do
if dot(self:normal(F),N)<=0 then if dot(self:normal(F),N)<=0 then
seenF[F] = nil seenF[F] = nil
n=n-1 n=n-1
end end
end end
end end
-- remove (old) seen faces -- remove (old) seen faces
local z=#self.hull local z=#self.hull
for i=#self.hull,1,-1 do for i=#self.hull,1,-1 do
if seenF[self.hull[i]] then if seenF[self.hull[i]] then
table.remove(self.hull,i) table.remove(self.hull,i)
end end
end end
-- insert new boundaries with seen faces -- insert new boundaries with seen faces
for E in pairs(self:bdry(seenF)) do for E in pairs(self:bdry(seenF)) do
table.insert(self.hull,{E[1],E[2],p}) table.insert(self.hull,{E[1],E[2],p})
end end
end end
return self return self
end end
function ConvexHull:verticesSet() function ConvexHull:verticesSet()
local v = {} local v = {}
if self.hull then if self.hull then
for _,F in ipairs(self.hull) do for _,F in ipairs(self.hull) do
v[F[1]] = true v[F[1]] = true
v[F[2]] = true v[F[2]] = true
v[F[3]] = true v[F[3]] = true
end end
end end
return v return v
end end
function ConvexHull:verticesSize() function ConvexHull:verticesSize()
local n = 0 local n = 0
for _ in pairs(self:verticesSet()) do n=n+1 end for _ in pairs(self:verticesSet()) do n=n+1 end
return n return n
end end
function ConvexHull:distToFace(F,pt) function ConvexHull:distToFace(F,pt)
local N=unit(self:normal(F)) local N=unit(self:normal(F))
local P=self:vect(F[1],pt) local P=self:vect(F[1],pt)
return dot(N,P) return dot(N,P)
end end
function ConvexHull:distToHull(pt) function ConvexHull:distToHull(pt)
local d local d
for _,F in ipairs(self.hull) do for _,F in ipairs(self.hull) do
local t = self:distToFace(F,pt) local t = self:distToFace(F,pt)
d = d==nil and t or d = d==nil and t or
(0<=t and t<d or (0<=t and t<d or
0>=t and t>d) and t or 0>=t and t>d) and t or
d d
if d==0 then break end if d==0 then break end
end end
return d return d
end end
end -- ConvexHull end -- ConvexHull

View File

@ -1,454 +1,454 @@
-- thomson.lua : lots of utility for handling -- thomson.lua : lots of utility for handling
-- thomson screen. -- thomson screen.
-- --
-- Version: 02-jan-2017 -- Version: 02-jan-2017
-- --
-- Copyright 2016-2017 by Samuel Devulder -- Copyright 2016-2017 by Samuel Devulder
-- --
-- This program is free software; you can redistribute -- This program is free software; you can redistribute
-- it and/or modify it under the terms of the GNU -- it and/or modify it under the terms of the GNU
-- General Public License as published by the Free -- General Public License as published by the Free
-- Software Foundation; version 2 of the License. -- Software Foundation; version 2 of the License.
-- See <http://www.gnu.org/licenses/> -- See <http://www.gnu.org/licenses/>
if not thomson then if not thomson then
run("color.lua") -- optionnal run("color.lua") -- optionnal
thomson = {optiMAP=true} thomson = {optiMAP=true}
-- RAM banks -- RAM banks
thomson.ramA = {} thomson.ramA = {}
thomson.ramB = {} thomson.ramB = {}
function thomson.clear() function thomson.clear()
for i=1,8000 do for i=1,8000 do
thomson.ramA[i] = 0 thomson.ramA[i] = 0
thomson.ramB[i] = 0 thomson.ramB[i] = 0
end end
end end
-- color levels -- color levels
thomson.levels = { thomson.levels = {
-- in pc-space (0-255): -- in pc-space (0-255):
pc = {0,100,127,142,163,179,191,203,215,223,231,239, pc = {0,100,127,142,163,179,191,203,215,223,231,239,
243,247,251,255}, 243,247,251,255},
-- in linear space (0-255): -- in linear space (0-255):
linear = {}, linear = {},
-- maps pc-levels (0-255) to thomson levels (1-16) -- maps pc-levels (0-255) to thomson levels (1-16)
pc2to={}, pc2to={},
-- maps linear-levels (0-255) to thomson levels (1-16) -- maps linear-levels (0-255) to thomson levels (1-16)
linear2to={} linear2to={}
}; };
-- pc space to linear space -- pc space to linear space
local function toLinear(val) local function toLinear(val)
-- use the version from Color library -- use the version from Color library
if not Color then if not Color then
val = val/255 val = val/255
if val<=0.081 then if val<=0.081 then
val = val/4.5; val = val/4.5;
else else
val = ((val+0.099)/1.099)^2.2; val = ((val+0.099)/1.099)^2.2;
end end
val = val*255 val = val*255
return val; return val;
else else
return Color:new(val,0,0):toLinear().r return Color:new(val,0,0):toLinear().r
end end
end end
for i=1,16 do for i=1,16 do
thomson.levels.linear[i] = toLinear(thomson.levels.pc[i]) thomson.levels.linear[i] = toLinear(thomson.levels.pc[i])
end end
for i=0,255 do for i=0,255 do
local r,cm,dm; local r,cm,dm;
r,cm,dm = toLinear(i),0,1e30 r,cm,dm = toLinear(i),0,1e30
for c,v in ipairs(thomson.levels.linear) do for c,v in ipairs(thomson.levels.linear) do
local d = math.abs(v-r); local d = math.abs(v-r);
if d<dm then cm,dm = c,d; end if d<dm then cm,dm = c,d; end
end end
thomson.levels.pc2to[i] = cm; thomson.levels.pc2to[i] = cm;
r,cm,dm = i,0,1e30 r,cm,dm = i,0,1e30
for c,v in ipairs(thomson.levels.linear) do for c,v in ipairs(thomson.levels.linear) do
local d = math.abs(v-r); local d = math.abs(v-r);
if d<dm then cm,dm = c,d; end if d<dm then cm,dm = c,d; end
end end
thomson.levels.linear2to[i] = cm; thomson.levels.linear2to[i] = cm;
end end
-- palette stuff -- palette stuff
function thomson.palette(i, pal) function thomson.palette(i, pal)
-- returns palette #i if pal is missing (nil) -- returns palette #i if pal is missing (nil)
-- if pal is a number, sets palette #i -- if pal is a number, sets palette #i
-- if pal is an array, sets the palette #i, #i+1, ... -- if pal is an array, sets the palette #i, #i+1, ...
if type(pal)=='table' then if type(pal)=='table' then
for j,v in ipairs(pal) do for j,v in ipairs(pal) do
thomson.palette(i+j-1,v) thomson.palette(i+j-1,v)
end end
elseif pal and i>=0 and i<thomson._palette.max then elseif pal and i>=0 and i<thomson._palette.max then
thomson._palette[i+1] = pal thomson._palette[i+1] = pal
elseif not pal and i>=0 and i<thomson._palette.max then elseif not pal and i>=0 and i<thomson._palette.max then
return thomson._palette[i+1] return thomson._palette[i+1]
end end
end; end;
thomson._palette = {offset = 0, max=16} thomson._palette = {offset = 0, max=16}
thomson.default_palette = {0,15,240,255,3840,3855,4080,4095, thomson.default_palette = {0,15,240,255,3840,3855,4080,4095,
1911,826,931,938,2611,2618,3815,123} 1911,826,931,938,2611,2618,3815,123}
-- border color -- border color
function thomson.border(c) function thomson.border(c)
if c then if c then
thomson._border = c; thomson._border = c;
else else
return thomson._border return thomson._border
end end
end end
thomson.border(0) thomson.border(0)
-- helper to appen tables to tables -- helper to appen tables to tables
function thomson._append(result, ...) function thomson._append(result, ...)
for _,tab in ipairs({...}) do for _,tab in ipairs({...}) do
for _,v in ipairs(tab) do for _,v in ipairs(tab) do
table.insert(result,v) table.insert(result,v)
end end
end end
end end
-- RLE compression of data into result -- RLE compression of data into result
function thomson._compress(result,data) function thomson._compress(result,data)
local partial,p,pmax={},1,#data local partial,p,pmax={},1,#data
local function addCarToPartial(car) local function addCarToPartial(car)
partial[2] = partial[2]+1 partial[2] = partial[2]+1
partial[2+partial[2]] = car partial[2+partial[2]] = car
end end
while p<=pmax do while p<=pmax do
local num,car = 1,data[p] local num,car = 1,data[p]
while num<255 and p<pmax and data[p+1]==car do while num<255 and p<pmax and data[p+1]==car do
num,p = num+1,p+1 num,p = num+1,p+1
end end
local default=true local default=true
if partial[1] then if partial[1] then
-- 01 aa 01 bb ==> 00 02 aa bb -- 01 aa 01 bb ==> 00 02 aa bb
if default and num==1 and partial[1]==1 then if default and num==1 and partial[1]==1 then
partial = {0,2,partial[2],car} partial = {0,2,partial[2],car}
default = false default = false
end end
-- 00 n xx xx xx 01 bb ==> 00 n+1 xx xx xx bb -- 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 if default and num==1 and partial[1]==0 and partial[2]<255 then
addCarToPartial(car) addCarToPartial(car)
default = false default = false
end 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) -- 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 if default and num==2 and partial[1]==0 and partial[2]<254 then
addCarToPartial(car) addCarToPartial(car)
addCarToPartial(car) addCarToPartial(car)
default = false default = false
end end
end end
if default then if default then
thomson._append(result, partial) thomson._append(result, partial)
partial = {num,car} partial = {num,car}
end end
p=p+1 p=p+1
end end
thomson._append(result, partial) thomson._append(result, partial)
return result return result
end end
-- save a map file corresponging to the current file -- save a map file corresponging to the current file
-- if a map file already exist, a confirmation is -- if a map file already exist, a confirmation is
-- prompted to the user -- prompted to the user
local function save_current_file() local function save_current_file()
local function exist(file) local function exist(file)
local f=io.open(file,'rb') local f=io.open(file,'rb')
if not f then return false else io.close(f); return true; end if not f then return false else io.close(f); return true; end
end end
local name,path = getfilename() local name,path = getfilename()
local mapname = string.gsub(name,"%.%w*$","") .. ".map" local mapname = string.gsub(name,"%.%w*$","") .. ".map"
local fullname = path .. '/' .. mapname local fullname = path .. '/' .. mapname
local ok = not exist(fullname) local ok = not exist(fullname)
if not ok then if not ok then
selectbox("Ovr " .. mapname .. "?", "Yes", function() ok = true; end, "No", function() ok = false; end) selectbox("Ovr " .. mapname .. "?", "Yes", function() ok = true; end, "No", function() ok = false; end)
end end
if ok then thomson.savep(fullname) end if ok then thomson.savep(fullname) end
end end
-- saves the thomson screen into a MAP file -- saves the thomson screen into a MAP file
function thomson.savep(name) function thomson.savep(name)
if not name then return save_current_file() end if not name then return save_current_file() end
wait(0) -- allow for key handling wait(0) -- allow for key handling
local data = thomson._get_map_data() local data = thomson._get_map_data()
local tmp = {0, math.floor(#data/256), #data%256,0,0} local tmp = {0, math.floor(#data/256), #data%256,0,0}
thomson._append(tmp,data,{255,0,0,0,0}) thomson._append(tmp,data,{255,0,0,0,0})
local function save(name, buf) local function save(name, buf)
local out = io.open(name,"wb") local out = io.open(name,"wb")
out:write(buf) out:write(buf)
out:close() out:close()
end end
save(name, string.char(unpack(tmp))) save(name, string.char(unpack(tmp)))
-- save raw data as well ? -- save raw data as well ?
local moved, key, mx, my, mb = waitinput(0.01) local moved, key, mx, my, mb = waitinput(0.01)
if key==4123 then -- shift-ESC ==> save raw files as well if key==4123 then -- shift-ESC ==> save raw files as well
save(name .. ".rama", string.char(unpack(thomson.ramA))) save(name .. ".rama", string.char(unpack(thomson.ramA)))
save(name .. ".ramb", string.char(unpack(thomson.ramB))) save(name .. ".ramb", string.char(unpack(thomson.ramB)))
local pal = "" local pal = ""
for i=0,15 do for i=0,15 do
local val = thomson.palette(i) local val = thomson.palette(i)
pal=pal..string.char(math.floor(val/256),val%256) pal=pal..string.char(math.floor(val/256),val%256)
end end
save(name .. ".pal", pal) save(name .. ".pal", pal)
messagebox('Saved MAP + RAMA/RAMB/PAL files.') messagebox('Saved MAP + RAMA/RAMB/PAL files.')
end end
end end
waitbreak(0.01) waitbreak(0.01)
function thomson.info(...) function thomson.info(...)
local txt = "" local txt = ""
for _,t in ipairs({...}) do txt = txt .. t end for _,t in ipairs({...}) do txt = txt .. t end
statusmessage(txt); statusmessage(txt);
if waitbreak(0)==1 then if waitbreak(0)==1 then
local ok=false local ok=false
selectbox("Abort ?", "Yes", function() ok = true end, "No", function() ok = false end) selectbox("Abort ?", "Yes", function() ok = true end, "No", function() ok = false end)
if ok then error('Operation aborted') end if ok then error('Operation aborted') end
end end
end end
-- copy ramA/B onto GrafX2 screen -- copy ramA/B onto GrafX2 screen
function thomson.updatescreen() function thomson.updatescreen()
-- back out -- back out
for i=0,255 do for i=0,255 do
setcolor(i,0,0,0) setcolor(i,0,0,0)
end end
-- refresh screen content -- refresh screen content
clearpicture(thomson._palette.offset + thomson.border()) clearpicture(thomson._palette.offset + thomson.border())
for y=0,thomson.h-1 do for y=0,thomson.h-1 do
for x=0,thomson.w-1 do for x=0,thomson.w-1 do
local p = thomson.point(x,y) local p = thomson.point(x,y)
if p<0 then p=-p-1 end if p<0 then p=-p-1 end
thomson._putpixel(x,y,thomson._palette.offset + p) thomson._putpixel(x,y,thomson._palette.offset + p)
end end
end end
-- refresh palette -- refresh palette
for i=1,thomson._palette.max do for i=1,thomson._palette.max do
local v=thomson._palette[i] local v=thomson._palette[i]
local r=v % 16 local r=v % 16
local g=math.floor(v/16) % 16 local g=math.floor(v/16) % 16
local b=math.floor(v/256) % 16 local b=math.floor(v/256) % 16
setcolor(i+thomson._palette.offset-1, setcolor(i+thomson._palette.offset-1,
thomson.levels.pc[r+1], thomson.levels.pc[r+1],
thomson.levels.pc[g+1], thomson.levels.pc[g+1],
thomson.levels.pc[b+1]) thomson.levels.pc[b+1])
end end
updatescreen() updatescreen()
end end
-- bitmap 16 mode -- bitmap 16 mode
function thomson.setBM16() function thomson.setBM16()
-- put a pixel onto real screen -- put a pixel onto real screen
function thomson._putpixel(x,y,c) function thomson._putpixel(x,y,c)
putpicturepixel(x*2+0,y,c) putpicturepixel(x*2+0,y,c)
putpicturepixel(x*2+1,y,c) putpicturepixel(x*2+1,y,c)
end end
-- put a pixel in thomson screen -- put a pixel in thomson screen
function thomson.pset(x,y,c) function thomson.pset(x,y,c)
local bank = x%4<2 and thomson.ramA or thomson.ramB local bank = x%4<2 and thomson.ramA or thomson.ramB
local offs = math.floor(x/4)+y*40+1 local offs = math.floor(x/4)+y*40+1
if x%2==0 then if x%2==0 then
bank[offs] = (bank[offs]%16)+c*16 bank[offs] = (bank[offs]%16)+c*16
else else
bank[offs] = math.floor(bank[offs]/16)*16+c bank[offs] = math.floor(bank[offs]/16)*16+c
end end
-- c=c+thomson._palette.offset -- c=c+thomson._palette.offset
-- putpicturepixel(x*2+0,y,c) -- putpicturepixel(x*2+0,y,c)
-- putpicturepixel(x*2+1,y,c) -- putpicturepixel(x*2+1,y,c)
end end
-- get thomson pixel at (x,y) -- get thomson pixel at (x,y)
function thomson.point(x,y) function thomson.point(x,y)
local bank = x%4<2 and thomson.ramA or thomson.ramB local bank = x%4<2 and thomson.ramA or thomson.ramB
local offs = math.floor(x/4)+y*40+1 local offs = math.floor(x/4)+y*40+1
if x%2==0 then if x%2==0 then
return math.floor(bank[offs]/16) return math.floor(bank[offs]/16)
else else
return bank[offs]%16 return bank[offs]%16
end end
end end
-- return internal MAP file -- return internal MAP file
function thomson._get_map_data() function thomson._get_map_data()
local tmp = {} local tmp = {}
for x=1,40 do for x=1,40 do
for y=x,x+7960,40 do for y=x,x+7960,40 do
table.insert(tmp, thomson.ramA[y]) table.insert(tmp, thomson.ramA[y])
end end
for y=x,x+7960,40 do for y=x,x+7960,40 do
table.insert(tmp, thomson.ramB[y]) table.insert(tmp, thomson.ramB[y])
end end
wait(0) -- allow for key handling wait(0) -- allow for key handling
end end
local pal = {} local pal = {}
for i=1,16 do for i=1,16 do
pal[2*i-1] = math.floor(thomson._palette[i]/256) pal[2*i-1] = math.floor(thomson._palette[i]/256)
pal[2*i+0] = thomson._palette[i]%256 pal[2*i+0] = thomson._palette[i]%256
end end
-- build data -- build data
local data={ local data={
-- BM16 -- BM16
0x40, 0x40,
-- ncols-1 -- ncols-1
79, 79,
-- nlines-1 -- nlines-1
24 24
}; };
thomson._compress(data, tmp) thomson._compress(data, tmp)
thomson._append(data,{0,0}) thomson._append(data,{0,0})
-- padd to word -- padd to word
if #data%2==1 then table.insert(data,0); end if #data%2==1 then table.insert(data,0); end
-- tosnap -- tosnap
thomson._append(data,{0,128,0,thomson.border(),0,3}) thomson._append(data,{0,128,0,thomson.border(),0,3})
thomson._append(data, pal) thomson._append(data, pal)
thomson._append(data,{0xa5,0x5a}) thomson._append(data,{0xa5,0x5a})
return data return data
end end
thomson.w = 160 thomson.w = 160
thomson.h = 200 thomson.h = 200
thomson.palette(0,thomson.default_palette) thomson.palette(0,thomson.default_palette)
thomson.border(0) thomson.border(0)
thomson.clear() thomson.clear()
end end
-- mode MO5 -- mode MO5
function thomson.setMO5() function thomson.setMO5()
-- put a pixel onto real screen -- put a pixel onto real screen
thomson._putpixel = putpicturepixel thomson._putpixel = putpicturepixel
-- helpers -- helpers
local function bittst(val,mask) local function bittst(val,mask)
-- return bit32.btest(val,mask) -- return bit32.btest(val,mask)
return (val % (2*mask))>=mask; return (val % (2*mask))>=mask;
end end
local function bitset(val,mask) local function bitset(val,mask)
-- return bit32.bor(val, mask) -- return bit32.bor(val, mask)
return bittst(val,mask) and val or (val+mask) return bittst(val,mask) and val or (val+mask)
end end
local function bitclr(val,mask) local function bitclr(val,mask)
-- return bit32.band(val,255-mask) -- return bit32.band(val,255-mask)
return bittst(val,mask) and (val-mask) or val return bittst(val,mask) and (val-mask) or val
end end
-- put a pixel in thomson screen -- put a pixel in thomson screen
function thomson.pset(x,y,c) function thomson.pset(x,y,c)
local offs = math.floor(x/8)+y*40+1 local offs = math.floor(x/8)+y*40+1
local mask = 2^(7-(x%8)) local mask = 2^(7-(x%8))
if c>=0 then if c>=0 then
thomson.ramB[offs] = (thomson.ramB[offs]%16)+c*16 thomson.ramB[offs] = (thomson.ramB[offs]%16)+c*16
thomson.ramA[offs] = bitset(thomson.ramA[offs],mask) thomson.ramA[offs] = bitset(thomson.ramA[offs],mask)
else else
c=-c-1 c=-c-1
thomson.ramB[offs] = math.floor(thomson.ramB[offs]/16)*16+c thomson.ramB[offs] = math.floor(thomson.ramB[offs]/16)*16+c
thomson.ramA[offs] = bitclr(thomson.ramA[offs],mask) thomson.ramA[offs] = bitclr(thomson.ramA[offs],mask)
end end
end end
-- get thomson pixel at (x,y) -- get thomson pixel at (x,y)
function thomson.point(x,y) function thomson.point(x,y)
local offs = math.floor(x/8)+y*40+1 local offs = math.floor(x/8)+y*40+1
local mask = 2^(7-(x%8)) local mask = 2^(7-(x%8))
if bittst(thomson.ramA[offs],mask) then if bittst(thomson.ramA[offs],mask) then
return math.floor(thomson.ramB[offs]/16) return math.floor(thomson.ramB[offs]/16)
else else
return -(thomson.ramB[offs]%16)-1 return -(thomson.ramB[offs]%16)-1
end end
end end
-- convert color from MO5 to TO7 (MAP requires TO7 encoding) -- convert color from MO5 to TO7 (MAP requires TO7 encoding)
local function mo5to7(val) local function mo5to7(val)
-- MO5: DCBA 4321 -- MO5: DCBA 4321
-- __ -- __
-- TO7: 4DCB A321 -- TO7: 4DCB A321
local t=((val%16)>=8) and 0 or 128 local t=((val%16)>=8) and 0 or 128
val = math.floor(val/16)*8 + (val%8) val = math.floor(val/16)*8 + (val%8)
val = (val>=64 and val-64 or val+64) + t val = (val>=64 and val-64 or val+64) + t
return val return val
end end
-- return internal MAP file -- return internal MAP file
function thomson._get_map_data() function thomson._get_map_data()
-- create columnwise data -- create columnwise data
local tmpA,tmpB={},{}; local tmpA,tmpB={},{};
for x=1,40 do for x=1,40 do
for y=x,x+7960,40 do for y=x,x+7960,40 do
table.insert(tmpA, thomson.ramA[y]) table.insert(tmpA, thomson.ramA[y])
table.insert(tmpB, thomson.ramB[y]) table.insert(tmpB, thomson.ramB[y])
end end
wait(0) -- allow for key handling wait(0) -- allow for key handling
end end
if thomson.optiMAP then if thomson.optiMAP then
-- optimize -- optimize
for i=2,8000 do for i=2,8000 do
local c1,c2 = math.floor(tmpB[i-0]/16),tmpB[i-0]%16 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 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 if tmpA[i-1]==255-tmpA[i] or c1==d2 and c2==c1 then
tmpA[i] = 255-tmpA[i] tmpA[i] = 255-tmpA[i]
tmpB[i] = c2*16+c1 tmpB[i] = c2*16+c1
elseif tmpA[i]==255 and c1==d1 or tmpA[i]==0 and c2==d2 then elseif tmpA[i]==255 and c1==d1 or tmpA[i]==0 and c2==d2 then
tmpB[i] = tmpB[i-1] tmpB[i] = tmpB[i-1]
end end
end end
else else
for i=1,8000 do for i=1,8000 do
local c1,c2 = math.floor(tmpB[i]/16),tmpB[i]%16 local c1,c2 = math.floor(tmpB[i]/16),tmpB[i]%16
if tmpA[i]==255 or c1<c2 then if tmpA[i]==255 or c1<c2 then
tmpA[i] = 255-tmpA[i] tmpA[i] = 255-tmpA[i]
tmpB[i] = c2*16+c1 tmpB[i] = c2*16+c1
end end
end end
end end
-- convert into to7 encoding -- convert into to7 encoding
for i=1,#tmpB do tmpB[i] = mo5to7(tmpB[i]); end for i=1,#tmpB do tmpB[i] = mo5to7(tmpB[i]); end
-- build data -- build data
local data={ local data={
-- BM40 -- BM40
0x00, 0x00,
-- ncols-1 -- ncols-1
39, 39,
-- nlines-1 -- nlines-1
24 24
}; };
thomson._compress(data, tmpA); tmpA=nil; thomson._compress(data, tmpA); tmpA=nil;
thomson._append(data,{0,0}) thomson._append(data,{0,0})
thomson._compress(data, tmpB); tmpB=nil; thomson._compress(data, tmpB); tmpB=nil;
thomson._append(data,{0,0}) thomson._append(data,{0,0})
-- padd to word (for compatibility with basic) -- padd to word (for compatibility with basic)
if #data%2==1 then table.insert(data,0); end if #data%2==1 then table.insert(data,0); end
-- tosnap -- tosnap
local orig_palette = true local orig_palette = true
for i=0,15 do for i=0,15 do
if thomson.default_palette[i+1]~=thomson.palette(i) then if thomson.default_palette[i+1]~=thomson.palette(i) then
orig_palette = false orig_palette = false
break break
end end
end end
if not orig_palette then if not orig_palette then
local pal = {} local pal = {}
for i=0,15 do for i=0,15 do
local v = thomson.palette(i) local v = thomson.palette(i)
pal[2*i+1] = math.floor(v/256) pal[2*i+1] = math.floor(v/256)
pal[2*i+2] = v%256 pal[2*i+2] = v%256
end end
thomson._append(data,{0,0,0,thomson.border(),0,0}) thomson._append(data,{0,0,0,thomson.border(),0,0})
thomson._append(data, pal) thomson._append(data, pal)
thomson._append(data,{0xa5,0x5a}) thomson._append(data,{0xa5,0x5a})
end end
return data return data
end end
thomson.w = 320 thomson.w = 320
thomson.h = 200 thomson.h = 200
thomson.palette(0,thomson.default_palette) thomson.palette(0,thomson.default_palette)
thomson.border(0) thomson.border(0)
thomson.clear() thomson.clear()
end end
end -- thomson end -- thomson

View File

@ -1,27 +1,27 @@
-- ostro_mo5.lua : converts a color image into a -- ostro_mo5.lua : converts a color image into a
-- MO5 image (16 fixed colors with color clash) -- MO5 image (16 fixed colors with color clash)
-- using Ostromoukhov's error diffusion algorithm. -- using Ostromoukhov's error diffusion algorithm.
-- --
-- Version: 02-jan-2017 -- Version: 02-jan-2017
-- --
-- Copyright 2016-2017 by Samuel Devulder -- Copyright 2016-2017 by Samuel Devulder
-- --
-- This program is free software; you can redistribute -- This program is free software; you can redistribute
-- it and/or modify it under the terms of the GNU -- it and/or modify it under the terms of the GNU
-- General Public License as published by the Free -- General Public License as published by the Free
-- Software Foundation; version 2 of the License. -- Software Foundation; version 2 of the License.
-- See <http://www.gnu.org/licenses/> -- See <http://www.gnu.org/licenses/>
run('lib/ostromoukhov.lua') run('lib/ostromoukhov.lua')
local dith=OstroDither:new() local dith=OstroDither:new()
local tmp=dith.setLevelsFromPalette local tmp=dith.setLevelsFromPalette
dith.setLevelsFromPalette = function(self) dith.setLevelsFromPalette = function(self)
tmp(self) tmp(self)
self.attenuation=0 self.attenuation=0
end end
dith:dither40cols(function(w,h,getLinearPixel) dith:dither40cols(function(w,h,getLinearPixel)
local pal={} local pal={}
for i=0,15 do pal[i+1] = thomson.palette(i) end for i=0,15 do pal[i+1] = thomson.palette(i) end
return pal return pal
end) end)

View File

@ -1,82 +1,82 @@
-- ostro_to8.lua : convert a color image to a BM16 -- ostro_to8.lua : convert a color image to a BM16
-- (160x200x16) thomson image using the Ostromoukhov's -- (160x200x16) thomson image using the Ostromoukhov's
-- error diffusion algorithm. -- error diffusion algorithm.
-- --
-- Version: 02-jan-2017 -- Version: 02-jan-2017
-- --
-- Copyright 2016-2017 by Samuel Devulder -- Copyright 2016-2017 by Samuel Devulder
-- --
-- This program is free software; you can redistribute -- This program is free software; you can redistribute
-- it and/or modify it under the terms of the GNU -- it and/or modify it under the terms of the GNU
-- General Public License as published by the Free -- General Public License as published by the Free
-- Software Foundation; version 2 of the License. -- Software Foundation; version 2 of the License.
-- See <http://www.gnu.org/licenses/> -- See <http://www.gnu.org/licenses/>
run('lib/thomson.lua') run('lib/thomson.lua')
run('lib/ostromoukhov.lua') run('lib/ostromoukhov.lua')
run('lib/color_reduction.lua') run('lib/color_reduction.lua')
-- run('lib/zzz.lua') -- run('lib/zzz.lua')
-- get screen size -- get screen size
local screen_w, screen_h = getpicturesize() local screen_w, screen_h = getpicturesize()
-- Converts thomson coordinates (0-159,0-199) into screen coordinates -- Converts thomson coordinates (0-159,0-199) into screen coordinates
local function thom2screen(x,y) local function thom2screen(x,y)
local i,j; local i,j;
if screen_w/screen_h < 1.6 then if screen_w/screen_h < 1.6 then
i = x*screen_h/200 i = x*screen_h/200
j = y*screen_h/200 j = y*screen_h/200
else else
i = x*screen_w/320 i = x*screen_w/320
j = y*screen_w/320 j = y*screen_w/320
end end
return math.floor(i*2), math.floor(j) return math.floor(i*2), math.floor(j)
end end
-- return the Color @(x,y) in normalized linear space (0-1) -- return the Color @(x,y) in normalized linear space (0-1)
-- corresonding to the thomson screen (x in 0-319, y in 0-199) -- corresonding to the thomson screen (x in 0-319, y in 0-199)
local function getLinearPixel(x,y) local function getLinearPixel(x,y)
local x1,y1 = thom2screen(x,y) local x1,y1 = thom2screen(x,y)
local x2,y2 = thom2screen(x+1,y+1) local x2,y2 = thom2screen(x+1,y+1)
if x2==x1 then x2=x1+1 end if x2==x1 then x2=x1+1 end
if y2==y1 then y2=y1+1 end if y2==y1 then y2=y1+1 end
local p = Color:new(0,0,0); local p = Color:new(0,0,0);
for j=y1,y2-1 do for j=y1,y2-1 do
for i=x1,x2-1 do for i=x1,x2-1 do
p:add(getLinearPictureColor(i,j)) p:add(getLinearPictureColor(i,j))
end end
end end
p:div((y2-y1)*(x2-x1)) --:floor() p:div((y2-y1)*(x2-x1)) --:floor()
return p return p
end end
local red = ColorReducer:new():analyzeWithDither(160,200, local red = ColorReducer:new():analyzeWithDither(160,200,
getLinearPixel, getLinearPixel,
function(y) function(y)
thomson.info("Collecting stats...",math.floor(y/2),"%") thomson.info("Collecting stats...",math.floor(y/2),"%")
end) end)
-- BM16 mode -- BM16 mode
thomson.setBM16() thomson.setBM16()
-- define palette -- define palette
local palette = red:boostBorderColors():buildPalette(16) local palette = red:boostBorderColors():buildPalette(16)
thomson.palette(0, palette) thomson.palette(0, palette)
-- convert picture -- convert picture
OstroDither:new(palette, 0) OstroDither:new(palette, 0)
:dither(thomson.h,thomson.w, :dither(thomson.h,thomson.w,
function(y,x) return getLinearPixel(x,y) end, function(y,x) return getLinearPixel(x,y) end,
function(y,x,c) thomson.pset(x,y,c) end, function(y,x,c) thomson.pset(x,y,c) end,
true, true,
function(x) thomson.info("Converting...",math.floor(x*100/160),"%") end) function(x) thomson.info("Converting...",math.floor(x*100/160),"%") end)
-- refresh screen -- refresh screen
setpicturesize(320,200) setpicturesize(320,200)
thomson.updatescreen() thomson.updatescreen()
finalizepicture() finalizepicture()
-- save picture -- save picture
thomson.savep() thomson.savep()

View File

@ -1,55 +1,55 @@
-- ostro_mo5.lua : converts a color image into a -- ostro_mo5.lua : converts a color image into a
-- TO9 image (320x200x16 with color clashes) -- TO9 image (320x200x16 with color clashes)
-- using Ostromoukhov's error diffusion algorithm. -- using Ostromoukhov's error diffusion algorithm.
-- --
-- Version: 02-jan-2017 -- Version: 02-jan-2017
-- --
-- Copyright 2016-2017 by Samuel Devulder -- Copyright 2016-2017 by Samuel Devulder
-- --
-- This program is free software; you can redistribute -- This program is free software; you can redistribute
-- it and/or modify it under the terms of the GNU -- it and/or modify it under the terms of the GNU
-- General Public License as published by the Free -- General Public License as published by the Free
-- Software Foundation; version 2 of the License. -- Software Foundation; version 2 of the License.
-- See <http://www.gnu.org/licenses/> -- See <http://www.gnu.org/licenses/>
run('lib/thomson.lua') run('lib/thomson.lua')
thomson.optiMAP = false thomson.optiMAP = false
run('lib/ostromoukhov.lua') run('lib/ostromoukhov.lua')
run('lib/color_reduction.lua') run('lib/color_reduction.lua')
local dith=OstroDither:new() local dith=OstroDither:new()
local tmp=dith.setLevelsFromPalette local tmp=dith.setLevelsFromPalette
dith.setLevelsFromPalette = function(self) dith.setLevelsFromPalette = function(self)
tmp(self) tmp(self)
self.attenuation=0 self.attenuation=0
end end
dith:dither40cols(function(w,h,getLinearPixel) dith:dither40cols(function(w,h,getLinearPixel)
local c16 = true local c16 = true
for y=0,h-1 do for y=0,h-1 do
for x=0,w-1 do for x=0,w-1 do
if getbackuppixel(x,y)>15 then c16 = false end if getbackuppixel(x,y)>15 then c16 = false end
end end
end end
local pal local pal
if c16 then if c16 then
pal = {} pal = {}
for i=0,15 do for i=0,15 do
local r,g,b=getbackupcolor(i) local r,g,b=getbackupcolor(i)
r = thomson.levels.pc2to[r] r = thomson.levels.pc2to[r]
g = thomson.levels.pc2to[g] g = thomson.levels.pc2to[g]
b = thomson.levels.pc2to[b] b = thomson.levels.pc2to[b]
pal[i+1] = r+g*16+b*256-273 pal[i+1] = r+g*16+b*256-273
end end
else else
pal=ColorReducer:new():analyzeWithDither(w,h, pal=ColorReducer:new():analyzeWithDither(w,h,
getLinearPixel, getLinearPixel,
function(y) function(y)
thomson.info("Building palette...",math.floor(y*100/h),"%") thomson.info("Building palette...",math.floor(y*100/h),"%")
end):buildPalette(16) end):buildPalette(16)
end end
thomson.palette(0, pal) thomson.palette(0, pal)
return pal return pal
end) end)

View File

@ -1,21 +1,21 @@
-- ostro_mo5.lua : converts a color image into a -- ostro_mo5.lua : converts a color image into a
-- MO5 image (16 fixed colors with color clash) -- MO5 image (16 fixed colors with color clash)
-- using Ostromoukhov's error diffusion algorithm. -- using Ostromoukhov's error diffusion algorithm.
-- --
-- Version: 02-jan-2017 -- Version: 02-jan-2017
-- --
-- Copyright 2016-2017 by Samuel Devulder -- Copyright 2016-2017 by Samuel Devulder
-- --
-- This program is free software; you can redistribute -- This program is free software; you can redistribute
-- it and/or modify it under the terms of the GNU -- it and/or modify it under the terms of the GNU
-- General Public License as published by the Free -- General Public License as published by the Free
-- Software Foundation; version 2 of the License. -- Software Foundation; version 2 of the License.
-- See <http://www.gnu.org/licenses/> -- See <http://www.gnu.org/licenses/>
run('lib/ostromoukhov.lua') run('lib/ostromoukhov.lua')
OstroDither:new():dither40cols(function(w,h,getLinearPixel) OstroDither:new():dither40cols(function(w,h,getLinearPixel)
local pal={} local pal={}
for i=0,15 do pal[i+1] = thomson.palette(i) end for i=0,15 do pal[i+1] = thomson.palette(i) end
return pal return pal
end) end)

View File

@ -1,21 +1,21 @@
-- ostro_mo5.lua : converts a color image into a -- ostro_mo5.lua : converts a color image into a
-- TO7 image (8 fixed colors with color clash) -- TO7 image (8 fixed colors with color clash)
-- using Ostromoukhov's error diffusion algorithm. -- using Ostromoukhov's error diffusion algorithm.
-- --
-- Version: 02-jan-2017 -- Version: 02-jan-2017
-- --
-- Copyright 2016-2017 by Samuel Devulder -- Copyright 2016-2017 by Samuel Devulder
-- --
-- This program is free software; you can redistribute -- This program is free software; you can redistribute
-- it and/or modify it under the terms of the GNU -- it and/or modify it under the terms of the GNU
-- General Public License as published by the Free -- General Public License as published by the Free
-- Software Foundation; version 2 of the License. -- Software Foundation; version 2 of the License.
-- See <http://www.gnu.org/licenses/> -- See <http://www.gnu.org/licenses/>
run('lib/ostromoukhov.lua') run('lib/ostromoukhov.lua')
OstroDither:new():dither40cols(function(w,h,getLinearPixel) OstroDither:new():dither40cols(function(w,h,getLinearPixel)
local pal={} local pal={}
for i=0,7 do pal[i+1] = thomson.palette(i) end for i=0,7 do pal[i+1] = thomson.palette(i) end
return pal return pal
end) end)

View File

@ -1,82 +1,82 @@
-- ostro_to8.lua : convert a color image to a BM16 -- ostro_to8.lua : convert a color image to a BM16
-- (160x200x16) thomson image using the Ostromoukhov's -- (160x200x16) thomson image using the Ostromoukhov's
-- error diffusion algorithm. -- error diffusion algorithm.
-- --
-- Version: 02-jan-2017 -- Version: 02-jan-2017
-- --
-- Copyright 2016-2017 by Samuel Devulder -- Copyright 2016-2017 by Samuel Devulder
-- --
-- This program is free software; you can redistribute -- This program is free software; you can redistribute
-- it and/or modify it under the terms of the GNU -- it and/or modify it under the terms of the GNU
-- General Public License as published by the Free -- General Public License as published by the Free
-- Software Foundation; version 2 of the License. -- Software Foundation; version 2 of the License.
-- See <http://www.gnu.org/licenses/> -- See <http://www.gnu.org/licenses/>
run('lib/thomson.lua') run('lib/thomson.lua')
run('lib/ostromoukhov.lua') run('lib/ostromoukhov.lua')
run('lib/color_reduction.lua') run('lib/color_reduction.lua')
-- run('lib/zzz.lua') -- run('lib/zzz.lua')
-- get screen size -- get screen size
local screen_w, screen_h = getpicturesize() local screen_w, screen_h = getpicturesize()
-- Converts thomson coordinates (0-159,0-199) into screen coordinates -- Converts thomson coordinates (0-159,0-199) into screen coordinates
local function thom2screen(x,y) local function thom2screen(x,y)
local i,j; local i,j;
if screen_w/screen_h < 1.6 then if screen_w/screen_h < 1.6 then
i = x*screen_h/200 i = x*screen_h/200
j = y*screen_h/200 j = y*screen_h/200
else else
i = x*screen_w/320 i = x*screen_w/320
j = y*screen_w/320 j = y*screen_w/320
end end
return math.floor(i*2), math.floor(j) return math.floor(i*2), math.floor(j)
end end
-- return the Color @(x,y) in normalized linear space (0-1) -- return the Color @(x,y) in normalized linear space (0-1)
-- corresonding to the thomson screen (x in 0-319, y in 0-199) -- corresonding to the thomson screen (x in 0-319, y in 0-199)
local function getLinearPixel(x,y) local function getLinearPixel(x,y)
local x1,y1 = thom2screen(x,y) local x1,y1 = thom2screen(x,y)
local x2,y2 = thom2screen(x+1,y+1) local x2,y2 = thom2screen(x+1,y+1)
if x2==x1 then x2=x1+1 end if x2==x1 then x2=x1+1 end
if y2==y1 then y2=y1+1 end if y2==y1 then y2=y1+1 end
local p = Color:new(0,0,0); local p = Color:new(0,0,0);
for j=y1,y2-1 do for j=y1,y2-1 do
for i=x1,x2-1 do for i=x1,x2-1 do
p:add(getLinearPictureColor(i,j)) p:add(getLinearPictureColor(i,j))
end end
end end
p:div((y2-y1)*(x2-x1)) --:floor() p:div((y2-y1)*(x2-x1)) --:floor()
return p return p
end end
local red = ColorReducer:new():analyzeWithDither(160,200, local red = ColorReducer:new():analyzeWithDither(160,200,
getLinearPixel, getLinearPixel,
function(y) function(y)
thomson.info("Collecting stats...",math.floor(y/2),"%") thomson.info("Collecting stats...",math.floor(y/2),"%")
end) end)
-- BM16 mode -- BM16 mode
thomson.setBM16() thomson.setBM16()
-- define palette -- define palette
local palette = red:boostBorderColors():buildPalette(16) local palette = red:boostBorderColors():buildPalette(16)
thomson.palette(0, palette) thomson.palette(0, palette)
-- convert picture -- convert picture
OstroDither:new(palette) OstroDither:new(palette)
:dither(thomson.h,thomson.w, :dither(thomson.h,thomson.w,
function(y,x) return getLinearPixel(x,y) end, function(y,x) return getLinearPixel(x,y) end,
function(y,x,c) thomson.pset(x,y,c) end, function(y,x,c) thomson.pset(x,y,c) end,
true, true,
function(x) thomson.info("Converting...",math.floor(x*100/160),"%") end) function(x) thomson.info("Converting...",math.floor(x*100/160),"%") end)
-- refresh screen -- refresh screen
setpicturesize(320,200) setpicturesize(320,200)
thomson.updatescreen() thomson.updatescreen()
finalizepicture() finalizepicture()
-- save picture -- save picture
thomson.savep() thomson.savep()

View File

@ -1,47 +1,47 @@
-- ostro_mo5.lua : converts a color image into a -- ostro_mo5.lua : converts a color image into a
-- TO9 image (320x200x16 with color clashes) -- TO9 image (320x200x16 with color clashes)
-- using Ostromoukhov's error diffusion algorithm. -- using Ostromoukhov's error diffusion algorithm.
-- --
-- Version: 02-jan-2017 -- Version: 02-jan-2017
-- --
-- Copyright 2016-2017 by Samuel Devulder -- Copyright 2016-2017 by Samuel Devulder
-- --
-- This program is free software; you can redistribute -- This program is free software; you can redistribute
-- it and/or modify it under the terms of the GNU -- it and/or modify it under the terms of the GNU
-- General Public License as published by the Free -- General Public License as published by the Free
-- Software Foundation; version 2 of the License. -- Software Foundation; version 2 of the License.
-- See <http://www.gnu.org/licenses/> -- See <http://www.gnu.org/licenses/>
run('lib/ostromoukhov.lua') run('lib/ostromoukhov.lua')
run('lib/color_reduction.lua') run('lib/color_reduction.lua')
OstroDither:new():dither40cols(function(w,h,getLinearPixel) OstroDither:new():dither40cols(function(w,h,getLinearPixel)
local c16 = h==200 and w==320 local c16 = h==200 and w==320
for y=0,h-1 do for y=0,h-1 do
for x=0,w-1 do for x=0,w-1 do
if getbackuppixel(x,y)>15 then c16 = false end if getbackuppixel(x,y)>15 then c16 = false end
end end
end end
local pal local pal
if c16 then if c16 then
pal = {} pal = {}
for i=0,15 do for i=0,15 do
local r,g,b=getbackupcolor(i) local r,g,b=getbackupcolor(i)
r = thomson.levels.pc2to[r] r = thomson.levels.pc2to[r]
g = thomson.levels.pc2to[g] g = thomson.levels.pc2to[g]
b = thomson.levels.pc2to[b] b = thomson.levels.pc2to[b]
pal[i+1] = r+g*16+b*256-273 pal[i+1] = r+g*16+b*256-273
end end
else else
pal=ColorReducer:new():analyzeWithDither(w,h, pal=ColorReducer:new():analyzeWithDither(w,h,
getLinearPixel, getLinearPixel,
function(y) function(y)
thomson.info("Building palette...",math.floor(y*100/h),"%") thomson.info("Building palette...",math.floor(y*100/h),"%")
end):boostBorderColors():buildPalette(16) end):boostBorderColors():buildPalette(16)
end end
thomson.palette(0, pal) thomson.palette(0, pal)
return pal return pal
end) end)