Created
November 25, 2012 17:22
-
-
Save JMV38/4144412 to your computer and use it in GitHub Desktop.
Mesh loader
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--# 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 | |
function Convert:init() | |
end | |
function Convert:saveMesh(ms,name,debug) | |
local obj = {} | |
obj.texture = ms.texture | |
obj.vertices = ms.vertices | |
obj.texCoords = ms.texCoords | |
obj.colors = ms.colors | |
local img,size = self:pushObject00(obj,debug) | |
if debug then print("mesh size = "..math.floor(size*100).."% of max size") end | |
self:saveImage(name, img) | |
return size | |
end | |
function Convert:loadMesh(name,debug) | |
local img = self:readImage(name) | |
local obj = self:popObject00(img,debug) | |
local ms = mesh() | |
ms.texture = obj.texture | |
ms.vertices = obj.vertices | |
ms.texCoords = obj.texCoords | |
ms.colors = obj.colors | |
return ms | |
end | |
-- local functions to be faster | |
local insert = table.insert | |
local floor = math.floor | |
function Convert:saveImage(name,img) | |
local w,h = img.width, img.height | |
local x,y,r,g,b,a | |
local out = image(w,h*2) | |
for x=1,w do for y=1,h do | |
r,g,b,a = img:get(x,y) | |
out:set(x,y*2-1,r,g,0,255) | |
out:set(x,y*2 ,b,a,0,255) | |
end end | |
saveImage("Documents:"..name,out) | |
end | |
function Convert:readImage(name) | |
local img = readImage("Documents:"..name) | |
local w,h = img.width, img.height | |
local x,y,r,g,b,a,_ | |
local out = image(w,h/2) | |
for x=1,w do for y=1,h/2 do | |
r,g,_,_ = img:get(x,y*2-1) | |
b,a,_,_ = img:get(x,y*2) | |
out:set(x,y,r,g,b,a) | |
end end | |
return out | |
end | |
function debugPrint(debug,txt,t) | |
if debug then | |
local str = "" | |
if type(t)=="table" then str ="{"..tostring(t[1])..",... }" | |
elseif t~=nil then str = tostring(t) | |
end | |
print(txt..str) | |
end | |
end | |
function Convert:popObject00(img,debug) | |
local n | |
local t,out = {},{} | |
-- read verification information | |
n = 1 | |
t,n = self:getTable(n,img) | |
self:popToObject(t,out,debug) | |
t,n = self:getTable(n,img) | |
self:popToObject(t,out,debug) | |
if out.id~="JMV38-3d-object" | |
then error("this image is not a 3d object saved to an image") | |
elseif out.saveVersion ~= 0 | |
then error("the object version ("..out.saveVersion..") is not compatible with this reader") | |
end | |
-- get other elements | |
t,n = self:getTable(n,img) | |
while t do | |
self:popToObject(t,out,debug) | |
t,n = self:getTable(n,img) | |
end | |
return out,n | |
end | |
function Convert:pushObject00(obj,debug) | |
local input = {} | |
input.id = "JMV38-3d-object" | |
input.saveVersion = 0 | |
-- put everything in a table | |
local t ={} | |
self:pushToTable("id",input,t) | |
debugPrint(debug,"pushed : obj.".."id".." = ",input.id) | |
self:pushToTable("saveVersion",input,t) | |
debugPrint(debug,"pushed : obj.".."saveVersion".." = ",input.saveVersion) | |
local i,v | |
for i,v in pairs(obj) do | |
self:pushToTable(i,obj,t) | |
debugPrint(debug,"pushed : obj."..i.." = ",v) | |
end | |
-- 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 sizefraction = (h+1)/2048 | |
local txt | |
if sizefraction>1 then | |
txt = "Sorry, your mesh size once converted is: "..floor(sizefraction*100) | |
txt = txt.."% of maximum size, too big for this saving tool." | |
error(txt) | |
end | |
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,sizefraction | |
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=="number" 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=="number" then self:pushNumber(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:pushNumber(x,out) | |
insert(out,self:floatToColor(x)) | |
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 | |
-- this is a texCoord, so it is transformed in (1-y) | |
insert(out,self:floatToColor(v.x)) | |
insert(out,self:floatToColor(1.0-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,r,g,b,a | |
for i,v in ipairs(t) do | |
-- the color table is read from a mesh: it is normalized into [0.0;1.0] | |
r,g,b,a = v.r, v.g, v.b, v.a | |
r,g,b,a = floor(r*255),floor(g*255),floor(b*255),floor(a*255) | |
v = color(r,g,b,a) | |
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,debug) | |
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=="number" then out,n = self:popNumber(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 | |
debugPrint(debug,"pop : obj."..tag.." = ",out) | |
return tag,n | |
end | |
function Convert:popNumber(n,nmax,t) | |
local out | |
out = self:colorToFloat( t[1+n] ) | |
return out,n+1+1 | |
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 | |
if x==0 then p=0 else | |
p = log10(x) | |
p = floor(p) | |
end | |
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 | |
--# Controllers | |
function resetTest(v) | |
if v=="reset" then | |
confirmBox:popup("Do you really want to reset all controllers?", | |
function() print(confirmBox.choice) end) | |
--allControllers:resetDemo() | |
end | |
end | |
--# ConfirmBox | |
ConfirmBox = class() | |
function ConfirmBox:init() | |
self.message = "confirm this decision?" | |
self.choice = nil | |
self.active = false | |
self.callback = nil | |
-- outer box | |
self.x0,self.y0,self.w0,self.h0 = WIDTH/2, HEIGHT/2, WIDTH/8, HEIGHT/8 | |
-- text message | |
self.x1,self.y1,self.w1,self.h1 = WIDTH/2, HEIGHT*(1/2+1/20), WIDTH/9, HEIGHT/32 | |
-- confirm | |
self.x2,self.y2,self.w2,self.h2 = WIDTH*(1/2-1/16), HEIGHT*(1/2-1/16), WIDTH/20, HEIGHT/32 | |
-- cancel | |
self.x3,self.y3,self.w3,self.h3 = WIDTH*(1/2+1/16), HEIGHT*(1/2-1/16), WIDTH/20, HEIGHT/32 | |
end | |
function ConfirmBox:popup(message,callback) | |
self.message = message | |
self.callback = callback | |
self.active = true | |
self.choice = nil | |
end | |
function ConfirmBox:draw() | |
if self.active then | |
pushStyle() pushMatrix() | |
font("AmericanTypewriter-Bold") | |
rectMode(RADIUS) | |
textMode(RADIUS) | |
strokeWidth(5) | |
fill(127, 127, 127, 255) | |
stroke(0, 0, 0, 255) | |
fontSize(20) | |
rect(self.x0,self.y0,self.w0,self.h0) | |
rect(self.x2,self.y2,self.w2,self.h2) | |
rect(self.x3,self.y3,self.w3,self.h3) | |
fill(0, 0, 0, 255) | |
textWrapWidth(self.w1*2) | |
text("confirm",self.x2,self.y2) | |
text("cancel",self.x3,self.y3) | |
text(self.message,self.x1,self.y1) | |
popStyle() popMatrix() | |
end | |
end | |
function ConfirmBox:touched(touch) | |
local choice = nil | |
if self.active then | |
if math.abs((touch.x- self.x2))<self.w2 | |
and math.abs((touch.y- self.y2))<self.h2 | |
then choice = true | |
elseif math.abs((touch.x- self.x3))<self.w3 | |
and math.abs((touch.y- self.y3))<self.h3 | |
then choice = false | |
end | |
self.choice = choice | |
if choice~=nil then | |
self.active = false | |
if self.callback then self.callback() end | |
end | |
end | |
end | |
--# InfoBloc | |
InfoBloc = class() | |
function InfoBloc:init(args) | |
-- layout | |
self.title = args.title or "info" | |
self.txt = args.txt or "the side bar is highlighted when info is present" | |
self.side = args.side or "bottom" | |
self.textPos = args.textPos or vec2(WIDTH/2,HEIGHT-100) | |
self.textWidth = args.textWidth or 700 | |
self.popupEnabled = args.popupEnabled or true | |
self.t0 = ElapsedTime | |
self.timeout = 0.5 -- seconds before txt vanishes | |
-- settings by pos and w | |
local x = args.pos | |
local w = args.width or 0.09 | |
-- or by x0 and x1 | |
if args.x0 and args.x1 then | |
x = (args.x0 + args.x1)/2 | |
w = args.x1 - args.x0 | |
end | |
local w1,w2,h1,h2 | |
local pos | |
local dx0,dy0,dx,dy | |
local htxt = 30 | |
local w0,h0 = math.floor( w * WIDTH ), math.floor( w * HEIGHT ) | |
if self.side == "top" then | |
w1,w2,h1,h2 = w0,w0,htxt,htxt | |
pos = vec2(x*WIDTH, HEIGHT-h1/2) | |
dx0,dy0,dx,dy = 0, -h1-5, 0, -h2-2 | |
elseif self.side == "bottom" then | |
w1,w2,h1,h2 = w0,w0,htxt,htxt | |
pos = vec2(x*WIDTH, h1/2) | |
dx0,dy0,dx,dy = 0,h1+5, 0, h2+2 | |
elseif self.side == "left" then | |
w1,w2,h1,h2 = htxt,w0,h0,htxt | |
pos = vec2(w1/2, x*HEIGHT) | |
dx0,dy0,dx,dy = w1/2+5+w2/2, h1/2-h2/2, 0, -h2-2 | |
elseif self.side == "right" then | |
w1,w2,h1,h2 = htxt,w0,h0,htxt | |
pos = vec2(WIDTH-w1/2, x*HEIGHT) | |
dx0,dy0,dx,dy = -w1/2-5-w2/2, h1/2-h2/2, 0, -h2-2 | |
end | |
self.img = self:calcImg(self.title,w1,h1,self.side) | |
-- position of images | |
local x,y = pos.x,pos.y | |
self.pos = {} | |
self.radius = {} | |
self.pos["title"] = pos | |
self.radius["title"] = vec2(w1/2,h1/2) | |
-- manage various drawing states | |
self.deployed = false | |
self.highlighted = true -- the title is highlighted | |
end | |
function InfoBloc:resetDemo() | |
-- do nothing | |
end | |
function InfoBloc:changeTxt(txt) | |
if txt then | |
self.txt = txt | |
self.highlighted = true -- the title is highlighted | |
else | |
self.txt = nil | |
self.highlighted = false -- the title is not highlighted | |
end | |
-- reset timing | |
if self.popupEnabled then | |
self.t0 = ElapsedTime | |
self.finishing = true | |
end | |
end | |
function InfoBloc:calcImg(txt,w,h,rot) | |
local w0,h0 | |
pushStyle() pushMatrix() | |
if rot=="left" or rot=="right" | |
then w0,h0 = h,w | |
else w0,h0 = w,h | |
end | |
local img0 = image(w0,h0) | |
setContext(img0) | |
font("AmericanTypewriter-Bold") | |
rectMode(CENTER) | |
textMode(CENTER) | |
strokeWidth(1) | |
background(255, 255, 255, 184) | |
fill(0, 0, 0, 255) | |
stroke(0, 0, 0, 255) | |
fontSize(20) | |
text(txt,w0/2,h0/2) | |
setContext() | |
local img = image(w,h) | |
setContext(img) | |
background(255, 255, 255, 184) | |
spriteMode(CENTER) | |
translate(w/2,h/2) | |
if rot=="left" then rotate(-90) end | |
if rot=="right" then rotate(90) end | |
sprite(img0,0,0) | |
setContext() | |
popStyle() popMatrix() | |
return img | |
end | |
function InfoBloc:draw() | |
pushStyle() | |
spriteMode(CENTER) | |
-- menu title | |
if self.highlighted then | |
local n = 1 | |
tint(255,255,255,128 + (math.cos(ElapsedTime*2*math.pi*n)+1)/2*128 ) | |
else tint(255,255,255,128) | |
end | |
local pos = self.pos | |
-- menu button | |
sprite(self.img, pos["title"].x, pos["title"].y) | |
-- deployed or finishing | |
if self.deployed or self.finishing then | |
local isel = self.selected | |
local delta = (self.timeout-(ElapsedTime - self.t0))/self.timeout | |
if delta<0 then finishing = false end | |
local alpha = 255 | |
if self.finishing and not self.deployed then alpha = 255*delta end | |
font("AmericanTypewriter-Bold") | |
textMode(CENTER) | |
textAlign(LEFT) | |
textWrapWidth(self.textWidth) | |
strokeWidth(1) | |
fill(194, 194, 194, alpha) | |
stroke(0, 0, 0, 255) | |
fontSize(20) | |
if self.txt then text(self.txt,self.textPos.x,self.textPos.y) end | |
end | |
popStyle() | |
end | |
function InfoBloc:titleTouched(touch) | |
local pos,radius= self.pos["title"],self.radius["title"] | |
local goodZone = false | |
if math.abs((touch.x-pos.x))<radius.x | |
and math.abs((touch.y-pos.y))<radius.y | |
then | |
goodZone = true | |
end | |
self.choiceDone = false | |
return goodZone | |
end | |
function InfoBloc:touched(t) | |
if self:titleTouched(t) and t.state == BEGAN then | |
self.deployed=true | |
self.highlighted = false -- stop flickering | |
end | |
if t.state == ENDED and self.deployed then | |
self.deployed=false | |
-- reset timing | |
self.t0 = ElapsedTime | |
self.finishing = true | |
end | |
end | |
--# Menu | |
Menu = class() | |
function Menu:init(args) | |
-- actions | |
self.callback = args.callback or doNothing | |
-- layout | |
self.title = args.title or "menu" | |
local list = args.list or {"choice1",true,"choice2",true,"choice3",true} | |
self.list,self.disabled = {},{} | |
local imax = #list/2 | |
for i=1,imax do | |
self.list[i] = list[2*i-1] | |
self.disabled[i] = not list[2*i] | |
end | |
self.selected = args.selected or 1 | |
self.side = args.side or "bottom" | |
-- settings by pos and w | |
local x = args.pos | |
local w = args.width or 0.09 | |
-- or by x0 and x1 | |
if args.x0 and args.x1 then | |
x = (args.x0 + args.x1)/2 | |
w = args.x1 - args.x0 | |
end | |
local w1,w2,h1,h2 | |
local pos | |
local dx0,dy0,dx,dy | |
local htxt = 30 | |
local w0,h0 = math.floor( w * WIDTH ), math.floor( w * HEIGHT ) | |
if self.side == "top" then | |
w1,w2,h1,h2 = w0,w0,htxt,htxt | |
pos = vec2(x*WIDTH, HEIGHT-h1/2) | |
dx0,dy0,dx,dy = 0, -h1-5, 0, -h2-2 | |
elseif self.side == "bottom" then | |
w1,w2,h1,h2 = w0,w0,htxt,htxt | |
pos = vec2(x*WIDTH, h1/2) | |
dx0,dy0,dx,dy = 0,h1+5, 0, h2+2 | |
elseif self.side == "left" then | |
w1,w2,h1,h2 = htxt,w0,h0,htxt | |
pos = vec2(w1/2, x*HEIGHT) | |
dx0,dy0,dx,dy = w1/2+5+w2/2, h1/2-h2/2, 0, -h2-2 | |
elseif self.side == "right" then | |
w1,w2,h1,h2 = htxt,w0,h0,htxt | |
pos = vec2(WIDTH-w1/2, x*HEIGHT) | |
dx0,dy0,dx,dy = -w1/2-5-w2/2, h1/2-h2/2, 0, -h2-2 | |
end | |
-- create text images | |
self.img = {} | |
for i,txt in ipairs(self.list) do | |
self.img[i] = self:calcImg(txt,w2,h2) | |
end | |
self.img["title"] = self:calcImg(self.title,w1,h1,self.side) | |
-- position of images | |
local x,y = pos.x,pos.y | |
self.pos = {} | |
self.radius = {} | |
self.pos["title"] = pos | |
self.radius["title"] = vec2(w1/2,h1/2) | |
for i,img in ipairs(self.img) do | |
if i==1 then x,y = x+dx0,y+dy0 | |
else x,y = x+dx,y+dy end | |
self.pos[i] = vec2(x,y) | |
self.radius[i] = vec2(w2/2,h2/2) | |
end | |
-- manage various drawing states | |
self.deployed = false | |
self.highlighted = nil -- the choice highlighted | |
end | |
function Menu:resetDemo() | |
-- do nothing | |
end | |
function Menu:calcImg(txt,w,h,rot) | |
local w0,h0 | |
pushStyle() pushMatrix() | |
if rot=="left" or rot=="right" | |
then w0,h0 = h,w | |
else w0,h0 = w,h | |
end | |
local img0 = image(w0,h0) | |
setContext(img0) | |
font("AmericanTypewriter-Bold") | |
rectMode(CENTER) | |
textMode(CENTER) | |
strokeWidth(1) | |
-- background(255, 255, 255, 184) | |
background(255, 255, 255, 255) | |
fill(0, 0, 0, 255) | |
stroke(0, 0, 0, 255) | |
fontSize(20) | |
text(txt,w0/2,h0/2) | |
setContext() | |
local img = image(w,h) | |
setContext(img) | |
-- background(255, 255, 255, 184) | |
background(255, 255, 255, 255) | |
spriteMode(CENTER) | |
translate(w/2,h/2) | |
if rot=="left" then rotate(-90) end | |
if rot=="right" then rotate(90) end | |
sprite(img0,0,0) | |
setContext() | |
popStyle() popMatrix() | |
return img | |
end | |
function Menu:draw() | |
pushStyle() | |
spriteMode(CENTER) | |
-- menu title | |
if self.deployed then | |
tint(196, 196, 196, 255) | |
else tint(211, 211, 211, 127) | |
end | |
local pos = self.pos | |
-- menu button | |
sprite(self.img["title"], pos["title"].x, pos["title"].y) | |
-- deployed | |
if self.deployed then | |
local isel = self.selected | |
for i,img in ipairs(self.img) do | |
pushStyle() | |
if self.disabled[i] then tint(72, 72, 72, 255) | |
elseif i==isel then tint(255,255,255,255) | |
else tint(127, 127, 127, 255) end | |
sprite(img, pos[i].x, pos[i].y) | |
popStyle() | |
end | |
end | |
popStyle() | |
end | |
function Menu:titleTouched(touch) | |
local pos,radius= self.pos["title"],self.radius["title"] | |
local goodZone = false | |
if math.abs((touch.x-pos.x))<radius.x | |
and math.abs((touch.y-pos.y))<radius.y | |
then | |
goodZone = true | |
end | |
self.choiceDone = false | |
return goodZone | |
end | |
function Menu:selectTouched(touch) | |
local pos,radius | |
for i,v in ipairs(self.pos) do | |
pos,radius = self.pos[i],self.radius[i] | |
if math.abs((touch.x-pos.x))<radius.x | |
and math.abs((touch.y-pos.y))<radius.y | |
and not self.disabled[i] | |
then self.selected = i self.choiceDone=true | |
end | |
end | |
end | |
function Menu:touched(t) | |
if self:titleTouched(t) and t.state == BEGAN then | |
self.deployed=true | |
self.initialSelect = self.selected | |
end | |
if self.deployed then | |
self:selectTouched(t) | |
end | |
if t.state == ENDED and self.deployed then | |
self.deployed=false | |
if self.choiceDone then | |
self.callback(self.list[self.selected]) | |
end | |
end | |
end | |
--# Menu2 | |
Menu2 = class() | |
function Menu2:update() | |
end | |
function Menu2:init(args) | |
-- actions | |
self.callback = args.callback or doNothing | |
-- layout | |
self.title = args.title or "menu" | |
-- the | |
local list = args.list | |
self.list,self.enabled,self.selected,self.group = {},{},{},{} | |
self.groups = {} | |
local i,key | |
local imax = #list/3 | |
for i=1,imax do | |
self.list[i] = list[(i-1)*3+1] | |
self.enabled[i] = list[(i-1)*3+2] | |
self.selected[i] = false | |
self.group[i] = list[(i-1)*3+3] | |
end | |
self.side = args.side or "bottom" | |
-- settings by pos and w | |
local x = args.pos | |
local w = args.width or 0.09 | |
-- or by x0 and x1 | |
if args.x0 and args.x1 then | |
x = (args.x0 + args.x1)/2 | |
w = args.x1 - args.x0 | |
end | |
local w1,w2,h1,h2 | |
local pos | |
local dx0,dy0,dx,dy | |
local htxt = 30 | |
local w0,h0 = math.floor( w * WIDTH ), math.floor( w * HEIGHT ) | |
if self.side == "top" then | |
w1,w2,h1,h2 = w0,w0,htxt,htxt | |
pos = vec2(x*WIDTH, HEIGHT-h1/2) | |
dx0,dy0,dx,dy = 0, -h1-5, 0, -h2-2 | |
elseif self.side == "bottom" then | |
w1,w2,h1,h2 = w0,w0,htxt,htxt | |
pos = vec2(x*WIDTH, h1/2) | |
dx0,dy0,dx,dy = 0,h1+5, 0, h2+2 | |
elseif self.side == "left" then | |
w1,w2,h1,h2 = htxt,w0,h0,htxt | |
pos = vec2(w1/2, x*HEIGHT) | |
dx0,dy0,dx,dy = w1/2+5+w2/2, h1/2-h2/2, 0, -h2-2 | |
elseif self.side == "right" then | |
w1,w2,h1,h2 = htxt,w0,h0,htxt | |
pos = vec2(WIDTH-w1/2, x*HEIGHT) | |
dx0,dy0,dx,dy = -w1/2-5-w2/2, h1/2-h2/2, 0, -h2-2 | |
end | |
-- create text images | |
self.img = {} | |
for i,txt in ipairs(self.list) do | |
self.img[i] = self:calcImg(txt,w2,h2) | |
end | |
self.img["title"] = self:calcImg(self.title,w1,h1,self.side) | |
-- position of images | |
local x,y = pos.x,pos.y | |
self.pos = {} | |
self.radius = {} | |
self.pos["title"] = pos | |
self.radius["title"] = vec2(w1/2,h1/2) | |
for i,txt in ipairs(self.list) do | |
if i==1 then x,y = x+dx0,y+dy0 | |
else x,y = x+dx,y+dy end | |
self.pos[i] = vec2(x,y) | |
self.radius[i] = vec2(w2/2,h2/2) | |
end | |
-- manage various drawing states | |
self.deployed = false | |
self.highlighted = nil -- the choice highlighted | |
end | |
function Menu2:select(key) | |
local i,v | |
for i,v in ipairs(self.list) do | |
if self.list[i]==key then self.selected[i]=true end | |
end | |
end | |
function Menu2:groupEnable(key) | |
local i,v | |
for i,v in ipairs(self.group) do | |
if self.group[i]==key then self.enabled[i]=true end | |
end | |
end | |
function Menu2:groupDisable(key) | |
local i,v | |
for i,v in ipairs(self.group) do | |
if self.group[i]==key then self.enabled[i]=false end | |
end | |
end | |
function Menu2:resetDemo() | |
-- do nothing | |
end | |
function Menu2:calcImg(txt,w,h,rot) | |
local w0,h0 | |
pushStyle() pushMatrix() | |
if rot=="left" or rot=="right" | |
then w0,h0 = h,w | |
else w0,h0 = w,h | |
end | |
local img0 = image(w0,h0) | |
setContext(img0) | |
font("AmericanTypewriter-Bold") | |
rectMode(CENTER) | |
textMode(CENTER) | |
strokeWidth(1) | |
-- background(255, 255, 255, 184) | |
background(255, 255, 255, 255) | |
fill(0, 0, 0, 255) | |
stroke(0, 0, 0, 255) | |
fontSize(20) | |
text(txt,w0/2,h0/2) | |
setContext() | |
local img = image(w,h) | |
setContext(img) | |
-- background(255, 255, 255, 184) | |
background(255, 255, 255, 255) | |
spriteMode(CENTER) | |
translate(w/2,h/2) | |
if rot=="left" then rotate(-90) end | |
if rot=="right" then rotate(90) end | |
sprite(img0,0,0) | |
setContext() | |
popStyle() popMatrix() | |
return img | |
end | |
function Menu2:draw() | |
pushStyle() | |
spriteMode(CENTER) | |
-- menu title | |
if self.deployed then | |
tint(196, 196, 196, 255) | |
else tint(211, 211, 211, 127) | |
end | |
local pos = self.pos | |
-- menu button | |
sprite(self.img["title"], pos["title"].x, pos["title"].y) | |
-- deployed | |
if self.deployed then | |
local isel = self.choice | |
for i,txt in ipairs(self.list) do | |
pushStyle() | |
if self.enabled[i] == false then tint(72, 72, 72, 255) | |
elseif self.selected[i] == true or i==isel then tint(255,255,255,255) | |
elseif self.enabled[i] == true then tint(127, 127, 127, 255) | |
end | |
sprite( self.img[i], pos[i].x, pos[i].y) | |
popStyle() | |
end | |
end | |
popStyle() | |
end | |
function Menu2:titleTouched(touch) | |
local pos,radius= self.pos["title"],self.radius["title"] | |
local goodZone = false | |
if math.abs((touch.x-pos.x))<radius.x | |
and math.abs((touch.y-pos.y))<radius.y | |
then | |
goodZone = true | |
end | |
self.choiceDone = false | |
return goodZone | |
end | |
function Menu2:selectTouched(touch) | |
local pos,radius | |
for i,v in ipairs(self.pos) do | |
pos,radius = self.pos[i],self.radius[i] | |
if math.abs((touch.x-pos.x))<radius.x | |
and math.abs((touch.y-pos.y))<radius.y | |
then | |
self.choice = i | |
self.choiceDone=true | |
end | |
end | |
end | |
function Menu2:touched(t) | |
local group | |
if self:titleTouched(t) and t.state == BEGAN then | |
self.deployed=true | |
end | |
if self.deployed then | |
self:selectTouched(t) | |
end | |
if t.state == ENDED and self.deployed then | |
self.deployed=false | |
if self.choiceDone and self.enabled[self.choice] then | |
group = self.group[self.choice] | |
for i,_ in ipairs(self.group) do | |
if self.group[i] == group then self.selected[i] = false end | |
end | |
self.selected[self.choice] = true | |
self.callback( self.list[self.choice] ) | |
end | |
self.choice = nil | |
end | |
end | |
--# Menu3 | |
Menu3 = class() | |
function Menu3:update() | |
end | |
function Menu3:init(args) | |
-- actions | |
self.callback = args.callback or doNothing | |
-- layout | |
self.title = args.title or "menu" | |
self.hFactor = 0.1 | |
-- the | |
local list = args.list | |
self.list,self.enabled,self.selected,self.group = {},{},{},{} | |
self.groups = {} | |
local i,key | |
local imax = #list/3 | |
for i=1,imax do | |
self.list[i] = list[(i-1)*3+1] | |
self.enabled[i] = list[(i-1)*3+2] | |
self.selected[i] = false | |
self.group[i] = list[(i-1)*3+3] | |
end | |
self.side = args.side or "bottom" | |
-- settings by pos and w | |
local x = args.pos | |
local w = args.width or 0.09 | |
-- or by x0 and x1 | |
if args.x0 and args.x1 then | |
x = (args.x0 + args.x1)/2 | |
w = args.x1 - args.x0 | |
end | |
local w1,w2,h1,h2 | |
local pos | |
local dx0,dy0,dx,dy | |
local htxt = 30 | |
local w0,h0 = math.floor( w * WIDTH ), math.floor( w * HEIGHT ) | |
if self.side == "top" then | |
w1,w2,h1,h2 = w0,w0,htxt,htxt | |
pos = vec2(x*WIDTH, HEIGHT-h1/2) | |
dx0,dy0,dx,dy = 0, -h1-5, 0, -h2-2 | |
elseif self.side == "bottom" then | |
w1,w2,h1,h2 = w0,w0,htxt,htxt | |
pos = vec2(x*WIDTH, h1/2) | |
dx0,dy0,dx,dy = 0,h1+5, 0, h2+2 | |
elseif self.side == "left" then | |
w1,w2,h1,h2 = htxt,w0,h0,htxt | |
pos = vec2(w1/2, x*HEIGHT) | |
dx0,dy0,dx,dy = w1/2+5+w2/2, h1/2-h2/2, 0, -h2-2 | |
elseif self.side == "right" then | |
w1,w2,h1,h2 = htxt,w0,h0,htxt | |
pos = vec2(WIDTH-w1/2, x*HEIGHT) | |
dx0,dy0,dx,dy = -w1/2-5-w2/2, h1/2-h2/2, 0, -h2-2 | |
end | |
-- create text images | |
self.img = {} | |
for i,txt in ipairs(self.list) do | |
self.img[i] = self:calcImg(txt,w2,h2) | |
end | |
self.img["title"] = self:calcImg(self.title,w1,h1,self.side) | |
-- position of images | |
local x,y = pos.x,pos.y | |
self.pos = {} | |
self.radius = {} | |
self.pos["title"] = pos | |
self.radius["title"] = vec2(w1/2,h1/2) | |
for i,txt in ipairs(self.list) do | |
if i==1 then x,y = x+dx0,y+dy0 | |
else x,y = x+dx,y+dy end | |
self.pos[i] = vec2(x,y) | |
self.radius[i] = vec2(w2/2,h2/2) | |
end | |
-- manage various drawing states | |
self.deployed = false | |
self.highlighted = nil -- the choice highlighted | |
end | |
function Menu3:select(key) | |
local i,v | |
for i,v in ipairs(self.list) do | |
if self.list[i]==key then self.selected[i]=true end | |
end | |
end | |
function Menu3:groupEnable(key) | |
local i,v | |
for i,v in ipairs(self.group) do | |
if self.group[i]==key then self.enabled[i]=true end | |
end | |
end | |
function Menu3:groupDisable(key) | |
local i,v | |
for i,v in ipairs(self.group) do | |
if self.group[i]==key then self.enabled[i]=false end | |
end | |
end | |
function Menu3:resetDemo() | |
-- do nothing | |
end | |
function Menu3:calcImg(txt,w,h,rot) | |
local w0,h0 | |
pushStyle() pushMatrix() | |
if rot=="left" or rot=="right" | |
then w0,h0 = h,w | |
else w0,h0 = w,h | |
end | |
local img0 = image(w0,h0) | |
setContext(img0) | |
font("AmericanTypewriter-Bold") | |
rectMode(CENTER) | |
textMode(CENTER) | |
strokeWidth(1) | |
-- background(255, 255, 255, 184) | |
background(255, 255, 255, 255) | |
fill(0, 0, 0, 255) | |
stroke(0, 0, 0, 255) | |
fontSize(20) | |
text(txt,w0/2,h0/2) | |
setContext() | |
local img = image(w,h) | |
setContext(img) | |
-- background(255, 255, 255, 184) | |
background(255, 255, 255, 255) | |
spriteMode(CENTER) | |
translate(w/2,h/2) | |
if rot=="left" then rotate(-90) end | |
if rot=="right" then rotate(90) end | |
sprite(img0,0,0) | |
setContext() | |
popStyle() popMatrix() | |
return img | |
end | |
function Menu3:draw() | |
pushStyle() | |
spriteMode(CENTER) | |
-- menu title | |
if self.deployed then | |
tint(196, 196, 196, 255) | |
else tint(211, 211, 211, 127) | |
end | |
local pos = self.pos | |
-- menu button | |
sprite(self.img["title"], pos["title"].x, pos["title"].y) | |
-- deployed | |
if self.deployed then | |
local isel = self.choice | |
local y0=pos["title"].y | |
for i,txt in ipairs(self.list) do | |
pushStyle() | |
if self.enabled[i] == false then tint(72, 72, 72, 255) | |
elseif self.selected[i] == true or i==isel then tint(255,255,255,255) | |
elseif self.enabled[i] == true then tint(127, 127, 127, 255) | |
end | |
sprite( self.img[i], pos[i].x, pos[i].y* self.hFactor+y0*(1-self.hFactor)) | |
popStyle() | |
end | |
end | |
popStyle() | |
end | |
function Menu3:titleTouched(touch) | |
local pos,radius= self.pos["title"],self.radius["title"] | |
local goodZone = false | |
if math.abs((touch.x-pos.x))<radius.x | |
and math.abs((touch.y-pos.y))<radius.y | |
then | |
goodZone = true | |
end | |
self.choiceDone = false | |
return goodZone | |
end | |
function Menu3:selectTouched(touch) | |
local pos,radius | |
for i,v in ipairs(self.pos) do | |
pos,radius = self.pos[i],self.radius[i] | |
if math.abs((touch.x-pos.x))<radius.x | |
and math.abs((touch.y-pos.y))<radius.y | |
then | |
self.choice = i | |
self.choiceDone=true | |
end | |
end | |
end | |
function Menu3:touched(t) | |
local group | |
if self:titleTouched(t) and t.state == BEGAN then | |
self.deployed=true | |
tween( 1.0, self, { hFactor = 1 }, "outElastic") | |
end | |
if self.deployed then | |
self:selectTouched(t) | |
end | |
if t.state == ENDED and self.deployed then | |
self.deployed=false | |
self.hFactor = 0.1 | |
if self.choiceDone and self.enabled[self.choice] then | |
group = self.group[self.choice] | |
for i,_ in ipairs(self.group) do | |
if self.group[i] == group then self.selected[i] = false end | |
end | |
self.selected[self.choice] = true | |
self.callback( self.list[self.choice] ) | |
end | |
self.choice = nil | |
end | |
end | |
--# Controller | |
-- Base class for controllers | |
-- | |
-- Controllers translate touch events into callbacks to functions | |
-- that do something in the app. (Model/View/Controller style). | |
-- | |
-- Controllers can draw a representation of their current state on | |
-- the screen, but you can choose not to. | |
-- | |
-- A controller can be installed as the global handler for touch | |
-- events by calling its activate() method | |
Controller = class() | |
function Controller:activate(input) | |
self.x0 = input.x0 or 0 | |
self.x1 = input.x1 or 1 | |
self.y0 = input.y0 or 0 | |
self.y1 = input.y1 or 1 | |
self.cx = (self.x0+self.x1)/2 *WIDTH | |
self.cy = (self.y0+self.y1)/2 *HEIGHT | |
self.wx = (self.x1- self.x0)/2 *WIDTH | |
self.wy = (self.y1- self.y0)/2 *HEIGHT | |
self.name = input.name | |
self.nameSide = input.nameSide | |
if self.nameSide then | |
self:textPanel() | |
end | |
self.timeout = input.timeout or 2 | |
self.t0 = ElapsedTime | |
end | |
function Controller:resetDemo() | |
self.t0 = ElapsedTime | |
end | |
function Controller:textPanel() | |
local w,h = 20,20 | |
local side = self.nameSide | |
if side=="top" or side=="bottom" then w = self.wx*2 | |
elseif side=="left" or side=="right" then w = self.wy*2 | |
else print("the text for nameSide is not recognized") | |
end | |
local img = image(w,h) | |
setContext(img) | |
pushStyle() pushMatrix() | |
strokeWidth(1) | |
rectMode(RADIUS) | |
background(127, 127, 127, 57) | |
fill(0, 0, 0, 255) | |
fontSize(20) | |
text(self.name,w/2,h/2) | |
popStyle() popMatrix() | |
setContext() | |
local imgActive = image(w,h) | |
setContext(imgActive) | |
pushStyle() pushMatrix() | |
strokeWidth(1) | |
rectMode(RADIUS) | |
background(255, 255, 255, 184) | |
fill(0, 0, 0, 255) | |
fontSize(20) | |
text(self.name,w/2,h/2) | |
popStyle() popMatrix() | |
setContext() | |
local side = self.nameSide | |
local x,y,r | |
if side=="top" then | |
x,y,r = self.cx , self.y1*HEIGHT - img.height/2 , 0 | |
elseif side=="bottom" then | |
x,y,r = self.cx , self.y0*HEIGHT + img.height/2 , 0 | |
elseif side=="left" then | |
x,y,r = self.x0*WIDTH + img.height/2 , self.cy , -90 | |
elseif side=="right" then | |
x,y,r = self.x1*WIDTH - img.height/2 , self.cy , 90 | |
end | |
self.tx, self.ty, self.tr = x,y,r | |
self.imgPassive = img | |
self.imgActive = imgActive | |
end | |
function Controller:demo(timeout) | |
self.timeout = timeout | |
if (ElapsedTime - self.t0)<timeout then | |
pushStyle() pushMatrix() | |
strokeWidth(1) | |
rectMode(RADIUS) | |
fill(255, 255, 255, 46) | |
rect(self.cx,self.cy,self.wx,self.wy) | |
fill(255, 255, 255, 255) | |
fontSize(20) | |
text("touch here to",self.cx,self.cy+20) | |
text(self.name,self.cx,self.cy - 20) | |
popStyle() popMatrix() | |
end | |
end | |
function Controller:checkTitle(touch) | |
-- show region if title touch | |
local img | |
img = self.imgActive | |
local tw,th = img.width/2, img.height/2 | |
if self.tr ~=0 then tw,th = th,tw end | |
if math.abs((touch.x- self.tx))<tw | |
and math.abs((touch.y- self.ty))<th | |
then self.t0=ElapsedTime self:demo(1) end | |
end | |
function Controller:check(touch) | |
local goodZone = false | |
if math.abs((touch.x-self.cx))<self.wx | |
and math.abs((touch.y-self.cy))<self.wy | |
then | |
goodZone = true | |
-- show region if title touch | |
self:checkTitle(touch) | |
end | |
return goodZone | |
end | |
function Controller:drawName() | |
local img | |
if self.touchId | |
then img = self.imgActive | |
else img = self.imgPassive | |
end | |
local x,y,r = self.tx, self.ty, self.tr | |
pushMatrix() | |
translate(x,y) | |
rotate(r) | |
spriteMode(CENTER) | |
sprite(img ,0,0) | |
popMatrix() | |
end | |
-- Utility functions | |
function touchPos(t) | |
return vec2(t.x, t.y) | |
end | |
function clamp(x, min, max) | |
return math.max(min, math.min(max, x)) | |
end | |
function clampAbs(x, maxAbs) | |
return clamp(x, -maxAbs, maxAbs) | |
end | |
function clampLen(vec, maxLen) | |
if vec == vec2(0,0) then | |
return vec | |
else | |
return vec:normalize() * math.min(vec:len(), maxLen) | |
end | |
end | |
-- projects v onto the direction represented by the given unit vector | |
function project(v, unit) | |
return v:dot(unit) | |
end | |
function sign(x) | |
if x == 0 then | |
return 0 | |
elseif x < 0 then | |
return -1 | |
elseif x > 0 then | |
return 1 | |
else | |
return x -- x is NaN | |
end | |
end | |
function doNothing() | |
end | |
--# Contoller_TwoTouch | |
TwoTouch = class(Controller) | |
function TwoTouch:init(args) | |
self.touches = {} | |
self.touchStart = {} | |
self.releasedCallback1 = args.released1 or doNothing | |
self.steerCallback1 = args.moved1 or doNothing | |
self.pressedCallback1 = args.pressed1 or doNothing | |
self.releasedCallback2 = args.released2 or doNothing -- never fires..?? | |
self.zoomCallback2 = args.moved2 or doNothing | |
self.pressedCallback2 = args.pressed2 or doNothing | |
self.activated = args.activate or true | |
-- pre-draw sprites | |
self.stick = self:createStick() | |
if self.activated then self:activate(args) end | |
end | |
function TwoTouch:touchCount() | |
local n = 0 | |
for i,v in pairs(self.touches) do n = n + 1 end | |
return n | |
end | |
function TwoTouch:touched(touch) | |
local goodZone = self:check(touch) | |
local Nbefore,Nafter | |
Nbefore = self:touchCount() | |
if touch.state == BEGAN then | |
if goodZone and Nbefore<2 then | |
self.touches[touch.id] = touch -- new touch | |
self.touchStart[touch.id] = touch | |
else end -- that touch doesnt concern us | |
elseif self.touches[touch.id] then -- if this one of my touches | |
self.touches[touch.id] = touch -- update touch data | |
end | |
Nafter = self:touchCount() -- what is the new touch count? | |
-- if exactly one touch then move | |
if Nafter==1 and Nbefore<=1 then | |
--info:changeTxt("1 touch") | |
self:move(touch) | |
end | |
-- if exactly 2 touches then zoom | |
if Nafter==2 or Nbefore==2 then | |
-- info:changeTxt("2 touch") | |
self:zoom() | |
end | |
if touch.state == ENDED or touch.state == CANCELLED then | |
self:reset(touch) | |
end | |
end | |
function TwoTouch:draw() | |
if self.nameSide then self:drawName() end | |
if self.name then self:demo(self.timeout) end | |
for i,t in pairs(self.touches) do | |
if t then sprite(self.stick, t.x, t.y) end | |
end | |
end | |
function TwoTouch:reset(t) | |
self.touches[t.id] = nil | |
self.touchStart[t.id] = nil | |
end | |
function TwoTouch:move(t) | |
if t.state == BEGAN then | |
self.pressedCallback1(touchPos(t)) | |
elseif t.state == MOVING then | |
self.steerCallback1(touchPos(t)) | |
elseif t.state == ENDED or t.state == CANCELLED then | |
self.releasedCallback1() | |
end | |
end | |
function TwoTouch:zoom() | |
local n = 1 | |
local t = {} | |
for i,touch in pairs(self.touches) do | |
t[n] = touch | |
n = n + 1 | |
end | |
local v = touchPos(t[1]) - touchPos(t[2]) | |
if t[1].state == BEGAN or t[2].state == BEGAN then | |
self.pressedCallback2( v ) | |
elseif t[1].state == MOVING or t[2].state == MOVING then | |
self.zoomCallback2( v ) | |
elseif | |
t[1].state == ENDED or t[2].state == ENDED or | |
t[1].state == CANCELLED or t[2].state == CANCELLED | |
then | |
self.releasedCallback2() | |
if not(t[1].state == ENDED or t[1].state == CANCELLED) then | |
self.pressedCallback1(touchPos(t[2])) | |
else | |
self.pressedCallback1(touchPos(t[1])) | |
end | |
end | |
end | |
function TwoTouch:createStick() | |
local base = image(56,56) | |
pushStyle() pushMatrix() | |
ellipseMode(RADIUS) | |
strokeWidth(1) | |
stroke(255, 255, 255, 255) | |
noFill() | |
setContext(base) | |
background(0, 0, 0, 0) | |
ellipse(base.width/2, base.height/2, 25, 25) | |
setContext() | |
popMatrix() popStyle() | |
return base | |
end | |
--# Controller_oneTouch | |
OneTouch = class(Controller) | |
function OneTouch:init(args) | |
self.releasedCallback = args.released or doNothing | |
self.steerCallback = args.moved or doNothing | |
self.pressedCallback = args.pressed or doNothing | |
self.activated = args.activate or true | |
-- pre-draw sprites | |
self.stick = self:createStick() | |
if self.activated then self:activate(args) end | |
end | |
function OneTouch:draw() | |
if self.nameSide then self:drawName() end | |
if self.name then self:demo(self.timeout) end | |
if self.touchId then | |
sprite(self.stick, self.pos.x, self.pos.y) | |
end | |
end | |
function OneTouch:reset() | |
self.touchId = nil | |
self.touchStart = nil | |
end | |
function OneTouch:touched(t) | |
local pos = touchPos(t) | |
local goodZone = self:check(t) | |
if t.state == BEGAN and self.touchId == nil and goodZone then | |
self.touchId = t.id | |
self.touchStart = pos | |
self.pos = pos | |
self.pressedCallback(pos) | |
elseif t.id == self.touchId then | |
if t.state == MOVING then | |
self.pos = pos | |
self.steerCallback(pos) | |
elseif t.state == ENDED or t.state == CANCELLED then | |
self:reset() | |
self.releasedCallback(pos) | |
end | |
end | |
end | |
function OneTouch:createStick() | |
local base = image(56,56) | |
pushStyle() pushMatrix() | |
ellipseMode(RADIUS) | |
strokeWidth(1) | |
stroke(255, 255, 255, 255) | |
noFill() | |
setContext(base) | |
background(0, 0, 0, 0) | |
ellipse(base.width/2, base.height/2, 25, 25) | |
setContext() | |
popMatrix() popStyle() | |
return base | |
end | |
--# Controller_VirtualStick | |
-- A virtual analogue joystick with a dead-zone at the center, | |
-- which activates wherever the user touches their finger | |
-- | |
-- Arguments: | |
-- radius - radius of the stick (default = 100) | |
-- deadZoneRadius - radius of the stick's dead zone (default = 25) | |
-- moved(v) - Called when the stick is moved | |
-- v : vec2 - in the range vec2(-1,-1) and vec2(1,1) | |
-- pressed() - Called when the user starts using the stick (optional) | |
-- released() - Called when the user releases the stick (optional) | |
VirtualStick = class(Controller) | |
function VirtualStick:init(args) | |
self.radius = args.radius or 100 | |
self.deadZoneRadius = args.deadZoneRadius or 25 | |
self.releasedCallback = args.released or doNothing | |
self.steerCallback = args.moved or doNothing | |
self.pressedCallback = args.pressed or doNothing | |
self.activated = args.activate or true | |
-- pre-draw sprites | |
self.base = self:createBase() | |
self.stick = self:createStick() | |
if self.activated then self:activate(args) end | |
end | |
function VirtualStick:createBase() | |
local base = image(self.radius*2+6,self.radius*2+6) | |
pushStyle() pushMatrix() | |
ellipseMode(RADIUS) | |
strokeWidth(1) | |
stroke(255, 255, 255, 255) | |
noFill() | |
setContext(base) | |
background(0, 0, 0, 0) | |
ellipse(base.width/2, base.height/2, self.radius, self.radius) | |
ellipse(base.width/2, base.height/2, self.deadZoneRadius, self.deadZoneRadius) | |
setContext() | |
popMatrix() popStyle() | |
return base | |
end | |
function VirtualStick:createStick() | |
local base = image(56,56) | |
pushStyle() pushMatrix() | |
ellipseMode(RADIUS) | |
strokeWidth(1) | |
stroke(255, 255, 255, 255) | |
noFill() | |
setContext(base) | |
background(0, 0, 0, 0) | |
ellipse(base.width/2, base.height/2, 25, 25) | |
setContext() | |
popMatrix() popStyle() | |
return base | |
end | |
function VirtualStick:touched(t) | |
local pos = touchPos(t) | |
local goodZone = self:check(t) | |
if t.state == BEGAN and self.touchId == nil and goodZone then | |
self.touchId = t.id | |
self.touchStart = pos | |
self.stickOffset = vec2(0, 0) | |
self.pressedCallback() | |
elseif t.id == self.touchId then | |
if t.state == MOVING then | |
self.stickOffset = clampLen(pos - self.touchStart, self.radius) | |
self.steerCallback(self:vector()) | |
elseif t.state == ENDED or t.state == CANCELLED then | |
self:reset() | |
self.releasedCallback() | |
end | |
end | |
end | |
function VirtualStick:vector() | |
local stickRange = self.radius - self.deadZoneRadius | |
local stickAmount = math.max(self.stickOffset:len() - self.deadZoneRadius, 0) | |
local stickDirection = self.stickOffset | |
if stickDirection:len()>0 then stickDirection = self.stickOffset:normalize() end | |
return stickDirection * (stickAmount/stickRange) | |
end | |
function VirtualStick:reset() | |
self.touchId = nil | |
self.touchStart = nil | |
self.stickOffset = nil | |
end | |
function VirtualStick:draw() | |
if self.nameSide then self:drawName() end | |
if self.name then self:demo(self.timeout) end | |
if self.touchId then | |
sprite(self.base,self.touchStart.x, self.touchStart.y) | |
sprite(self.stick, | |
self.touchStart.x+self.stickOffset.x, | |
self.touchStart.y+self.stickOffset.y) | |
end | |
end | |
--# Controller_VirtualSlider | |
-- A virtual analogue slider with a dead-zone at the center, | |
-- which activates wherever the user touches their finger | |
-- | |
-- Arguments: | |
-- orientation - A unit vector that defines the orientation of the slider. | |
-- For example orientation=vec2(1,0) creates a horizontal slider, | |
-- orientation=vec2(0,1) creates a vertical slider. The slider | |
-- can be given an arbitrary orientation; it does not have to be | |
-- aligned with the x or y axis. For example, setting | |
-- orientation=vec2(1,1):normalize() creates a diagonal slider. | |
-- radius - Distance from the center to the end of the slider (default = 100) | |
-- deadZoneRadius - Distance from the center to the end of the dead zone (default = 25) | |
-- moved(x) - Called when the slider is moved | |
-- x : float - in the range -1 to 1 | |
-- pressed() - Called when the user starts using the slider (optional) | |
-- released() - Called when the user releases the slider (optional) | |
VirtualSlider = class(Controller) | |
function VirtualSlider:init(args) | |
self.orientation = args.orientation or vec2(1,0) | |
self.radius = args.radius or 100 | |
self.deadZoneRadius = args.deadZoneRadius or 25 | |
self.releasedCallback = args.released or doNothing | |
self.movedCallback = args.moved or doNothing | |
self.pressedCallback = args.pressed or doNothing | |
self.activated = args.activate or true | |
self.base = self:createBase() | |
self.stick = self:createStick() | |
if self.activated then self:activate(args) end | |
end | |
function VirtualSlider:touched(t) | |
local pos = touchPos(t) | |
local goodZone = self:check(t) | |
if t.state == BEGAN and self.touchId == nil and goodZone then | |
self.touchId = t.id | |
self.touchStart = pos | |
self.sliderOffset = 0 | |
self.pressedCallback() | |
elseif t.id == self.touchId then | |
if t.state == MOVING then | |
local v = pos - self.touchStart | |
self.sliderOffset = clampAbs(project(v, self.orientation), self.radius) | |
self.movedCallback(self:value()) | |
elseif t.state == ENDED or t.state == CANCELLED then | |
self:reset() | |
self.releasedCallback() | |
end | |
end | |
end | |
function VirtualSlider:reset() | |
self.touchId = nil | |
self.touchStart = nil | |
self.sliderOffset = nil | |
end | |
function VirtualSlider:value() | |
local range = self.radius - self.deadZoneRadius | |
local amount = sign(self.sliderOffset) * math.max(math.abs(self.sliderOffset) - self.deadZoneRadius, 0) | |
return amount/range | |
end | |
function VirtualSlider:createBase() | |
local img = image(self.radius*2+6,self.radius*2+6) | |
setContext(img) | |
pushStyle() | |
ellipseMode(RADIUS) | |
strokeWidth(3) | |
stroke(255, 255, 255, 255) | |
lineCapMode(SQUARE) | |
noFill() | |
background(0, 0, 0, 0) | |
local function polarLine(orientation, fromRadius, toRadius) | |
local from = orientation * fromRadius + vec2(1,1)*(self.radius + 3) | |
local to = orientation * toRadius + vec2(1,1)*(self.radius + 3) | |
line(from.x, from.y, to.x, to.y) | |
end | |
polarLine(self.orientation, self.deadZoneRadius, self.radius) | |
polarLine(self.orientation, -self.deadZoneRadius, -self.radius) | |
popStyle() | |
setContext() | |
return img | |
end | |
function VirtualSlider:createStick() | |
local img = image(56,56) | |
setContext(img) | |
pushStyle() pushMatrix() | |
ellipseMode(RADIUS) | |
strokeWidth(3) | |
stroke(255, 255, 255, 255) | |
lineCapMode(SQUARE) | |
noFill() | |
background(0, 0, 0, 0) | |
strokeWidth(1) | |
ellipse(28, 28, 25, 25) | |
popMatrix() popStyle() | |
setContext() | |
return img | |
end | |
function VirtualSlider:draw() | |
if self.nameSide then self:drawName() end | |
if self.name then self:demo(self.timeout) end | |
if self.touchId then | |
pushMatrix() pushStyle() | |
spriteMode(CENTER) | |
sprite(self.base,self.touchStart.x, self.touchStart.y) | |
local sliderPos = self.orientation * self.sliderOffset + self.touchStart | |
strokeWidth(1) | |
sprite(self.stick, sliderPos.x, sliderPos.y) | |
popStyle() popMatrix() | |
end | |
end | |
--# Controller_All | |
All = class(Controller) | |
-- Forwards each touch event to all the controllers in the table | |
-- passed to the constructor | |
function All:init(controllers) | |
self.controllers = controllers | |
end | |
function All:touched(t) | |
for _, c in pairs(self.controllers) do | |
c:touched(t) | |
end | |
end | |
function All:resetDemo() | |
for _, c in pairs(self.controllers) do | |
c:resetDemo() | |
end | |
end | |
function All:draw() | |
for _, c in pairs(self.controllers) do | |
c:draw() | |
end | |
end | |
--# FPS | |
FPS = class() | |
function FPS:init() | |
self.val = 60 | |
end | |
function FPS:draw() | |
-- update FPS value with some smoothing | |
self.val = self.val*0.99+ 1/(DeltaTime)*0.01 | |
-- write the FPS on the screen | |
fill(208, 208, 208, 255) | |
fontSize(30) | |
font("AmericanTypewriter-Bold") | |
rectMode(CENTER) | |
text(math.floor(self.val).." fps",50,HEIGHT-25) | |
end | |
--# Main | |
-- project "sphere" JMV38 | |
displayMode(FULLSCREEN) | |
function setup() | |
-- a global object to manage infos | |
info = InfoBloc({side="right", x0=0.9 , x1=1 , | |
textPos=vec2(WIDTH/2, HEIGHT-125), textWidth =800} ) | |
-- object creation | |
axis = Axis(25) | |
-- this is to see the codea controls | |
codeaPanelSetup() | |
-- controls and menus | |
menus = Menus() | |
controls = ObjectControl2() | |
-- start with an embeded object | |
cube = Cube(100,0,0,0) | |
obj = cube.ms | |
-- load saved object | |
tool = Convert() | |
--obj = tool:loadMesh("test") | |
local txt = "Start by loading some objects i've put on the web for you\n" | |
txt = txt.."Then save save them locally on your ipad.\n" | |
txt = txt.."Then create, save and load your own meshes." | |
info:changeTxt(txt) | |
--sprite() | |
end | |
function codeaPanelSetup() | |
-- global variable | |
codeaPanel = image(265,32) | |
setContext(codeaPanel) | |
background(127, 127, 127, 42) | |
setContext() | |
end | |
function codeaPanelDraw() | |
spriteMode(CORNER) -- a white background below controls | |
sprite(codeaPanel, 5,5) | |
end | |
function draw() | |
pushMatrix() | |
perspective(45) | |
camera(0,0,400, 0,0,0, 0,1,0) | |
background(14, 14, 14, 255) -- clean background | |
controls:move() | |
obj:draw() | |
axis:draw() | |
popMatrix() | |
ortho() -- Restore orthographic projection | |
viewMatrix(matrix()) -- Restore the view matrix to the identity | |
controls:draw() | |
menus.all:draw() | |
codeaPanelDraw() | |
end | |
function touched(touch) | |
controls:touched(touch) | |
menus.all:touched(touch) | |
end | |
--# Cube | |
Cube = class() | |
function Cube:init(size,x0,y0,z0) | |
-- position of the object | |
self.m = modelMatrix() | |
self.x0,self.y0,self.z0 = x0,y0,z0 | |
self.m[13],self.m[14],self.m[15] = self.x0,self.y0,self.z0 | |
-- object design | |
self.size = size | |
local R = color(148, 24, 25, 255) | |
local V = color(35, 140, 28, 255) | |
local B = color(15, 109, 204, 255) | |
local r = size/2 | |
self.ms = mesh() | |
self.ms.vertices={ | |
vec3(-r,-r,r),vec3(-r,r,r),vec3(r,-r,r), | |
vec3(r,r,r),vec3(-r,r,r),vec3(r,-r,r), | |
vec3(-r,-r,-r),vec3(-r,r,-r),vec3(r,-r,-r), | |
vec3(r,r,-r),vec3(-r,r,-r),vec3(r,-r,-r), | |
vec3(-r,-r,r),vec3(-r,r,r),vec3(-r,r,-r), | |
vec3(-r,-r,r),vec3(-r,-r,-r),vec3(-r,r,-r), | |
vec3(r,-r,r),vec3(r,r,r),vec3(r,r,-r), | |
vec3(r,-r,r),vec3(r,-r,-r),vec3(r,r,-r), | |
vec3(r,r,r),vec3(-r,r,r),vec3(r,r,-r), | |
vec3(-r,r,-r),vec3(-r,r,r),vec3(r,r,-r), | |
vec3(r,-r,r),vec3(-r,-r,r),vec3(r,-r,-r), | |
vec3(-r,-r,-r),vec3(-r,-r,r),vec3(r,-r,-r), | |
} | |
self.ms.colors = { | |
R,R,R,R,R,R,R,R,R,R,R,R, | |
V,V,V,V,V,V,V,V,V,V,V,V, | |
B,B,B,B,B,B,B,B,B,B,B,B, | |
} | |
end | |
function Cube:draw() | |
pushMatrix() | |
local m = self.m * modelMatrix() | |
modelMatrix(m) | |
self.ms:draw() | |
popMatrix() | |
end | |
--# Axis | |
Axis = class() | |
function Axis:init(size) | |
-- position of the object | |
self.m = modelMatrix() | |
self.x0,self.y0,self.z0 = -190,-100,0 | |
-- object design | |
self.size = size | |
-- triangles of the x,y,z axis | |
local X,Y,Z,O = vec3(1,0,0) * size , | |
vec3(0,1,0) * size , vec3(0,0,1) * size , vec3(0,0,0) | |
self.X, self.Y, self.Z, self.O = X,Y,Z,O | |
local vertices = { | |
O,X,Y, | |
O,X,Z, | |
O,Y,Z | |
} | |
local R = color(148, 24, 25, 255) | |
local V = color(35, 140, 28, 255) | |
local B = color(15, 109, 204, 255) | |
local colors = { | |
R,R,R, | |
V,V,V, | |
B,B,B | |
} | |
self.ms = mesh() | |
self.ms.vertices = vertices | |
self.ms.colors = colors | |
-- labels | |
pushStyle() pushMatrix() | |
local w = math.floor(size/5) | |
self.w = w | |
if w<10 then w=10 end | |
if w>30 then w=30 end | |
local c=w/2 | |
local s = w-2 | |
self.xlabel = image(w,w) | |
setContext(self.xlabel) | |
background(127, 127, 127, 255) | |
fill(208, 208, 208, 255) | |
fontSize(s) | |
font("AmericanTypewriter-Bold") | |
textMode(CENTER) | |
text("X",c,c) | |
setContext() | |
self.ylabel = image(w,w) | |
setContext(self.ylabel) | |
background(127, 127, 127, 255) | |
fill(208, 208, 208, 255) | |
fontSize(s) | |
font("AmericanTypewriter-Bold") | |
textMode(CENTER) | |
text("Y",c,c) | |
setContext() | |
self.zlabel = image(w,w) | |
setContext(self.zlabel) | |
background(127, 127, 127, 255) | |
fill(208, 208, 208, 255) | |
fontSize(s) | |
font("AmericanTypewriter-Bold") | |
textMode(CENTER) | |
text("Z",c,c) | |
setContext() | |
self.olabel = image(w,w) | |
setContext(self.olabel) | |
background(127, 127, 127, 255) | |
fill(208, 208, 208, 255) | |
fontSize(s) | |
font("AmericanTypewriter-Bold") | |
textMode(CENTER) | |
text("O",c,c) | |
setContext() | |
popMatrix() popStyle() | |
end | |
function Axis:draw() | |
local c = self.w/2 | |
local m | |
m = modelMatrix() | |
pushMatrix() | |
resetMatrix() | |
--translate(-150,0,20) | |
m[13],m[14],m[15] = self.x0,self.y0,self.z0 | |
modelMatrix(m) | |
pushStyle() | |
spriteMode(CENTER) | |
pushMatrix() | |
translate(self.X.x + c, self.X.y, self.X.z -0.05) | |
sprite(self.xlabel,0,0) | |
popMatrix() pushMatrix() | |
translate(self.Y.x, self.Y.y + c, self.Y.z -0.05) | |
sprite(self.ylabel) | |
popMatrix() pushMatrix() | |
translate(self.Z.x, self.Z.y, self.Z.z +0.05) | |
sprite(self.zlabel) | |
popMatrix() pushMatrix() | |
translate(self.O.x - c, self.O.y - c, self.O.z -0.05) | |
sprite(self.olabel) | |
popMatrix() | |
self.ms:draw() | |
popStyle() | |
popMatrix() | |
end | |
--# Menus | |
Menus = class() | |
function Menus:init() | |
-- save | |
local menu1 = Menu({side="right", x0=0.70, x1=0.79, title="save", | |
callback = self.selectSave, | |
list={ "Slots:",false,"slot1",true,"slot2",true, "slot3",true,"slot4", true, | |
"slot5", true, "slot6", true, | |
}, selected=0 }) | |
-- load from disk | |
local menu2 = Menu({side="right", x0=0.60, x1=0.69, title="disk", | |
callback = self.diskLoad, | |
list={ "Slots:",false,"slot1",true,"slot2",true, "slot3",true,"slot4", true, | |
"slot5", true,"slot6",true}, selected=0 }) | |
-- load from web | |
local menu3 = Menu({side="right", x0=0.50, x1=0.59, title="web", | |
callback = self.webLoad, | |
list={ "links:",false,"meca",true, "cube1",true, "cube2",true, | |
"cylin",true, "sphere",true, "earth",true, | |
}, selected=0 }) | |
self.all=All({info,menu1,menu2,menu3}) | |
end | |
function Menus.selectSave(v) | |
local txt = nil | |
local size = tool:saveMesh(obj,v) | |
txt = "the mesh has been saved in your CODEA 'Documents:' folder,\n" | |
txt = txt.."under the name '"..v.."', and its size is " | |
txt = txt..math.floor(size*100).."% of max size." | |
info:changeTxt(txt) | |
end | |
function Menus.diskLoad(v) | |
local txt = nil | |
local name = "Documents:"..v | |
local img = readImage(name) | |
if img==nil then | |
txt = "the image '"..v.."' from your CODEA 'Documents:' folder does not exist\n" | |
txt = txt.."so the current object has NOT been changed. " | |
else | |
obj = tool:loadMesh(v) | |
txt = "the image "..v.." from your CODEA document folder,\n" | |
txt = txt.."has been read and loaded in the current mesh: obj " | |
end | |
info:changeTxt(txt) | |
end | |
function Menus.webLoad(name) | |
-- all the urls here | |
local url = nil | |
if name == "meca" | |
then url = "http://www.jmv38.net23.net/mesh/mechanics.png" | |
elseif name == "cube1" | |
then url = "http://www.jmv38.net23.net/mesh/cube1.png" | |
elseif name == "cube2" | |
then url = "http://www.jmv38.net23.net/mesh/cube2.png" | |
elseif name == "cylin" | |
then url = "http://www.jmv38.net23.net/mesh/cylinder.png" | |
elseif name == "sphere" | |
then url = "http://www.jmv38.net23.net/mesh/sphere.png" | |
elseif name == "earth" | |
then url = "http://www.jmv38.net23.net/mesh/earth.png" | |
end | |
if url then | |
http.request(url, | |
function(img, status, head) | |
saveImage("Documents:webImage",img) | |
obj = tool:loadMesh("webImage") | |
end ) | |
end | |
end | |
function Menus:draw() | |
self.all:draw() | |
end | |
function Menus:touched(touch) | |
self.all:touched(touch) | |
end | |
--# WebImageLoader | |
WebImageLoader = class(Job2) | |
-- CAUTION! the Job2 tab MUST be at the left of this tab! | |
function WebImageLoader:start(args) | |
self.txtToIdentifyModifier = "web loading" | |
local name = args.name or args -- the 2 syntax can be accepted | |
self.name = name | |
self.t0 = ElapsedTime -- start count time | |
self.tmax = 20 | |
local continue = true | |
if name=="none" then -- this name is for no image | |
continue = false -- stop immediately | |
else | |
local img = hardDiskManager:readImg(name) -- is image on disk? | |
if img then -- if it is here, | |
continue = false -- stop immediately | |
else | |
continue = self:loadWebImage(name) -- start loading | |
end | |
end | |
return continue | |
end | |
function WebImageLoader:myFunction() | |
local frac = (ElapsedTime - self.t0)/self.tmax | |
local continue = true | |
-- the image is already there | |
if hardDiskManager:readImg(self.name) or frac>1 then | |
continue = false | |
end | |
return continue,frac | |
end | |
function WebImageLoader:print(...) | |
-- activate/desactivate printing | |
-- print(...) | |
end | |
function WebImageLoader:loadWebImage(name) | |
local continue | |
local url = self:urlWebImage(name) | |
-- load and store in table | |
if url then | |
continue = true | |
http.request(url, | |
function(img, status, head) | |
if hardDiskManager.localSaveEnabled then | |
hardDiskManager:saveImg(name,img) | |
end | |
end ) | |
else | |
print("image "..name.." not in my list of urls") | |
continue = false | |
end | |
return continue | |
end | |
function WebImageLoader:urlWebImage(name) | |
-- all the urls here | |
local url = nil | |
if name == "meca" | |
then url = "http://www.jmv38.net23.net/mesh/mechanics.png" | |
elseif name == "cube1" | |
then url = "http://www.jmv38.net23.net/mesh/cube1.png" | |
elseif name == "cube2" | |
then url = "http://www.jmv38.net23.net/mesh/cube1.png" | |
end | |
return url | |
end | |
--# ObjectControl2 | |
ObjectControl2 = class() | |
-- the axis are the view axis | |
function ObjectControl2:init() | |
-- la derniere valeur changee | |
self.lastChange = 0 | |
self.info = nil | |
self.m = matrix() | |
-- angles | |
self.ax = 0 | |
self.ay = 0 | |
self.az = 0 | |
local temp = 0 | |
local angle = 0 | |
local delta = 0 | |
local m , m0 | |
local v0=0 | |
-- keep angle | |
local function clipAngle(a) | |
if a > 180 then a = a - 360 end | |
if a < -180 then a = a + 360 end | |
return a | |
end | |
-- rotate an translate | |
self.controller1 = TwoTouch({ | |
pressed1 = function(v) self.lastVrot = nil self:selectTarget() end, | |
moved1 = function(v) self:rotChange(v) end, | |
released1 = function() self:rotReset() self.lastVrot=nil end, | |
pressed2 = function(v) self.m0 = self.m ; self.transV0 = v end, | |
-- pressed2 = function(v) self.m0 = self.target ; self.transV0 = v end, | |
moved2 = function(v) self:transChange(v) end, | |
x0=0,x1=0.9,y0=0.1,y1=0.9, timeout=2, | |
name="rotate and zoom (1 or 2 fingers)", nameSide = "left"}) | |
-- reset | |
self.controller3 = VirtualSlider({ | |
pressed = function(v) m = self.m self.m = matrix() end, | |
moved = function(v) if v==0 then self.m=matrix() else self.m=m end end, | |
orientation = vec2(0,1),deadZoneRadius=10, | |
x0=0.9,x1=1,y0=0.80,y1=0.89, timeout=2, | |
name="reset", nameSide = "right"}) | |
self.controls = All({self.controller1,self.controller2,self.controller3}) | |
end | |
function ObjectControl2:selectTarget() | |
local target = "control" | |
if source then | |
if source.visible | |
then target = "source" | |
else target = "control" | |
end | |
else target = "control" | |
end | |
--print(target) | |
self.target = target | |
end | |
function ObjectControl2:rotReset() | |
self.rotTime = nil self.rotAxis = nil | |
end | |
function ObjectControl2:transReset() | |
self.speed = nil self.speedAxis = nil | |
end | |
function ObjectControl2:rotChange(v) | |
local angle = 0 | |
local factor = 90 | |
local m0 | |
if self.target == "control" then | |
m0 = self.m:inverse() | |
else | |
m0 = (source.m*self.m):inverse() | |
end | |
local X,Y,Z = vec3(m0[1],m0[2],m0[3]),vec3(m0[5],m0[6],m0[7]),vec3(m0[9],m0[10],m0[11]) | |
local v1 | |
if self.lastVrot then | |
v1 = v - self.lastVrot | |
local v0 = vec3(v1.x,v1.y,0):rotate(90,0,0,1) | |
local R = X*v0.x + Y*v0.y + Z*v0.z | |
local a = v1:len()/4 | |
if R:len()~=0 then | |
if self.target == "control" then | |
self.m = self.m:rotate(a,R.x,R.y,R.z) | |
else | |
source.m = source.m:rotate(a,R.x,R.y,R.z) | |
end | |
end | |
end | |
self.lastVrot = v | |
end | |
function ObjectControl2:transChange(v) | |
local shift = 0 | |
local factor = 1 | |
local m0 = self.m0:inverse() | |
local x,y,z = m0[9],m0[10],m0[11] -- Z view axix, in object coords reference | |
-- translation along Z axis | |
shift = (v:len()-self.transV0:len()) * factor | |
self.m = self.m0:translate(shift*x,shift*y,shift*z) | |
-- self.target = self.m0:translate(shift*x,shift*y,shift*z) | |
-- rotation around Z axis | |
local angle = - v:angleBetween(self.transV0)*180/math.pi | |
self.m = self.m:rotate(angle,x,y,z) | |
-- self.target = self.target:rotate(angle,x,y,z) | |
end | |
function ObjectControl2:move() | |
modelMatrix(self.m) | |
end | |
function ObjectControl2:draw() | |
self.controls:draw() | |
end | |
function ObjectControl2:resetDemo() | |
self.controls:resetDemo() | |
end | |
function ObjectControl2:touched(touch) | |
self.controls:touched(touch) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment