Skip to content

Instantly share code, notes, and snippets.

@JMV38
Created November 24, 2012 13:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JMV38/4139706 to your computer and use it in GitHub Desktop.
Save JMV38/4139706 to your computer and use it in GitHub Desktop.
Bug saveImage
--# Main
-- 0 conversion
-- unit test of variuos fonctions
function setup()
convert = Convert()
-- types
typeUnitTests()
-- integer
intUnitTests()
-- float
floatUnitTests()
-- string
stringUnitTests()
testString2("hello world")
-- tables
myobj = Object2()
testPush("name",myobj)
testPush("version",myobj)
testPushImage("texture",myobj.ms)
listTypes(myobj)
print("vertices : "..convert:type(myobj.ms.vertices))
print("texCoords : "..convert:type(myobj.ms.texCoords))
print("colors : "..convert:type(myobj.ms.colors))
testPushColors("colors",myobj.ms)
testPushVec3("vertices",myobj.ms)
testPushVec2("texCoords",myobj.ms)
testPushMatrix("m",myobj)
testPushObject(myobj)
testSaveLoad(myobj)
end
function testSaveLoad(obj)
print(" ------------- ")
obj:saveLocal("titi")
obj:loadLocal("titi")
print(obj.name)
sprite()
end
function testPushObject(obj)
print(" ------------- ")
print(" push and pop object")
local img = convert:pushObject00(obj)
print("result image:"..tostring(img))
local t,n = convert:getTable(1,img)
print("First value:")
print(t,n)
local obj2 = {}
obj2,n = convert:popObject00(img)
local i,v
for i,v in pairs(obj2) do
print(i.." : "..tostring(v))
end
end
function listTypes(a)
print(" ------------- ")
for t,v in pairs(a) do
print(t.." : "..convert:type(v))
end
end
function typeUnitTests()
local t
t = 5
print(convert:type(t),t)
t = vec2(1,2)
print(convert:type(t),t)
t = vec3(1,2,3)
print(convert:type(t),t)
t = color(146, 45, 45, 255)
print(convert:type(t),t)
t = matrix()
print(convert:type(t),t)
t = image(10,10)
print(convert:type(t),t)
end
function testPushMatrix(tag,obj)
print(" ------------- ")
print("object.matrix to color table, and back")
local t,p = {},{}
print(tostring(obj[tag]))
local i,v,ob
convert:pushToTable(tag,obj,t)
print("intermediate table has "..#t.." elements")
convert:popToObject(t,p)
print(tostring(p[tag]))
end
function testPushVec2(tag,obj)
print(" ------------- ")
print("object.{Vec2} to color table, and back")
local t,p = {},{}
print(obj[tag])
print("initial table has "..#obj[tag].." elements")
local i,v,ob
ob = obj[tag]
for i,v in ipairs(ob) do print(tostring(ob[i])) end
convert:pushToTable(tag,obj,t)
print("intermediate table has "..#t.." elements")
convert:popToObject(t,p)
print("final table has "..#p[tag].." elements")
ob = p[tag]
for i,v in ipairs(ob) do print(tostring(ob[i])) end
end
function testPushVec3(tag,obj)
print(" ------------- ")
print("object.{Vec3} to color table, and back")
local t,p = {},{}
print(obj[tag])
print("initial table has "..#obj[tag].." elements")
local i,v,ob
ob = obj[tag]
for i,v in ipairs(ob) do print(tostring(ob[i])) end
convert:pushToTable(tag,obj,t)
print("intermediate table has "..#t.." elements")
convert:popToObject(t,p)
print("final table has "..#p[tag].." elements")
ob = p[tag]
for i,v in ipairs(ob) do print(tostring(ob[i])) end
end
function testPushColors(tag,obj)
print(" ------------- ")
print("object.{color} to color table, and back")
local t,p = {},{}
print(obj[tag])
print("initial table has "..#obj[tag].." elements")
local i,v,ob
ob = obj[tag]
for i,v in ipairs(ob) do print(tostring(ob[i])) end
convert:pushToTable(tag,obj,t)
print("intermediate table has "..#t.." elements")
convert:popToObject(t,p)
print("final table has "..#p[tag].." elements")
ob = p[tag]
for i,v in ipairs(ob) do print(tostring(ob[i])) end
end
function testPushImage(tag,obj)
print(" ------------- ")
print("object.image to color table")
local t,p = {},{}
print(obj[tag])
convert:pushToTable(tag,obj,t)
print(#t)
convert:popToObject(t,p)
print(p[tag])
img1 = p[tag]
end
function testPush(tag,obj)
local t = {}
print(" ------------- ")
print("object <=> table")
print(tag.." : "..tostring(obj[tag]))
convert:pushToTable(tag,obj,t)
print(tostring(t))
local p = {}
local tag = convert:popToObject(t,p)
print(tag.." : "..tostring(p[tag]))
end
function testString2(txt)
local t = {}
convert:pushString(txt,t)
--print(txt.." = "..table.concat(t,"."))
print(txt.." = "..tostring(t))
end
function stringUnitTests()
testString()
testString("")
testString("h")
testString("he")
testString("hel")
testString("hell")
testString("=")
testError(testString,"[{()}]")
end
function testError(f,arg)
local status,err = pcall(f,arg)
if status==false
then print("ok : "..tostring(arg).." has generated an error : "..err)
else print("FALSE : this should generate an error : "..tostring(arg))
end
end
function testString(i)
local c,t,txt
if i then
c=convert:stringToColor(i)
t=convert:colorToString(c)
if (i==t) then txt="ok" else txt="FALSE" end
print(txt.." : '"..i.."'".." = "..tostring(c).." = ".."'"..t.."'")
else
print("")
print("string to color and back to string")
end
end
function intUnitTests()
print("test of conversion functions")
print("")
print("intToColor")
testUInt(1)
testUInt(1000)
testUInt(1000000)
testUInt(100000000)
testError(testUInt,-1)
testError(testUInt,256*256*256*256+10)
end
function testUInt(i)
local c,i1,txt
c=convert:intToColor(i)
i1=convert:colorToInt(c)
if (i==i1) then txt="ok" else txt="FALSE" end
print(txt.." : "..i.." = "..tostring(c).." = "..i1)
end
function floatUnitTests()
test2()
test2(2)
test2(1003)
test2(1000003)
test2(-19)
test2(-8000)
test2(-1000003)
test2(0.2)
test2(0.00003)
test2(0.00000000003)
test2(-0.5)
test2(-0.00003)
test2(-0.000008888888)
test2(math.random())
test2(math.pi)
test2(math.pi*math.pow(10,22))
test2(-math.pi*math.pow(10,22))
end
function test2(i)
local precision = 10/256/256/255
if i then
local c,i1,t,txt
c=convert:floatToColor(i)
i1=convert:colorToFloat(c)
local err = (i-i1)/i
if math.abs(err)<precision then txt="ok" else txt="FALSE" end
print(txt.." : "..i.." = "..tostring(c).." = "..i1)
else
print("")
print("floatToColor and back to float")
print("precision required : "..precision)
end
end
-- This function gets called once every frame
function draw()
-- This sets a dark background color
background(40, 40, 50)
-- This sets the line thickness
strokeWidth(5)
-- Do your drawing here
sprite( myobj.ms.texture,WIDTH/4,HEIGHT/2)
sprite(img1,WIDTH/2,HEIGHT/2)
sprite("Documents:titi",WIDTH/2*3,HEIGHT/2)
end
--# Convert
Convert = class()
-- converting a 3d object into a color image is done in steps:
-- 1/ convert each element of object in an image
-- 2/ assemble the image elements in one big image
-- the reading is the same in inverse order
-- each element is one of the following types:
-- ["xxx"]={{uint}} or
-- ["xxx"]={vec3} or
-- ["xxx"]={vec2} or
-- ["xxx"]={colors} or
-- ["xxx"]=image or
-- ["xxx"]=string or
-- ["m"]=matrix or
-- so the conversion can simply be:
-- total number of pixels = tag = type = values
-- ["xxx"]={{uint}}
-- => {n0,"=","xxx","=","{{uint}}","=",1,v1,2,v1,v2,1,v1,...}
-- => n0.=000.xxx0.=000.{{ui.nt}}.=000.n1.v1.n2.v1.v2. etc n = number of vals, v = vals
-- characters are directly converted to ascii, except 0 above meaning 0/255,
-- and the 0 are discarded during readout.the dots .xxx0. mean 1 color .rgba.
-- "=" will be used as a separator. uint and float are coded on 4 octets.
-- n0 is the number of pixels remaining to be read.
-- ["xxx"]={vec3}
-- => n0.=000.xxx0.=000.{vec.3}00.=000.v1x.v1y.v1z.v2x. etc...
-- ["xxx"]={vec2}
-- => n0.=000.xxx0.=000.{vec.2}00.=000.v1x.v1y.v2x.v2y. etc...
-- ["xxx"]={colors}
-- => n0.=000.xxx0.=000.{col.or}0.=000.rgba.rgba.rgba. etc...
-- ["xxx"]=image
-- => n0.=000.xxx0.=000.imag.e000.=000.w.h.=000.rgba.rgba.rgba. etc...
-- ["xxx"]=string
-- => n0.=000.xxx0.=000.strin.g000.=000.hell.o wo.ld00. etc...
-- ["m"]=matrix
-- => n0.=000.xxx0.=000.matr.ix00.=000.m1.m2.m3.m4. etc...
-- each image is w =2048 and the number of row needed
-- The assembly of individual images into a big image will be:
-- individual images on top of another => easy to read back.
-- or can be assembled one after the other to save memory
-- if the result is less than 1x2048, then the image can be made smaller.
-- note that (0,0,0,0) pixels means: nothing left, we're finished.
-- the first pixels are a string "JMV38-3d-object-version-" to recognize the image is usable
-- the next 2 pixels are uint coding the version of object (tells me how to read it)
-- the advantages of above method are
-- compact
-- easily expandable
-- fast transfer
-- accurate
-- everything can be saved
function Convert:init()
end
-- local functions to be faster
local insert = table.insert
local floor = math.floor
function Convert:popObject00(img)
local n
local t,out = {},{}
-- read verification information
n = 1
t,n = self:getTable(n,img)
print(t,n)
self:popToObject(t,out)
t,n = self:getTable(n,img)
self:popToObject(t,out)
if out.id~="JMV38-3d-object"
then error("this image is not a 3d object saved to an image")
elseif out.version ~= "00"
then error("the object version is not compatible with this reader")
end
print(out.id)
print(out.version)
-- get other elements
t,n = self:getTable(n,img)
while t do
self:popToObject(t,out)
t,n = self:getTable(n,img)
end
return out,n
end
function Convert:pushObject00(obj)
local identifier = "JMV38-3d-object"
local version = "00"
local input = {}
input.id = identifier
input.version = version
-- put everything in a table
local t ={}
self:pushToTable("id",input,t)
self:pushToTable("version",input,t)
--[[
local i,v
for i,v in pairs(obj) do
self:pushToTable(i,obj,t)
end
self:pushToTable("name",obj,t)
self:pushToTable("m",obj,t)
self:pushToTable("texture",obj.ms,t)
self:pushToTable("vertices",obj.ms,t)
self:pushToTable("texCoords",obj.ms,t)
self:pushToTable("colors",obj.ms,t)
]]
-- 1 more pixel at 0 to indicate it is finished (and avoid image overflow)
insert(t, self:intToColor(0) )
-- convert to an image and return it
return self:tableToImage(t)
end
function Convert:tableToImage(t)
-- write table in an image
local n = #t
local h = floor(n/2048)+1
local w
if n>2048 then w=2048 else w=n end
local img = image(w,h)
local x,y,i
for i=1,n do
y = floor((i-1)/2048)
x = i-1 - y*2048
img:set(x+1,y+1,t[i])
-- if self:type(t[i])~="color" then
-- error(i.." : "..tostring(t[i]).." : "..self:type(t[i])) end
end
return img
end
function Convert:xy(i)
local x,y
y = floor((i-1)/2048)
x = i-1 - y*2048
return x+1,y+1
end
function Convert:getTable(n,img)
-- write some of the colors into a table
local w,h = img.width, img.height
local x,y,i
local t = {}
nmax = self:colorToInt( color(img:get(self:xy(n))) )
if nmax==0 then t=nil
else
for i=0,nmax do
t[i+1] = color(img:get( self:xy(i+n) ))
end
end
return t,(n+nmax+1)
end
-- **********************************************
function Convert:supported(typ)
local ans = false
if typ=="string" then ans = true
elseif typ=="image" then ans = true
elseif typ=="{color}"then ans = true
elseif typ=="{vec3}" then ans = true
elseif typ=="{vec2}" then ans = true
elseif typ=="matrix" then ans = true
end
return ans
end
function Convert:pushToTable(tag,obj,out)
local x = obj[tag]
local typ = self:type(x)
if self:supported(typ) then
local n = #out
self:pushString(tag,out)
insert(out, self:stringToColor("=") )
self:pushString(typ,out)
insert(out, self:stringToColor("=") )
if typ=="string" then self:pushString(x,out)
elseif typ=="image" then self:pushImage(x,out)
elseif typ=="{color}"then self:pushColors(x,out)
elseif typ=="{vec3}" then self:pushVec3(x,out)
elseif typ=="{vec2}" then self:pushVec2(x,out)
elseif typ=="matrix" then self:pushMatrix(x,out)
end
local n1 = #out - n
insert(out, n+1, self:intToColor(n1) )
else
print("the type of '"..tag.."' is not supported for saving: "..typ)
end
return out
end
function Convert:pushMatrix(m,out)
local i
for i=1,16 do
insert(out,self:floatToColor(m[i]))
end
end
function Convert:pushVec2(t,out)
local i,v
for i,v in ipairs(t) do
insert(out,self:floatToColor(v.x))
insert(out,self:floatToColor(v.y))
end
end
function Convert:pushVec3(t,out)
local i,v
for i,v in ipairs(t) do
insert(out,self:floatToColor(v.x))
insert(out,self:floatToColor(v.y))
insert(out,self:floatToColor(v.z))
end
end
function Convert:pushColors(t,out)
local i,v
for i,v in ipairs(t) do
insert(out,v)
end
end
function Convert:pushImage(img,out)
local w,h,x,y
w,h = img.width, img.height
insert(out,self:intToColor(w))
insert(out,self:intToColor(h))
for x=1,w do
for y=1,h do
insert(out,color(img:get(x,y)))
end
end
end
function Convert:pushString(txt,out)
local t1,t2
if txt:len()>4 then
t1 = txt:sub(1,4)
t2 = txt:sub(5)
else
t1 = txt
end
insert(out,self:stringToColor(t1))
if t2 then self:pushString(t2,out) end
end
-- **********************************************
function Convert:getString(t,n)
local p = n+1
while self:colorToString(t[p]) ~= "=" do p = p + 1 end
local res = {}
local i
-- for i=1,(p-n-1) do insert(res,t[n+i]) end
for i=1,(p-n-1) do insert(res, self:colorToString(t[n+i]) ) end
return table.concat(res,""),p
end
function Convert:popToObject(t,obj)
local nmax = self:colorToInt( t[1] ) + 1
local n = 1
local tag,typ
tag,n = self:getString(t,n)
typ,n = self:getString(t,n)
local x,i
local out
if typ=="string" then out,n = self:popString(n,nmax,t)
elseif typ=="image" then out,n = self:popImage(n,nmax,t)
elseif typ=="{color}" then out,n = self:popColors(n,nmax,t)
elseif typ=="{vec3}" then out,n = self:popVec3(n,nmax,t)
elseif typ=="{vec2}" then out,n = self:popVec2(n,nmax,t)
elseif typ=="matrix" then out,n = self:popMatrix(n,nmax,t)
end
obj[tag]=out
return tag,n
end
function Convert:popMatrix(n,nmax,t)
local i
local out = matrix()
for i=1,16 do
out[i] = self:colorToFloat( t[i+n] )
end
return out,n+16+1
end
function Convert:popVec2(n,nmax,t)
local i=1
local out = {}
while i<(nmax-n) do
local v = vec2()
v.x = self:colorToFloat( t[i+n] )
v.y = self:colorToFloat( t[i+n+1] )
out[(i+1)/2] = v
i = i + 2
end
return out,n+i
end
function Convert:popVec3(n,nmax,t)
local i=1
local out = {}
while i<(nmax-n) do
local v = vec3()
v.x = self:colorToFloat( t[i+n] )
v.y = self:colorToFloat( t[i+n+1] )
v.z = self:colorToFloat( t[i+n+2] )
out[(i+2)/3] = v
i = i + 3
end
return out,n+i
end
function Convert:popColors(n,nmax,t)
local i
local out = {}
for i=1,nmax-n do out[i] = t[i+n] end
return out,n+nmax+1
end
function Convert:popImage(n,nmax,t)
local w,h,x,y
w = self:colorToInt(t[n+1])
h = self:colorToInt(t[n+2])
local out = image(w,h)
local i = n+3
for x=1,w do
for y=1,h do
out:set(x,y,t[i])
i = i + 1
end
end
return out,n+i
end
function Convert:popString(n,nmax,t)
local i
local out = {}
for i=1,nmax-n do out[i] = self:colorToString(t[i+n]) end
out = table.concat(out,"")
return out,nmax+1
end
-- **********************************************
-- basic function to convert to and from colors
local abs = math.abs
local log10 = math.log10
local pow = math.pow
-- type(v):"nil", "number", "string", "boolean", "table", "function", "thread", "userdata".
function Convert:type(x)
local txt = type(x)
function typeCompare(x,ref)
-- nb: returns an error if x or ref have no metatable!
local i = getmetatable(x).__index
local j = getmetatable(ref).__index
return (i==j)
end
if txt == "userdata" then
if typeCompare(x,vec2()) then txt ="vec2"
elseif typeCompare(x,vec3()) then txt ="vec3"
elseif typeCompare(x,color()) then txt ="color"
elseif typeCompare(x,image(1,1)) then txt ="image"
elseif typeCompare(x,matrix()) then txt ="matrix"
end
end
if txt == "table" then
txt = "{"..self:type(x[1]).."}"
end
return txt
end
function Convert:intToColor(dat)
-- abs value coded on 4 octets uint (r,g,b,a)
local r,g,b,a = 0,0,0,0
local x
-- chech no sign
x = abs(dat)
if x~=dat then error("only uint supported : "..dat) end
-- convert in octet basis
r = x % 256
x = floor(x/256)
g = x % 256
x = floor(x/256)
b = x % 256
x = floor(x/256)
a = x
if a>255 then error("this integer is too big to save : "..dat) end
return color(r,g,b,a)
end
function Convert:colorToInt(c)
local r,g,b,a = c.r, c.g, c.b, c.a
local i = r +256*(g + 256*(b +256*a))
return i
end
function Convert:floatToColor(dat)
-- abs value coded on 3 octets (r,g,b)
-- a is coding the power of 10, from -64 to +63
-- the sign is coded by 1 bit (+128) in a
-- accuracy limit: about less than 10/(256*256*256) relative error
local r,g,b,a = 0,0,0,0
local x,sgn,p,c
-- remove the sign and save it
x = abs(dat)
if x==dat then sgn = 0 else sgn = 128 end
-- get the exponent
p = log10(x)
p = floor(p)
if (p<-64) or (63<p) then -- overflow
-- in case of overflow clip the data
if p<-64 then p=-64 else p=63 end
r,g,b,a =255,255,255,127
else
-- convert to int
x = x / pow(10,p)/10 -- number x is now : 0.abcdefg with a from 1 to 9
x = floor( x *255 * 256 * 256 )
-- convert to octet basis
c = self:intToColor(x)
r,g,b,a = c.r, c.g, c.b, c.a
if a>0 then error("bug: a should be 0") end
end
-- a saves sign and exponent
a = (p + 64) + sgn
return color(r,g,b,a)
end
function Convert:colorToFloat(c)
local r,g,b,a = c.r, c.g, c.b, c.a
local sgn
if a >127 then sgn =-1 ; a=a-128 else sgn =1 end
local p = a - 64
local x = ((r/256 + g)/256 +b)/255 * pow(10,p) * 10 * sgn
return x
end
function Convert:stringToColor(t)
local r,g,b,a = 0,0,0,0
local l = t:len()
if l>0 then r = t:byte(1) end
if l>1 then g = t:byte(2) end
if l>2 then b = t:byte(3) end
if l>3 then a = t:byte(4) end
if l>4 then error("the text: "..t.." is too long (4 chars max).") end
return color(r,g,b,a)
end
function Convert:colorToString(c)
local r,g,b,a = c.r, c.g, c.b, c.a
local txt = ""
if a>0 then txt = string.char(r,g,b,a)
elseif b>0 then txt = string.char(r,g,b)
elseif g>0 then txt = string.char(r,g)
elseif r>0 then txt = string.char(r)
end
return txt
end
--# Object2
Object2 = class()
-- 2012-11-12
-- the 'object' class was too complex, this new class is much simpler
-- the 'object1' class suffered of too much freedom in tables, that could be bad for performance
function Object2:init(args)
self.name ="object_name" -- a string
self.version = "00" -- version of couple: object + save method
self.m = matrix() -- position of the object
self.sv = { {1},{2},{3} } -- summit vectors: for each summit a table of vertice indexes
self.sf = { {1},{1},{1} } -- summit faces: for each summit a table of face indexes
self.fs = { {1,2,3} } -- face summits: each face has 3 summit index in a table
-- nota: if 1,2,3 is a face, 12:cross(13) is oriented to the outside of the object
local z = vec3(0,0,1)
-- normals: at least one of those is needed to draw shadows:
self.sn = { z, z, z} -- summit normals: for each summit a vec3 (optionnal)
self.fn = {z} -- face normals: for each face a vec3 (optionnal)
self.vn = { z, z, z} -- vertices normals: for each vertice a vec3 (optionnal)
self.ms = mesh() -- mesh
-- vertices: a table of vec3, by group of 3, corresponding to faces index*3+0,1,2
self.ms.vertices = {vec3(-50,-50,0),vec3(50,-50,0),vec3(-50,50,0)}
-- texture coords: a table of vec2, by group of 3, corresponding to faces index*3+0,1,2
-- self.ms.texCoords = {vec2(0,0),vec2(1,0),vec2(0,1)}
self.ms.texCoords = {vec2(3/32,3/32),vec2(29/32,3/32),vec2(3/32,29/32)}
local img = readImage("Documents:mechanics")
--setContext(img) background(255, 255, 255, 255) setContext()
self.ms.texture = img
-- colors: a table of colors, by group of 3, corresponding to faces index*3+0,1,2
-- self.ms.colors = {color(255, 0, 0, 255),color(0, 255, 2, 255),color(0, 19, 255, 255)}
local white = color(255, 255,255, 255)
self.ms.colors = {white,white,white}
self.img = img -- an image for various usages
self.icon = image(101,101) -- an image used to save a small view of the object
self.visible = true
-- functions to convert object <=> image
self.converter = Convert()
end
function Object2:saveLocal(name)
local obj = {}
obj.name = self.name
obj.m = self.m
--[[
obj.texture = self.ms.texture
obj.vertices = self.ms.vertices
obj.texCoords = self.ms.texCoords
obj.colors = self.ms.colors
]]
local img = self.converter:pushObject00(obj)
local saveName = "Documents:"..name
print("First value in saveLocal:")
print(saveName)
print(img)
print(img:get(1,1))
saveImage(saveName, img)
local img2 = readImage(saveName)
print("First value in loadLocal:")
print(saveName)
print(img2)
print(img2:get(1,1))
end
function Object2:loadLocal(name)
local saveName = "Documents:"..name
local img = readImage(saveName)
--[[
print("First value in loadLocal:")
print(saveName)
print(img)
print(img:get(1,1))
]]
local obj = self.converter:popObject00(img)
self.name = obj.name
self.m = obj.m
self.ms.texture = obj.texture
self.ms.vertices = obj.vertices
self.ms.texCoords = obj.texCoords
self.ms.colors = obj.colors
end
function Object2:draw()
pushMatrix()
pushStyle()
modelMatrix( self.m * modelMatrix() )
if self.visible then self.ms:draw() end
popStyle()
popMatrix()
end
function Object2:touched(touch)
-- Codea does not automatically call this method
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment