Created
May 20, 2013 10:39
-
-
Save loopspace/5611538 to your computer and use it in GitHub Desktop.
Library Graphics Release v2.2a -A library of classes and functions relating to graphical things.
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
Library Graphics Tab Order | |
------------------------------ | |
This file should not be included in the Codea project. | |
#ChangeLog | |
#Main | |
#Bezier | |
#Explosion | |
#Path | |
#TextNode | |
#View | |
#Fireworks | |
#DataSeries | |
#Frame | |
#TrendGraph | |
#Zoom |
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
--[==[ | |
-- Bezier path drawing | |
Bezier = class() | |
local __makeBezier = function(nsteps) | |
-- nsteps doesn't make a huge difference in the range 50,300 | |
nsteps = nsteps or 150 | |
local m = mesh() | |
m.shader = shader([[ | |
// | |
// A basic vertex shader | |
// | |
//This is the current model * view * projection matrix | |
// Codea sets it automatically | |
uniform mat4 modelViewProjection; | |
//This is the current mesh vertex position, color and tex coord | |
// Set automatically | |
attribute vec4 position; | |
attribute vec4 color; | |
attribute vec2 texCoord; | |
//This is an output variable that will be passed to the fragment shader | |
varying highp vec2 vTexCoord; | |
varying highp float vWidth; | |
uniform float len; | |
uniform float width; | |
uniform float taper; | |
uniform float blur; | |
float swidth = width + blur; | |
float ewidth = taper*width - width; | |
uniform vec2 pts[4]; | |
void main() | |
{ | |
highp float t = position.y/len; | |
highp float w = smoothstep(0.,1.,t); | |
vWidth = w*ewidth + swidth; | |
highp float tt = 1.0 - t; | |
highp vec2 bpos = tt*tt*tt*pts[0] + 3.0*tt*tt*t*pts[1] | |
+ 3.0*tt*t*t*pts[2] + t*t*t*pts[3]; | |
highp vec2 bdir = tt*tt*(pts[1]-pts[0]) | |
+ 2.0*tt*t*(pts[2]-pts[1]) + t*t*(pts[3]-pts[2]); | |
bdir = vec2(bdir.y,-bdir.x); | |
bdir = vWidth*position.x*normalize(bdir); | |
bpos = bpos + bdir; | |
highp vec4 bzpos = vec4(bpos.x,bpos.y,0,1); | |
//Pass the mesh color to the fragment shader | |
vTexCoord = vec2(texCoord.x, 1.0 - texCoord.y); | |
//Multiply the vertex position by our combined transform | |
gl_Position = modelViewProjection * bzpos; | |
} | |
]],[[ | |
// | |
// A basic fragment shader | |
// | |
//This represents the current texture on the mesh | |
uniform lowp sampler2D texture; | |
//uniform highp float width; | |
uniform highp float blur; | |
uniform lowp vec4 colour; | |
//The interpolated texture coordinate for this fragment | |
varying highp vec2 vTexCoord; | |
varying highp float vWidth; | |
void main() | |
{ | |
//Sample the texture at the interpolated coordinate | |
lowp vec4 col = colour; | |
highp float edge = blur/(vWidth+blur); | |
col.a = mix( 0., col.a, | |
smoothstep( 0., edge, min(vTexCoord.x,1. - vTexCoord.x) ) ); | |
gl_FragColor = col; | |
} | |
]]) | |
for n=1,nsteps do | |
m:addRect(0,(n-.5),1,1) | |
end | |
m.shader.len = nsteps | |
return m | |
end | |
local m = __makeBezier() | |
m.shader.blur = 2 | |
function bezier(a,b,c,d,e) | |
if type(a) == "table" then | |
e = b | |
a,b,c,d = unpack(a) | |
end | |
if type(c) ~= "userdata" then | |
e = e or c | |
d = b | |
b = 2*a/3 + d/3 | |
c = a/3 + 2*d/3 | |
elseif type(d) ~= "userdata" then | |
e = e or d | |
d = c | |
b = 2*b/3 | |
c = b + d/3 | |
b = b + a/3 | |
end | |
m.shader.taper = e or 1 | |
m.shader.width = strokeWidth() | |
m.shader.colour = color(stroke()) | |
m.shader.pts = {a,b,c,d} | |
m:draw() | |
end | |
function Bezier:init(...) | |
self:setPoints(...) | |
end | |
function Bezier:clone() | |
return Bezier(self.points) | |
end | |
function Bezier:makeDrawable(t) | |
t = t or {} | |
local m = __makeBezier(t.steps) | |
m.shader.taper = t.taper or 1 | |
m.shader.blur = t.blur or 2 | |
m.shader.width = t.width or strokeWidth() | |
m.shader.colour = t.colour or color(stroke()) | |
m.shader.pts = self.points | |
self.curve = m | |
self.draw = function(self) self.curve:draw() end | |
end | |
function Bezier:draw(t) | |
self:makeDrawable(t) | |
self.curve:draw() | |
end | |
function Bezier:setPoints(a,b,c,d) | |
if type(a) == "table" then | |
a,b,c,d = unpack(a) | |
end | |
if not c then | |
d = b | |
b = 2*a/3 + d/3 | |
c = a/3 + 2*d/3 | |
elseif not d then | |
d = c | |
b = 2*b/3 | |
c = b + d/3 | |
b = b + a/3 | |
elseif type(b) == "number" then | |
a,b,c,d = __hobby(a,b,c,d) | |
end | |
self.points = {a,b,c,d} | |
if self.curve then | |
self.curve.shader.pts = self.points | |
end | |
end | |
function Bezier:setStyle(t) | |
if not self.curve then | |
self:makeDrawable(t) | |
return | |
end | |
t = t or {} | |
if t.colour then | |
self.curve.shader.colour = t.colour | |
end | |
if t.width then | |
self.curve.shader.width = t.width | |
end | |
if t.taper then | |
self.curve.shader.taper = t.taper | |
end | |
if t.blur then | |
self.curve.shader.blur = t.blur | |
end | |
end | |
function Bezier:point(t) | |
local s = 1 - t | |
return s^3 * self.points[1] | |
+ 3*s*s*t * self.points[2] | |
+ 3*s*t*t * self.points[3] | |
+ t^3 * self.points[4] | |
end | |
function Bezier:tangent(t) | |
local s = 1 - t | |
return 3*s^2 * (self.points[2] - self.points[1]) | |
+ 6*s*t * (self.points[3] - self.points[2]) | |
+ 3*t^2 * (self.points[4] - self.points[3]) | |
end | |
function Bezier:normal(t) | |
return self:tangent(t):rotate90() | |
end | |
function Bezier:unitNormal(t) | |
local pt = self:normal(t) | |
local l = pt:len() | |
if l == 0 then | |
return vec2(0,0) | |
else | |
return pt/l | |
end | |
end | |
function Bezier:unitTangent(t) | |
local pt = self:tangent(t) | |
local l = pt:len() | |
if l == 0 then | |
return vec2(0,0) | |
else | |
return pt/l | |
end | |
end | |
local ha = math.sqrt(2) | |
local hb = 1/16 | |
local hc = (3 - math.sqrt(5))/2 | |
local hd = 1 - hc | |
function __hobby(a,tha,phb,b) | |
local c = b - a | |
local sth = math.sin(tha) | |
local cth = math.cos(tha) | |
local sph = math.sin(phb) | |
local cph = math.cos(phb) | |
local alpha = ha * (sth - hb * sph) * (sph - hb * sth) * (cth - cph) | |
local rho = (2 + alpha)/(1 + hd * cth + hc * cph) | |
local sigma = (2 - alpha)/(1 + hd * cph + hc * cth) | |
return a,a + rho*c:rotate(tha)/3, b - sigma*c:rotate(-phb)/3,b | |
end | |
function QuickHobby(a,b,c,tha) | |
if type(a) == "table" then | |
tha = b | |
a,b,c = unpack(a) | |
end | |
local da = a:dist(b) | |
local db = b:dist(c) | |
local wa = vec2(1,0):angleBetween(b-a) | |
local wb = vec2(1,0):angleBetween(c-b) | |
local psi = wb - wa | |
if psi > math.pi then | |
psi = psi - 2*math.pi | |
end | |
if psi <= -math.pi then | |
psi = psi + 2*math.pi | |
end | |
local thb,phb,phc | |
if tha then | |
thb = -(2*psi + tha) * db / (2*db + da) | |
phb = - psi - thb | |
phc = thb | |
else | |
thb = - psi * db / (da + db) | |
tha = - psi - thb | |
phb = tha | |
phc = thb | |
end | |
return Bezier(__hobby(a,tha,phb,b)),Bezier(__hobby(b,thb,phc,c)),thb | |
end | |
function QHobbyGenerator(a,b) | |
local th | |
return function(c) | |
local p,q | |
p,q,th = QuickHobby(a,b,c,th) | |
a = b | |
b = c | |
return p,q | |
end | |
end | |
function QHobby(pts,th) | |
local n = #pts | |
if n == 1 then | |
return {} | |
end | |
if n == 2 then | |
th = th or 0 | |
return {Bezier(pts[1], th, -th, pts[2] )},th | |
end | |
local a,b = pts[1],pts[2] | |
local p,q | |
local cvs = {} | |
for k=3,n do | |
p,q,th = QuickHobby(pts[k-2],pts[k-1],pts[k],th) | |
table.insert(cvs,p) | |
end | |
table.insert(cvs,q) | |
return cvs,th | |
end | |
function Hobby(pts,extra) | |
local z = {} | |
local d = {} | |
local omega = {} | |
local psi = {} | |
local it = {} | |
local ot = {} | |
local n = -1 | |
local A = {} | |
local B = {} | |
local C = {} | |
local D = {} | |
local icurl = 1 | |
local ocurl = 1 | |
local theta = {} | |
local phi = {} | |
local rho ={} | |
local sigma = {} | |
local a = math.sqrt(2) | |
local b = 1/16 | |
local c = (3 - math.sqrt(5))/2 | |
local cpta = {} | |
local cptb = {} | |
local dten = 1 | |
local curves = {} | |
if extra then | |
dten = extra.tension or 1 | |
icurl = extra.inCurl or 1 | |
ocurl = extra.outCurl or 1 | |
end | |
for _,t in ipairs(pts) do | |
n = n + 1 | |
if type(t) == "table" then | |
z[n] = t[1] | |
it[n] = t[2] or dten | |
ot[n] = t[3] or dten | |
else | |
z[n] = t | |
it[n] = dten | |
ot[n] = dten | |
end | |
end | |
if n < 1 then | |
return {} | |
end | |
if n == 1 then | |
local th,ph | |
if extra then | |
th = extra.inAngle | |
ph = extra.outAngle | |
end | |
if not th and ph then | |
th = -ph | |
elseif not ph and th then | |
ph = - th | |
elseif not th and not ph then | |
ph,th = 0,0 | |
end | |
return {Bezier(__hobby(z[0],th,ph,z[1]))} | |
end | |
local ang | |
for k=0,n-1 do | |
d[k] = z[k]:dist(z[k+1]) | |
omega[k] = vec2(1,0):angleBetween(z[k+1]-z[k]) | |
if k > 0 then | |
ang = omega[k] - omega[k-1] | |
if ang > math.pi then | |
ang = ang - 2*math.pi | |
end | |
if ang < -math.pi then | |
ang = ang + 2*math.pi | |
end | |
psi[k] = ang | |
end | |
end | |
if extra then | |
theta[0] = extra.inAngle | |
phi[n] = extra.outAngle | |
end | |
for k=1,n-1 do | |
A[k] = d[k] * it[k+1] * it[k]^2 | |
end | |
if theta[0] then | |
B[0] = 1 | |
else | |
B[0] = ot[0]^3 * (3 * it[1] - 1) + icurl * it[1]^3 | |
end | |
for k = 1,n-2 do | |
B[k] = d[k] * it[k+1] * it[k]^2 * (3 * ot[k-1] - 1) + d[k-1] * ot[k-1] * ot[k]^2 * (3 * it[k+1] - 1) | |
end | |
B[n-1] = d[n-1] * it[n] * it[n-1]^2 * (3 * ot[n-2] - 1) + d[n-2] * ot[n-2] * ot[n-1]^2 * (3 * it[n] - 1) | |
if not phi[n] then | |
B[n-1] = B[n-1] - d[n-2] * ot[n-2] * ot[n-1]^2 * (it[n]^3 + ocurl * ot[n-1]^3 * (3 * it[n] - 1)) / (it[n]^3 * (3 * ot[n-1] - 1) + ocurl * ot[n-1]^3) | |
end | |
if theta[0] then | |
C[0] = 0 | |
else | |
C[0] = ot[0]^3 + icurl * it[1]^3 * (3 * ot[0] - 1) | |
end | |
for i=1,n do | |
C[i] = d[i-1] * ot[i-1] * ot[i]^2 | |
end | |
if theta[0] then | |
D[0] = theta[0] | |
else | |
D[0] = - (ot[0]^3 + icurl * it[1]^3 * (3 * ot[0] - 1)) * psi[1] | |
end | |
for i=1,n-2 do | |
D[i] = - d[i] * it[i+1] * it[i]^2 * (3 * ot[i-1] - 1) * psi[i] - d[i-1] * ot[i-1] * ot[i]^2 * psi[i+1] | |
end | |
D[n-1] = - d[n-1] * it[n] * it[n-1]^2 * (3 * ot[n-2] - 1) * psi[n-1] | |
if phi[n] then | |
D[n-1] = D[n-1] - d[n-2] * ot[n-2] * ot[n-1]^2 * phi[n] | |
end | |
for i=1,n-1 do | |
B[i] = B[i-1] * B[i] - A[i] * C[i-1] | |
C[i] = B[i-1] * C[i] | |
D[i] = B[i-1] * D[i] - A[i] * D[i-1] | |
end | |
theta[n-1] = D[n-1]/B[n-1] | |
for i=n-2,1,-1 do | |
theta[i] = (D[i] - C[i] * theta[i+1])/B[i] | |
end | |
for i=1,n-1 do | |
phi[i] = -psi[i] - theta[i] | |
end | |
if not theta[0] then | |
theta[0] = (ot[0]^3 + icurl * it[1]^3 * (3 * ot[0] - 1)) / (ot[0]^3 * (3 * it[1] - 1) + icurl * it[1]^3) * phi[1] | |
end | |
if not phi[n] then | |
phi[n] = (it[n]^3 + ocurl * it[n-1]^3 * (3 * it[n] - 1)) / (it[n]^3 * (3 * ot[n-1] - 1) + ocurl * ot[n-1]^3) * theta[n-1] | |
end | |
local alpha | |
for i = 0,n-1 do | |
alpha = a * (math.sin(theta[i]) - b * math.sin(phi[i+1])) * (math.sin(phi[i+1]) - b * math.sin(theta[i])) * (math.cos(theta[i]) - math.cos(phi[i+1])) | |
rho[i] = (2 + alpha) / (1 + (1 - c) * math.cos(theta[i]) + c * math.cos(phi[i+1])) | |
sigma[i+1] = (2 - alpha) / (1 + (1 - c) * math.cos(phi[i+1]) + c * math.cos(theta[i])) | |
end | |
for i = 0,n-1 do | |
table.insert(curves,Bezier( | |
z[i], | |
z[i] + d[i]*rho[i] * vec2(math.cos(theta[i] + omega[i]), math.sin(theta[i] + omega[i]))/3, | |
z[i+1] - d[i] * sigma[i+1] * vec2(math.cos(omega[i] - phi[i+1]), math.sin(omega[i] - phi[i+1]))/3, | |
z[i+1] | |
)) | |
end | |
return curves | |
end | |
cmodule.gexport { | |
QuickHobby = QuickHobby, | |
HobbyPoints = HobbyPoints, | |
QHobbyGenerator = QHobbyGenerator, | |
Hobby = Hobby, | |
QHobby = QHobby, | |
bezier = bezier | |
} | |
return Bezier | |
--]==] |
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
--[[ | |
ChangeLog | |
========= | |
v2.2a Added and developed Bezier class and bezier shader. | |
v2.1 Updated to latest version of cmodule. Cleaned up Main | |
v2.0 Split into separate projects: "Library Graphics" is a suite of | |
mainly graphical functions. | |
v1.0 Converted to use toadkick's cmodule for importing | |
and Briarfox's AutoGist for versionning. | |
It needs toadkick's code from | |
https://gist.github.com/apendley/5411561 | |
tested with version 0.0.8 | |
To use without AutoGist, comment out the lines in setup. | |
--]] |
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
--[==[ | |
-- Data Series | |
DataSeries = class() | |
local Colour = unpack(cimport "Colour",nil) | |
function DataSeries:init(name, length, min, max, symbol, symbolsize, thick, clr, value) | |
self.name = name | |
self.symbol = symbol | |
self.symbolsize = symbolsize | |
self.nextpt = 0 | |
self.length = length | |
self.lineclr = clr | |
self.linethick = thick | |
self.points = {} | |
self.min = min | |
self.max = max | |
if self.min < 0 and self.max > 0 then | |
self.axis = true | |
self.acolour = Colour.shade(self.lineclr,50) | |
end | |
self.value = value | |
end | |
function DataSeries:addValue(y) | |
self.nextpt = self.nextpt + 1 | |
if self.nextpt > self.length then | |
self.nextpt = 1 | |
end | |
self.points[self.nextpt] = vec2(i, y) | |
end | |
function DataSeries:update() | |
if self.value then | |
self:addValue(self.value()) | |
end | |
end | |
function DataSeries:addBreak() | |
self.nextpt = self.nextpt + 1 | |
if self.nextpt > self.length then | |
self.nextpt = 1 | |
end | |
self.points[self.nextpt] = nil | |
end | |
function DataSeries:draw(frame) | |
pushStyle() | |
local ox, oy, w, h, x, y, p, dx, dy | |
strokeWidth(self.linethick) | |
w = frame.x2 - frame.x1 | |
h = frame.y2 - frame.y1 | |
dx = w / (self.length + 1) | |
dy = h / (self.max - self.min) | |
pushMatrix() | |
translate(frame.x1, frame.y1) | |
clip(frame.x1, frame.y1, w, h) | |
if self.axis then | |
stroke(self.acolour) | |
line(0,-self.min*dy,w,-self.min*dy) | |
end | |
ox = 0 | |
x = 0 | |
stroke(self.lineclr) | |
for i = self.nextpt + 1, self.length do | |
x = x + dx | |
p = self.points[i] | |
if p ~= nil then | |
y = (p.y - self.min) * dy | |
if ox > 0 then | |
line(ox,oy,x,y) | |
end | |
ox = x | |
oy = y | |
if self.symbol == 1 then | |
noFill() | |
ellipse(x, y, self.symbolsize) | |
end | |
else | |
ox = 0 | |
end | |
end | |
for i = 1, self.nextpt do | |
x = x + dx | |
p = self.points[i] | |
if p ~= nil then | |
y = (p.y - self.min) * dy | |
if ox > 0 then | |
line(ox,oy,x,y) | |
end | |
ox = x | |
oy = y | |
if self.symbol == 1 then | |
noFill() | |
ellipse(x, y, self.symbolsize) | |
end | |
else | |
ox = 0 | |
end | |
end | |
popMatrix() | |
noClip() | |
popStyle() | |
end | |
return DataSeries | |
--]==] |
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
--[==[ | |
-- Explosion | |
local Explosion = class() | |
function Explosion:init(t) | |
t = t or {} | |
self.mesh = mesh() | |
local s = shader() | |
s.vertexProgram, s.fragmentProgram = expshader() | |
self.mesh.shader = s | |
self.mesh.texture = t.image | |
self.mesh.shader.friction = t.friction or .1 | |
self.mesh.shader.separation = 0 | |
local ft = t.factor or 10 | |
self.mesh.shader.factor = ft | |
local vels = self.mesh:buffer("velocity") | |
local origin = self.mesh:buffer("origin") | |
local angv = self.mesh:buffer("angvel") | |
local lvl = self.mesh:buffer("level") | |
local m = t.rows or 20 | |
local n = t.cols or 20 | |
vels:resize(m*n*6) | |
origin:resize(m*n*6) | |
angv:resize(m*n*6) | |
lvl:resize(m*n*6) | |
local c = t.centre | |
local w,h | |
if type(t.image) == "string" then | |
local img = readImage(t.image) | |
w,h = img.width,img.height | |
else | |
w,h = t.image.width,t.image.height | |
end | |
local w = t.width or w | |
local h = t.height or h | |
local om = t.angularSpeed or 1/ft | |
local xx,y = c.x - w/2,c.y - h/2 | |
local cl = vec2(w,h):len()/2 | |
w,h = w/m,h/n | |
xx,y = xx+w/2,y+h/2 | |
local r,th,sf,x,df,tth | |
sf = .3 | |
df = math.random() | |
for i=1,m do | |
x = xx | |
for j = 1,n do | |
r = self.mesh:addRect(x,y,w,h) | |
self.mesh:setRectTex(r,(j-1)/n,(i-1)/m,1/n,1/m) | |
th = 2*noise(i*sf+df,j*sf+df)*math.pi | |
tth = 2*om*noise(j*sf+df,i*sf+df)*math.pi | |
for k=1,6 do | |
vels[6*r-k+1] = 20*(2-(c:dist(vec2(x,y))/cl))^2 | |
*vec4(math.cos(th),math.sin(th),0,0) | |
origin[6*r-k+1] = vec2(x,y) | |
angv[6*r-k+1] = vec2(tth,0) | |
lvl[6*r-k+1] = 0 | |
end | |
x = x + w | |
end | |
y = y + h | |
end | |
if t.trails then | |
local ntr = t.trailLength or 16 | |
self.trails = mesh() | |
self.trails:setColors(255,255,255,255) | |
s = shader() | |
s.vertexProgram, s.fragmentProgram = expshader() | |
self.trails.shader = s | |
self.trails.texture = t.image | |
self.trails.shader.friction = t.friction or .1 | |
self.trails.shader.factor = ft | |
self.trails.shader.separation = t.trailSeparation or .5 | |
vels = self.trails:buffer("velocity") | |
origin = self.trails:buffer("origin") | |
angv = self.trails:buffer("angvel") | |
lvl = self.trails:buffer("level") | |
vels:resize(ntr*m*n*6) | |
origin:resize(ntr*m*n*6) | |
angv:resize(ntr*m*n*6) | |
lvl:resize(ntr*m*n*6) | |
local yy | |
xx,yy = c.x - (m-1)*w/2,c.y - (n-1)*h/2 | |
for l=1,ntr do | |
y = yy | |
for i=1,m do | |
x = xx | |
for j = 1,n do | |
r = self.trails:addRect(x,y,w,h) | |
self.trails:setRectTex(r,(j-1)/n,(i-1)/m,1/n,1/m) | |
self.trails:setRectColor(r,255,255,255,l*127/ntr) | |
th = 2*noise(i*sf+df,j*sf+df)*math.pi | |
tth = 2*om*noise(j*sf+df,i*sf+df)*math.pi | |
for k=1,6 do | |
vels[6*r-k+1] = 20*(2-(c:dist(vec2(x,y))/cl))^2 | |
*vec4(math.cos(th),math.sin(th),0,0) | |
origin[6*r-k+1] = vec2(x,y) | |
angv[6*r-k+1] = vec2(tth,0) | |
lvl[6*r-k+1] = l - ntr - 1 | |
end | |
x = x + w | |
end | |
y = y + h | |
end | |
end | |
end | |
self.start = ElapsedTime | |
end | |
function Explosion:draw() | |
if not self.active then | |
return | |
end | |
pushStyle() | |
local time = ElapsedTime - self.start | |
if not self.paused then | |
if self.trails then | |
blendMode(SRC_ALPHA,ONE) | |
self.trails.shader.time = time | |
self.trails:draw() | |
end | |
self.mesh.shader.time = time | |
end | |
blendMode(NORMAL) | |
self.mesh:draw() | |
if not self.paused then | |
if self.stop and ElapsedTime > self.start + self.stop then | |
self:deactivate() | |
end | |
end | |
popStyle() | |
end | |
function Explosion:activate(t,s) | |
t = t or 0 | |
self.start = ElapsedTime + t | |
self.stop = s | |
self.active = true | |
self.paused = false | |
end | |
function Explosion:deactivate() | |
self.active = false | |
end | |
function Explosion:pause() | |
if self.paused then | |
self.start = ElapsedTime - self.pausetime | |
self.paused = false | |
else | |
self.paused = true | |
self.pausetime = ElapsedTime - self.start | |
end | |
end | |
expshader = function() | |
return [[ | |
// | |
// The explosion vertex shader | |
// | |
precision highp float; | |
//This is the current model * view * projection matrix | |
// Codea sets it automatically | |
uniform mat4 modelViewProjection; | |
uniform float time; | |
uniform float friction; | |
uniform float factor; | |
uniform float separation; | |
lowp vec4 gravity = vec4(0.,-1.,0.,0.); | |
mediump float mtime = max(0.,time)*factor; | |
//This is the current mesh vertex position, color and tex coord | |
// Set automatically | |
attribute vec4 position; | |
attribute vec4 color; | |
attribute vec2 texCoord; | |
// These are vertex buffers: initial velocity of the square, | |
// angular velocity, | |
// centre of square | |
attribute vec4 velocity; | |
attribute vec2 angvel; | |
attribute vec2 origin; | |
attribute float level; | |
//This is an output variable that will be passed to the fragment shader | |
varying lowp vec4 vColor; | |
varying highp vec2 vTexCoord; | |
//varying mediump float vLevel; | |
// ODE: x'' = -friction x' + gravity | |
// Solution: A exp(- friction * time) + B + time*gravity/friction | |
// Initial conditions: | |
// A = gravity/(friction*friction) - x'(0)/friction | |
// B = x(0) -A | |
void main() | |
{ | |
//Pass the mesh color to the fragment shader | |
vColor = color; | |
vTexCoord = texCoord; | |
//vLevel = level; | |
lowp vec4 pos; | |
mediump float t = mtime + level * separation; | |
lowp float angle = t*angvel.x; | |
highp vec4 A = gravity/(friction*friction) - velocity/friction; | |
highp vec4 B = vec4(origin,0.,0.) - A; | |
lowp mat2 rot = mat2(cos(angle), sin(angle), -sin(angle), cos(angle)); | |
pos = (position - vec4(origin,0.,0.)); | |
pos.xy = rot * pos.xy; | |
pos += exp(-t*friction)*A + B + t * gravity/friction; | |
if (level != 0. && t < 1.) pos = vec4(0.,0.,0.,1.); | |
//Multiply the vertex position by our combined transform | |
gl_Position = modelViewProjection * pos; | |
} | |
]],[[ | |
// | |
// A basic fragment shader | |
// | |
//This represents the current texture on the mesh | |
uniform lowp sampler2D texture; | |
//The interpolated vertex color for this fragment | |
varying lowp vec4 vColor; | |
//The interpolated texture coordinate for this fragment | |
varying highp vec2 vTexCoord; | |
void main() | |
{ | |
//Sample the texture at the interpolated coordinate | |
lowp vec4 col = texture2D( texture, vTexCoord ); | |
col *= vColor; | |
//Set the output color to the texture color | |
gl_FragColor = col; | |
} | |
]] | |
end | |
return Explosion | |
--]==] |
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
--[==[ | |
-- Firework display | |
-- Authors: Stavrogin (original standalone program) | |
-- Andrew Stacey (conversion to "celebration" class) | |
-- Websites: http://pagantongue.posterous.com/fireworks | |
-- http://www.math.ntnu.no/~stacey/HowDidIDoThat/iPad/Codea.html | |
-- Licence: unknown | |
local NUMSTARS = 50 | |
local COLOURS = { | |
color(255, 0, 0, 125), | |
color(255, 227, 0, 125), | |
color(99, 255, 0, 125), | |
color(0, 140, 255, 125), | |
color(238, 0, 255, 125), | |
color(255, 156, 0, 125), | |
color(0, 255, 189, 125), | |
color(255, 0, 146, 125) | |
} | |
local Stars = 1 | |
local See_Smoke = 1 | |
local Firework_Parts = 100 | |
local Life = 30 | |
local Life_Variation = 100 | |
local Air_Resistance = 0.1 | |
local PartSize = 35 | |
local PartSize_Variation = 50 | |
local Velocity = 10 | |
local Velocity_Variation = 100 | |
local Color_Variation = 50 | |
local Fireworks = class() | |
function Fireworks:init() | |
self.stars = {} | |
self.fireworks = {} | |
self.smokes = {} | |
for i=0,NUMSTARS do | |
self.stars[i] = {x=math.random(WIDTH),y=math.random(HEIGHT)} | |
end | |
self.active = false | |
end | |
function Fireworks:draw() | |
pushStyle() | |
noSmooth() | |
fill(0, 0, 0, 125) | |
rect(0,0,WIDTH,HEIGHT) | |
-- draw stars | |
if Stars == 1 then | |
for i=0, NUMSTARS do | |
fill(255, 255, 255, math.random(255)) | |
rect(self.stars[i].x, | |
self.stars[i].y, | |
math.random(3), | |
math.random(3)) | |
end | |
end | |
local dead = true | |
if self.ftimes[self.nfw] then | |
if ElapsedTime - self.fstart > self.ftimes[self.nfw] then | |
self.nfw = self.nfw + 1 | |
self.fstart = ElapsedTime | |
sound(SOUND_EXPLODE) | |
end | |
dead = false | |
end | |
local sm,fw | |
for k = 1,self.nfw do | |
sm = self.smokes[k] | |
fw = self.fireworks[k] | |
if not sm:isDead() then | |
dead = false | |
sm:draw() | |
end | |
if not fw:isDead() then | |
dead = false | |
fw:draw() | |
end | |
end | |
if dead then | |
self.active = false | |
end | |
popStyle() | |
return self.nfw | |
end | |
function Fireworks:newshow(p) | |
self.fireworks = {} | |
self.smokes = {} | |
self.ftimes = {} | |
if not p then | |
local n = math.random(4,8) | |
p = {} | |
for i = 1,n do | |
table.insert(p,{ | |
math.random(50,WIDTH - 50), | |
math.random(HEIGHT/2,HEIGHT - 50) | |
}) | |
end | |
end | |
local m,t | |
for k,v in ipairs(p) do | |
m = math.random(#COLOURS) | |
table.insert(self.fireworks,Firework(v[1],v[2],COLOURS[m])) | |
if See_Smoke == 1 then | |
table.insert(self.smokes,Smoke(v[1],v[2],3)) | |
end | |
t = math.random() + .5 | |
table.insert(self.ftimes,t) | |
self.fstart = ElapsedTime | |
self.nfw = 1 | |
end | |
table.remove(self.ftimes) | |
self.active = true | |
sound(SOUND_EXPLODE) | |
end | |
local Firework = class() | |
function Firework:init(x,y,colour) | |
-- you can accept and set parameters here | |
self.p = {} | |
self.numParticles = Firework_Parts | |
for i=1,self.numParticles do | |
local psize = genNumber(PartSize,PartSize_Variation) | |
local v = vec2(math.random(-100,100),math.random(-100,100)) | |
v = v:normalize() | |
v = v * genNumber(Velocity,Velocity_Variation) | |
local c = color(genNumber(colour.r,Color_Variation), | |
genNumber(colour.g,Color_Variation), | |
genNumber(colour.b,Color_Variation), | |
colour.a | |
) | |
self.p[i] = Particle(x, | |
y, | |
psize, | |
genNumber(Life,Life_Variation), | |
c, | |
v) | |
end | |
end | |
function Firework:draw() | |
local resistance = 1/(Air_Resistance + 1) | |
local g = vec2(Gravity.x,Gravity.y) | |
for i=1,self.numParticles do | |
local p = self.p[i] | |
p.x = p.x + p.v.x | |
p.y = p.y + p.v.y | |
p.v = p.v + g | |
p.v = p.v * resistance | |
local size = math.random(PartSize) * (p.lifeLeft/p.life) | |
p.width = size | |
p.height = size | |
p:draw() | |
end | |
end | |
function Firework:isDead() | |
for i=1,self.numParticles do | |
p = self.p[i] | |
if p.lifeLeft ~= 0 and | |
(p.x>0 and p.x<WIDTH and p.y>0 and p.y<HEIGHT)then | |
return false | |
end | |
end | |
return true | |
end | |
local Particle = class() | |
local Particle.DEFAULT_OPACITY = 125 | |
local Particle.DEFAULT_ANGLE = 0 | |
local Particle.DEFAULT_MASS = 1 | |
function Particle:init(posx,posy,size,life,colour, | |
velocity,mass,angle,sprite) | |
-- position | |
self.x = posx | |
self.y = posy | |
self.ox = 0 | |
self.oy = 0 | |
-- size | |
self.width = size | |
self.height = size | |
-- color | |
if colour == nil then | |
self.color = color(255, 255, 255, 255) | |
else | |
self.color = colour | |
end | |
-- velocity | |
self.v = velocity | |
-- life | |
self.life = life | |
self.lifeLeft= life | |
-- sprite | |
self.sprite = sprite | |
-- mass | |
if mass == nil then | |
self.mass = DEFAULT_MASS | |
else | |
self.mass = mass | |
end | |
-- angle | |
if angle == nil then | |
self.angle = self.DEFAULT_ANGLE | |
else | |
self.angle = angle | |
end | |
end | |
function Particle:draw() | |
if self.lifeLeft > 0 then | |
self.lifeLeft = self.lifeLeft - 1 | |
end | |
if self.lifeLeft ~= 0 then | |
if self.sprite == nil then | |
fill(self.color) | |
ellipse(self.x,self.y,self.width,self.height) | |
else | |
pushMatrix() | |
translate(self.x,self.y) | |
rotate(self.angle) | |
tint(self.color) | |
sprite(self.sprite,0,0,self.width,self.height) | |
popMatrix() | |
end | |
end | |
end | |
local function genNumber(number,variation) | |
ret = variation*0.01*number | |
ret = number + math.random(-ret,ret) | |
return ret | |
end | |
local Smoke = class() | |
function Smoke:init(x,y,n) | |
self.numparts = n | |
-- color used to tint the particle | |
local c = color(73, 73, 73, 69) | |
-- name of the sprite of each smoke particle | |
local s = "Tyrian Remastered:Dark Orb" | |
self.parts = {} | |
for i=0,n do | |
-- initial size of the smoke particle | |
local sz = genNumber(60,100) | |
self.parts[i] = Particle(genNumber(x,20), | |
genNumber(y,20),sz,-7,c,nil,-1,genNumber(180,100),s) | |
end | |
-- rotation speed | |
self.rSpeed = 0.5 | |
self.windX = 1 | |
self.windY = 1 | |
end | |
function Smoke:draw() | |
for i=1,self.numparts do | |
local p = self.parts[i] | |
if p.color.a > 0 then | |
p.angle = p.angle + self.rSpeed | |
p.width = p.width + 3 | |
p.height = p.height + 3 | |
p.color.a = p.color.a - 0.2 | |
p.x = p.x + self.windX | |
p.y = p.y + self.windY | |
p:draw() | |
end | |
end | |
end | |
function Smoke:isDead() | |
for i=1,self.numparts do | |
local p = self.parts[i] | |
if p.color.a > 0 then | |
return false | |
end | |
end | |
return true | |
end | |
return Fireworks | |
--]==] |
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
--[==[ | |
-- Frame | |
-- ==================== | |
-- Frame | |
-- ver. 0.1 | |
-- a simple rectangle to act as a base for controls | |
-- ==================== | |
local Frame = class() | |
function Frame:init(x1, y1, x2, y2) | |
self.x1 = x1 | |
self.x2 = x2 | |
self.y1 = y1 | |
self.y2 = y2 | |
end | |
function Frame:draw() | |
pushStyle() | |
rectMode(CORNERS) | |
rect(self.x1, self.y1, self.x2, self.y2) | |
popStyle() | |
end | |
function Frame:gloss() | |
local i, t, r, y | |
pushStyle() | |
fill(255, 255, 255, 255) | |
rectMode(CORNERS) | |
rect(self.x1, self.y1, self.x2, self.y2) | |
r = (self.y2 - self.y1) / 2 | |
for i = 1 , r do | |
t = 255 - i | |
stroke(t, t, t, 255) | |
y = (self.y1 + self.y2) / 2 | |
line(self.x1, y + i, self.x2, y + i) | |
line(self.x1, y - i, self.x2, y - i) | |
end | |
popStyle() | |
end | |
function Frame:touched(touch) | |
if touch.x >= self.x1 and touch.x <= self.x2 then | |
if touch.y >= self.y1 and touch.y <= self.y2 then | |
return true | |
end | |
end | |
return false | |
end | |
function Frame:midx() | |
return (self.x1 + self.x2) / 2 | |
end | |
function Frame:midy() | |
return (self.y1 + self.y2) / 2 | |
end | |
return Frame | |
--]==] |
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
VERSION = "2.2a" | |
clearProjectData() | |
-- DEBUG = true | |
-- Use this function to perform your initial setup | |
function setup() | |
autogist = AutoGist("Library Graphics","A library of classes and functions relating to graphical things.",VERSION) | |
autogist:backup(true) | |
--displayMode(FULLSCREEN_NO_BUTTONS) | |
cmodule "Library Graphics" | |
cmodule.path("Library Base", "Library UI", "Library Utilities", "Library Maths") | |
cimport "TestSuite" | |
local Touches = cimport "Touch" | |
local UI = cimport "UI" | |
local Debug = cimport "Debug" | |
local Colour = unpack(cimport "Colour") | |
local Explosion = cimport "Explosion" | |
local TextNode = cimport "TextNode" | |
local View = cimport "View" | |
Bezier = cimport "Bezier" | |
touches = Touches() | |
ui = UI(touches) | |
debug = Debug({ui = ui}) | |
ui:systemmenu() | |
testsuite.initialise({ui = ui}) | |
debug:log({ | |
name = "Screen north west", | |
message = function() local x,y = RectAnchorOf(Screen,"north west") return x .. ", " .. y end | |
}) | |
--debug:activate() | |
tn = TextNode({ | |
pos = function() return WIDTH/2,800 end, | |
anchor = "centre", | |
--angle = 30, | |
ui = ui, | |
fit = true, | |
maxHeight = HEIGHT, | |
}) | |
touches:pushHandler(tn) | |
view = View(ui,touches) | |
parameter.watch("view.baseRotation") | |
shape = mesh() | |
shape.texture = "Documents:Daniel at barnehage" | |
local x,y,z = 1,1,1 | |
shape.vertices = { | |
vec3(x,0,0), | |
vec3(0,y,0), | |
vec3(0,0,z), | |
vec3(0,0,0), | |
vec3(0,y,0), | |
vec3(0,0,z), | |
vec3(x,0,0), | |
vec3(0,0,0), | |
vec3(0,0,z), | |
vec3(x,0,0), | |
vec3(0,y,0), | |
vec3(0,0,0) | |
} | |
shape.colors = { | |
Colour.svg.Red, | |
Colour.svg.Green, | |
Colour.svg.Blue, | |
Colour.svg.White, | |
Colour.svg.Green, | |
Colour.svg.Blue, | |
Colour.svg.Red, | |
Colour.svg.White, | |
Colour.svg.Blue, | |
Colour.svg.Red, | |
Colour.svg.Green, | |
Colour.svg.White | |
} | |
view.eye = vec3(5,0,0) | |
view.range = .25 | |
tw = {t = 0,s=0} | |
ui:setTimer(5,function() tween(5,tw,{t = 1,s = 0}) return true end) | |
ui:setTimer(12,function() tween(5,tw,{t = 1,s = 1}) return true end) | |
ui:setTimer(18,function() tw = {t = 0,s = 1} return true end) | |
ui:setTimer(19,function() tween(5,tw,{t = 1,s = 0}) return true end) | |
explosion = Explosion({ | |
image = "Cargo Bot:Codea Icon", | |
trails = true, | |
centre = vec2(WIDTH/2,HEIGHT/2) | |
}) | |
explosion:activate(1,5) | |
--ui:addNotice({text = "Watch carefully"}) | |
strokeWidth(5) | |
stroke(255, 0, 0, 255) | |
ncurves = {} | |
--[[ | |
local b | |
for k=1,300 do | |
b = Bezier(vec2(100,100), | |
vec2(100,200), | |
vec2(200,100), | |
vec2(200,200) | |
) | |
b:makeDrawable() | |
table.insert(ncurves,b) | |
end | |
--]] | |
fps = {} | |
for k=1,20 do | |
table.insert(fps,1/60) | |
end | |
afps = 60 | |
parameter.watch("math.floor(20/afps)") | |
--displayMode(FULLSCREEN) | |
local width = 10 | |
pts = {vec2(width/2,0), | |
vec2(width/2,3*HEIGHT-width/2), | |
vec2(WIDTH - width/2,-2*HEIGHT+width/2), | |
vec2(WIDTH - width/2,HEIGHT)} | |
--[[ | |
vcurves = {} | |
local n = 200 | |
for k=1,n do | |
table.insert(vcurves,{ | |
function(t) return vec2(0,HEIGHT*(1+math.sin(t + 2*math.pi*k/n))/2) end, | |
function(t) return vec2(WIDTH/3,HEIGHT*(1+math.sin(t + 2*math.pi*(k+1)/n))/2) end, | |
function(t) return vec2(2*WIDTH/3,HEIGHT*(1+math.sin(t + 2*math.pi*(k+2)/n))/2) end, | |
function(t) return vec2(WIDTH,HEIGHT*(1+math.sin(t + 2*math.pi*(k+3)/n))/2) end | |
}) | |
end | |
--]] | |
parameter.watch("math.floor(20/afps)") | |
curves = {} | |
hcurves = {} | |
parameter.watch("#curves") | |
tpts = {} | |
cpts = {} | |
end | |
function draw() | |
table.remove(fps,1) | |
table.insert(fps,DeltaTime) | |
afps = 0 | |
for k,v in ipairs(fps) do | |
afps = afps + v | |
end | |
background(75, 104, 90, 255) | |
strokeWidth(5) | |
stroke(151, 115, 115, 255) | |
for i=1,3 do | |
line(pts[i].x,pts[i].y,pts[i+1].x,pts[i+1].y) | |
end | |
fill(160, 172, 22, 255) | |
noStroke() | |
for i = 1,4 do | |
ellipse(pts[i].x,pts[i].y,20) | |
end | |
strokeWidth(5) | |
stroke(255, 255, 255, 255) | |
for k,v in ipairs(ncurves) do | |
v:draw() | |
end | |
bezier(pts) | |
stroke(81, 255, 0, 255) | |
bezier(vec2(10,10),vec2(WIDTH/2,2*HEIGHT-20),vec2(WIDTH-10,10)) | |
for k,v in ipairs(curves) do | |
v:draw() | |
end | |
stroke(0, 0, 0, 255) | |
strokeWidth(3) | |
for k,v in ipairs(hcurves) do | |
v:draw() | |
end | |
noStroke() | |
for k,v in ipairs(cpts) do | |
ellipse(v.x,v.y,15) | |
end | |
--[[ | |
for _,v in ipairs(vcurves) do | |
bezier({v[1](ElapsedTime),v[2](ElapsedTime),v[3](ElapsedTime),v[4](ElapsedTime)}) | |
end | |
--]] | |
end | |
function touched(touch) | |
local tv = vec2(touch.x,touch.y) | |
if touch.state == BEGAN then | |
mpoint = nil | |
for k,v in ipairs(cpts) do | |
if v:distSqr(tv) < 900 then | |
mpoint = k | |
end | |
end | |
if not mpoint then | |
table.insert(tpts,vec2(touch.x,touch.y)) | |
table.insert(cpts,vec2(touch.x,touch.y)) | |
if #tpts == 3 then | |
local a,b,aa,bb | |
a,b,th = QuickHobby(tpts,th) | |
aa = a:clone() | |
bb = b:clone() | |
aa:setStyle({width = 8, colour = color(75, 104, 90, 255)}) | |
bb:setStyle({width = 8, colour = color(75, 104, 90, 255)}) | |
table.remove(curves) | |
table.remove(curves) | |
table.insert(curves,aa) | |
table.insert(curves,a) | |
table.insert(curves,bb) | |
table.insert(curves,b) | |
table.remove(tpts,1) | |
elseif #tpts == 2 then | |
cvs = QHobby(tpts) | |
curves = {} | |
for _,v in ipairs(cvs) do | |
vv = v:clone() | |
vv:setStyle({width = 8, colour = color(75, 104, 90, 255)}) | |
table.insert(curves,vv) | |
table.insert(curves,v) | |
end | |
end | |
end | |
elseif mpoint then | |
cpts[mpoint] = tv | |
if mpoint == #cpts then | |
tpts[#tpts] = tv | |
elseif mpoint == #cpts - 1 then | |
tpts[1] = tv | |
end | |
end | |
if touch.state == ENDED then | |
--if #cpts > 2 then | |
hcurves = Hobby(cpts) | |
--end | |
if mpoint then | |
local cvs,vv | |
cvs, th = QHobby(cpts) | |
curves = {} | |
for _,v in ipairs(cvs) do | |
vv = v:clone() | |
vv:setStyle({width = 8, colour = color(75, 104, 90, 255)}) | |
table.insert(curves,vv) | |
table.insert(curves,v) | |
end | |
end | |
end | |
end | |
--[[ | |
-- This function gets called once every frame | |
function draw() | |
-- process touches and taps | |
table.remove(fps,1) | |
table.insert(fps,DeltaTime) | |
afps = 0 | |
for k,v in ipairs(fps) do | |
afps = afps + v | |
end | |
touches:draw() | |
background(34, 47, 53, 255) | |
--[=[ | |
tn:draw() | |
pushMatrix() | |
view:draw() | |
vm = viewMatrix() | |
qt = qslp(tw.t) | |
qt = qt*qlp(tw.s) | |
modelMatrix(qt:tomatrix()) | |
perspective(40,WIDTH/HEIGHT) | |
camera(10,10,10,0,0,0,0,1,0) | |
shape:draw() | |
popMatrix() | |
resetMatrix() | |
viewMatrix(matrix()) | |
ortho() | |
--]=] | |
strokeWidth(5) | |
stroke(255, 0, 0, 255) | |
for k,v in ipairs(curves) do | |
v:draw() | |
end | |
explosion:draw() | |
ui:draw() | |
debug:draw() | |
touches:show() | |
end | |
function touched(touch) | |
touches:addTouch(touch) | |
end | |
--]] | |
function orientationChanged(o) | |
if ui then | |
ui:orientationChanged(o) | |
end | |
end | |
function fullscreen() | |
end | |
function reset() | |
end |
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
--[==[ | |
-- Path object | |
local Path = class() | |
local PATH_STEP = 1 | |
local PATH_RES = .05*.05 | |
local PATH_RENDER = 0 | |
local PATH_CREATE = 1 | |
local PATH_POINTS = 2 | |
function Path:init(t) | |
t = t or {} | |
self.style = t.style or {} | |
self.lastPoint = vec2(0,0) | |
self.elements = t.elements or {} | |
if t.touchHandler then | |
t.touchHandler:pushHandler(self) | |
end | |
self.touchpt = {} | |
self.m = mesh() | |
end | |
function Path:deletePoints() | |
self.elements = {} | |
self.lastPoint = vec2(0,0) | |
end | |
function Path:draw() | |
pushStyle() | |
resetStyle() | |
self:applyStyle(PATH_RENDER) | |
self.m:draw() | |
if self.edit then | |
self:applyStyle(PATH_POINTS) | |
local r = self.ptRadius | |
local lpt | |
pushStyle() | |
noStroke() | |
for k,v in ipairs(self.elements) do | |
ellipse(v[2].x,v[2].y,r) | |
if v[1] == "curve" then | |
popStyle() | |
line(lpt.x,lpt.y,v[2].x,v[2].y) | |
line(v[3].x,v[3].y,v[4].x,v[4].y) | |
pushStyle() | |
noStroke() | |
ellipse(v[3].x,v[3].y,r) | |
ellipse(v[4].x,v[4].y,r) | |
lpt = v[4] | |
else | |
lpt = v[2] | |
end | |
end | |
popStyle() | |
end | |
popStyle() | |
end | |
function Path:generate() | |
self:applyStyle(PATH_CREATE) | |
local ver = {} | |
local s,h | |
for k,v in ipairs(self.elements) do | |
ver,s,h = self:getPoints(v,ver,s,h) | |
end | |
if h then | |
ver,s = HobbyPoints(ver,s,h) | |
end | |
self.lastPoint = s | |
--debug:log({name = "path", message = "got called"}) | |
print("got called") | |
self:makeMesh(ver) | |
end | |
function Path:applyStyle(t) | |
local s = self.style or {} | |
if t == PATH_CREATE then | |
self.linewidth = s.linewidth or 5 | |
self.blur = s.blur or 1 | |
self.smooth = s.smooth or false | |
self.drawColour = s.drawColour or Colour.svg.Black | |
elseif t == PATH_RENDER then | |
elseif t == PATH_POINTS then | |
self.ptRadius = s.pointRadius or 15 | |
local l = s.pointLineWidth or 3 | |
strokeWidth(l) | |
local l = s.pointLineCap or SQUARE | |
lineCapMode(l) | |
end | |
end | |
function Path:addElement(e) | |
table.insert(self.elements,e) | |
end | |
function Path:moveto(v) | |
self:addElement({"move",v}) | |
self.lastPoint = v | |
end | |
function Path:lineto(v) | |
self:addElement({"line",v}) | |
self.lastPoint = v | |
end | |
function Path:curveto(b,c,d) | |
self:addElement({"curve",b,c,d}) | |
self.lastPoint = d | |
end | |
function Path:curvethrough(v) | |
self:addElement({"hobby",v}) | |
self.lastPoint = v | |
end | |
function Path:getPoints(e,ver,s,h) | |
local t = e[1] | |
if h and t ~= "hobby" then | |
ver,s = HobbyPoints(ver,s,h) | |
end | |
if t == "move" then | |
table.insert(ver,{e[2]}) | |
return ver, e[2] | |
elseif t == "line" then | |
local n = e[2] - s | |
n = n:rotate90() | |
n = n/n:len() | |
table.insert(ver,{s,n}) | |
table.insert(ver,{e[2],n}) | |
return ver,e[2] | |
elseif e[1] == "curve" then | |
return BezierPoints(ver,s,e[2],e[3],e[4]) | |
elseif e[1] == "hobby" then | |
h = h or {} | |
table.insert(h,e[2]) | |
return ver,s,h | |
else | |
return ver,s | |
end | |
end | |
function Path:makeMesh(pts) | |
local ver = {} | |
local col = {} | |
local l = self.linewidth/2 | |
local b = self.blur + l | |
local s = false -- self.smooth | |
local c = self.drawColour | |
local ct = Colour.opacity(c,0) | |
local p,n | |
local m = 0 | |
for k,v in ipairs(pts) do | |
if v[2] and n then | |
table.insert(ver,p + l*n) | |
table.insert(ver,p - l*n) | |
table.insert(ver,v[1] + l*v[2]) | |
table.insert(ver,v[1] + l*v[2]) | |
table.insert(ver,v[1] - l*v[2]) | |
table.insert(ver,p - l*n) | |
for i=1,6 do | |
table.insert(col,c) | |
end | |
m = m + 6 | |
if s then | |
table.insert(ver,p + l*n) | |
table.insert(ver,p + b*n) | |
table.insert(ver,v[1] + l*v[2]) | |
table.insert(ver,v[1] + l*v[2]) | |
table.insert(ver,v[1] + b*v[2]) | |
table.insert(ver,p + b*n) | |
table.insert(ver,p - l*n) | |
table.insert(ver,p - b*n) | |
table.insert(ver,v[1] - l*v[2]) | |
table.insert(ver,v[1] - l*v[2]) | |
table.insert(ver,v[1] - b*v[2]) | |
table.insert(ver,p - b*n) | |
for i=1,12 do | |
table.insert(col,ct) | |
end | |
end | |
end | |
p,n = unpack(v) | |
end | |
self.m.vertices = ver | |
self.m.colors = col | |
end | |
function Path:isTouchedBy(touch) | |
local p = vec2(touch.x,touch.y) | |
for k,v in ipairs(self.elements) do | |
if p:distSqr(v[2]) < 625 then | |
self.touchpt[touch.id] = v[2] | |
self.edit = true | |
return true | |
end | |
if v[1] == "curve" then | |
if p:distSqr(v[3]) < 625 then | |
self.touchpt[touch.id] = v[3] | |
self.edit = true | |
return true | |
end | |
if p:distSqr(v[4]) < 625 then | |
self.touchpt[touch.id] = v[4] | |
self.edit = true | |
return true | |
end | |
end | |
end | |
self.edit = false | |
return false | |
end | |
function Path:processTouches(g) | |
local regenerate = false | |
if g.updated then | |
for k,t in ipairs(g.touchesArr) do | |
if t.updated then | |
regenerate = true | |
self.touchpt[t.touch.id].x = | |
self.touchpt[t.touch.id].x | |
+ t.touch.deltaX | |
self.touchpt[t.touch.id].y = | |
self.touchpt[t.touch.id].y | |
+ t.touch.deltaY | |
end | |
if t.touch.state == ENDED then | |
self.touchpt[t.touch.id] = nil | |
end | |
end | |
g:noted() | |
if regenerate then | |
self:generate() | |
end | |
end | |
if g.type.ended then | |
g:reset() | |
end | |
end | |
function Path:setLineWidth(l) | |
self.style.linewidth = l | |
end | |
function Path:setBlur(l) | |
self.style.blur = l | |
end | |
function Path:setSmooth(l) | |
self.style.smooth = l | |
end | |
function Path:setColour(c) | |
self.style.drawColour = c | |
end | |
function BezierPoints(pts,a,b,c,d) | |
pts = pts or {} | |
if not(type(a) == "table" and a.is_a and a:is_a(Bezier)) then | |
a = Bezier(a,b,c,d) | |
end | |
local t = 0 | |
local r = PATH_RES | |
local s = PATH_STEP | |
local tpt = a | |
table.insert(pts,{tpt,a:unitNormal(0)}) | |
local dis | |
local p | |
while t < 1 do | |
dis = 0 | |
while dis < r do | |
t = t + s | |
p = a:point(t) | |
dis = p:distSqr(tpt) | |
end | |
if t > 1 then | |
t = 1 | |
p = d | |
end | |
table.insert(pts,{p,a:unitNormal(t)}) | |
tpt = p | |
end | |
return pts | |
end | |
function HobbyPoints(ver,s,pts,extra) | |
ver = ver or {} | |
if #pts == 1 then | |
local v = pts[1] | |
if type(v) == "table" then | |
v = v[1] | |
end | |
local n = v - s | |
n = n:rotate90() | |
n = n/n:len() | |
table.insert(ver,{s,n}) | |
table.insert(ver,{v,n}) | |
return ver | |
end | |
local apts = {} | |
table.insert(apts,s) | |
for _,v in ipairs(pts) do | |
table.insert(apts,v) | |
end | |
local bcurves = Hobby(apts,extra) | |
for _,v in ipairs(bcurves) do | |
ver = BezierPoints(v,ver) | |
end | |
return ver | |
end | |
return Path | |
--]==] |
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
--[==[ | |
-- Text Node | |
local Font,_,Textarea = unpack(cimport "Font",nil) | |
local Colour = unpack(cimport "Colour",nil) | |
local UTF8 = cimport "utf8" | |
cimport "Keyboard" | |
--cimport "RoundedRect" | |
cimport "ColourNames" | |
local TextNode = class(Textarea) | |
function TextNode:init(t) | |
t = t or {} | |
t.font = t.font or Font({name = "AmericanTypewriter", size = 12}) | |
t.width = t.width or "32em" | |
t.height = t.height or "1lh" | |
if t.fit == nil then | |
t.fit = true | |
end | |
t.angle = t.angle or 0 | |
Textarea.init(self,t) | |
self.keyboard = t.keyboard or "fullqwerty" | |
self.ui = t.ui | |
self:activate() | |
self.edit = t.edit or true | |
self.savecolour = self.colour | |
self:setColour(Colour.opacity(Colour.svg.Black,50)) | |
self.ui:useKeyboard(self.keyboard, | |
function(k) return self:processKey(k) end) | |
end | |
function TextNode:processTouches(g) | |
if g.type.ended and g.type.tap and g.num == 2 then | |
self:setEdit() | |
g:reset() | |
return | |
end | |
if g.updated then | |
if g.num == 1 then | |
local t = g.touchesArr[1] | |
if self.edit then | |
local td = | |
vec2(t.touch.deltaX, | |
t.touch.deltaY):rotate(-math.rad(self.angle)) | |
self:setSize({ | |
width = self.width + td.x, | |
height = self.height + td.y, | |
maxWidth = self.mwidth + td.x, | |
maxHeight = self.mheight + td.y, | |
}) | |
else | |
self.anchor = nil | |
self.opos = function() return self.x, self.y end | |
self.x = self.x + t.touch.deltaX | |
self.y = self.y + t.touch.deltaY | |
end | |
elseif g.num == 2 then | |
local ta,tb = g.touchesArr[1],g.touchesArr[2] | |
local sa,sb,ea,eb | |
ea = --OrientationInverse(PORTRAIT, | |
vec2(ta.touch.x,ta.touch.y) | |
eb = --OrientationInverse(PORTRAIT, | |
vec2(tb.touch.x,tb.touch.y) | |
if ta.updated and ta.state ~= BEGAN then | |
sa = --OrientationInverse(PORTRAIT, | |
vec2(ta.touch.prevX,ta.touch.prevY) | |
else | |
sa = ea | |
end | |
if tb.updated and tb.state ~= BEGAN then | |
sb = --OrientationInverse(PORTRAIT, | |
vec2(tb.touch.prevX,tb.touch.prevY) | |
else | |
sb = eb | |
end | |
local o = vec2(self.x,self.y) | |
local ang = (sb - sa):angleBetween(eb - ea) | |
local sc = ((sb + sa)/2 - o):rotate(ang) | |
local ec = (ea + eb)/2 - o | |
self.angle = self.angle + math.deg(ang) | |
self.anchor = nil | |
self.opos = function() return self.x, self.y end | |
self.x = self.x + ec.x - sc.x | |
self.y = self.y + ec.y - sc.y | |
end | |
end | |
g:noted() | |
if g.type.ended and not g.type.tap then | |
g:reset() | |
end | |
end | |
function TextNode:processKey(k) | |
if k == BACKSPACE then | |
self:delChar() | |
elseif k == RETURN then | |
self:addChar(UTF8("\n")) | |
else | |
self:addChar(k) | |
end | |
return false | |
end | |
function TextNode:setEdit(e) | |
if e == nil then | |
e = not self.edit | |
end | |
if e then | |
self.edit = true | |
self.savecolour = self.colour | |
self:setColour(Colour.opacity(Colour.svg.Black,50)) | |
self.ui:useKeyboard(self.keyboard, | |
function(k) return self:processKey(k) end) | |
else | |
self.edit = false | |
self:setColour(self.savecolour) | |
self.ui:unuseKeyboard() | |
end | |
end | |
return TextNode | |
--]==] |
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
--[==[ | |
-- Trend Graph | |
local TrendGraph = class() | |
function TrendGraph:init(t) | |
self.outer = Frame(t.x, t.y, t.x + t.width, t.y + t.height) | |
self.inner = Frame(t.x + 20, t.y + 10, t.x + t.width - 10, t.y + t.height - 20) | |
self.title = t.title or "" | |
self.font = t.font or "Copperplate" | |
self.fontSize = t.fontSize or 16 | |
self.colour = t.colour or Colour.svg.SlateGray | |
self.icolour = Colour.tint(self.colour,50) | |
self.rate = t.rate or 1 | |
self.series = {} | |
self.xlabel = "" | |
self.ylabel = "" | |
font(self.font) | |
fontSize(self.fontSize) | |
local w,h = textSize(self.title) | |
self.titlex = self.outer:midx() - w/2 | |
self.titley = self.outer.y2 - h - 5 | |
self.frame = 0 | |
end | |
function TrendGraph:addSeries(name, len, min, max, sym, size, thick, clr, value) | |
local i | |
i = table.getn(self.series) | |
self.series[i + 1] = DataSeries(name, len, min, max, sym, size, thick, clr, value) | |
end | |
function TrendGraph:draw(pts) | |
pushMatrix() | |
pushStyle() | |
self.frame = self.frame + 1 | |
fill(self.colour) | |
self.outer:draw() | |
stroke(253, 3, 3, 255) | |
fill(self.icolour) | |
self.inner:draw() | |
fill(0, 0, 0, 255) | |
noSmooth() | |
font(self.font) | |
fontSize(self.fontSize) | |
textMode(CORNER) | |
text(self.title,self.titlex,self.titley) | |
for s, series in ipairs(self.series) do | |
if self.frame%self.rate == 0 and not self.paused then | |
series:update() | |
end | |
series:draw(self.inner, self.min, self.max) | |
end | |
popMatrix() | |
popStyle() | |
end | |
function TrendGraph:pause(p) | |
self.paused = p | |
for s, series in ipairs(self.series) do | |
series:addBreak() | |
end | |
end | |
return TrendGraph | |
--]==] |
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
--[==[ | |
-- View | |
--[[ | |
The "View" class defines an object which handles positioning of | |
things in 3-dimensions and how they project to the 2-dimensional iPad | |
screen. It handles transformations and certain other aspects that are | |
more to do with the surrounding View than a particular object in it. | |
In this class, the term "internal" refers to the 3-dimensional | |
representation of View. The term "external" refers to things after | |
the projection to the plane of the iPad screen. | |
--]] | |
local View = class() | |
local Quaternion = cimport "Quaternion" | |
cimport "Coordinates" | |
--[[ | |
We need to know the user interface object as we want to have our own | |
menu for the user to choose various options. | |
--]] | |
function View:init(ui,t,p) | |
p = p or {} | |
self.baseRotation = Quaternion.Rotation(0,vec3(1,0,0)) | |
self.orientRotation = Quaternion.unit() | |
self.intScale = 1 | |
self.extScale = 1 | |
self.fishEye = 1 | |
self.speed = 1/600 | |
self.origin = vec3(0,0,0) | |
self.velocity = vec3(0,0,0) | |
self.acceleration = 1 | |
self.friction = .2 | |
self.bgColour = color(0,0,0,255) | |
self.eye = vec3(0,0,15) | |
self.looking = -self.eye | |
self.up = vec3(0,1,0) | |
self.light = vec3(0,1,0) | |
self.currentGravity = self.orientRotation:Gravity() | |
self.useGravity = true | |
self.rotation = self.baseRotation | |
self.matrix = matrix() | |
self.initials = {} | |
self.ui = ui | |
for k,v in pairs(p) do | |
self[k] = v | |
end | |
if Menu then | |
local m = ui:addMenu({title = "View", attach = true}) | |
m:addItem({ | |
title = "Use Gravity", | |
action = function() | |
self:gravityOnOff() | |
return true | |
end, | |
highlight = function() | |
return self.useGravity | |
end | |
}) | |
ui:addHelp({title = "View", text = {"Instructions:", | |
"Single tap: toggle the reaction to tilt", | |
"Double tap: restore initial spatial settings", | |
"Single swipe: rotate the object about an axis in the plane of the screen", | |
"Double swipe: translate the object in 3-View", | |
"Triple swipe: translate the projected image of the object", | |
"Vertical pinch: scale the object in 3-View", | |
"Horizontal pinch: scale the projected image" | |
}} | |
) | |
end | |
t:pushHandler(self) | |
self:saveInitials() | |
end | |
--[[ | |
Make sure we can get back to where we started. | |
--]] | |
function View:saveInitials() | |
self.initials.baseRotation = self.baseRotation | |
self.initials.intScale = self.intScale | |
self.initials.extScale = self.extScale | |
self.initials.origin = self.origin | |
self.initials.eye = self.eye | |
self.initials.currentGravity = self.currentGravity | |
self.initials.fishEye = self.fishEye | |
self.initials.light = self.light | |
end | |
--[[ | |
Get us back to where we started. | |
--]] | |
function View:restoreInitials() | |
self.baseRotation = self.initials.baseRotation | |
self.intScale = self.initials.intScale | |
self.extScale = self.initials.extScale | |
self.origin = self.initials.origin | |
self.eye = self.initials.eye | |
self.currentGravity = self.initials.currentGravity | |
self.fishEye = self.initials.fishEye | |
self.light = self.initials.light | |
end | |
function View:reset() | |
self:restoreInitials() | |
end | |
--[[ | |
This is the main draw function. It does not actually do much drawing | |
but rather sets up various things for use by other objects. | |
--]] | |
function View:draw() | |
self.orientRotation:updateReferenceFrame() | |
if self.angVelocity then | |
if ElapsedTime - self.angvTime > 1 then | |
self.angVelocity = nil | |
else | |
qa = self.angVelocity(ElapsedTime - self.angvTime) | |
self.baseRotation = self.baseRotation * qa | |
end | |
end | |
if self.moving then | |
self.origin = self.origin + DeltaTime*self.velocity | |
end | |
local q = self.baseRotation * self:getGravity() | |
local s = self.fishEye * self.intScale | |
local o = self.origin | |
local e = self.eye | |
local up = self.up | |
e = s*e^q + o | |
up = up^q | |
self.looking = o - e | |
if self.moving then | |
self.velocity = self.velocity | |
+ DeltaTime * self.acceleration * (o-e) | |
- DeltaTime * self.friction * self.velocity:len() * self.velocity | |
end | |
camera(e.x,e.y,e.z,o.x,o.y,o.z,up.x,up.y,up.z) | |
local fe = math.atan(self.fishEye)*180/math.pi | |
if self.near then | |
perspective(fe,WIDTH/HEIGHT,self.near,self.far) | |
else | |
perspective(fe,WIDTH/HEIGHT) | |
end | |
self.matrix = viewMatrix() * projectionMatrix() | |
end | |
--[[ | |
If we are noticing gravity then this figures out the rotation defined | |
by our current gravity vector as a quatertion. | |
--]] | |
function View:getGravity() | |
if self.useGravity then | |
return self.currentGravity * self.orientRotation:Gravity()^"" | |
else | |
return self.currentGravity | |
end | |
end | |
--[[ | |
This applies the current internal transformation to the given vector; | |
this is before stereographic projection has occured. | |
--]] | |
function View:applyIntTransformation(v) | |
local q = self.baseRotation * self:getGravity() | |
local s = self.fishEye * self.intScale | |
local o = self.origin | |
return s * (v^q + o) | |
end | |
function View:applyIntDirTransformation(v) | |
local q = self.baseRotation * self:getGravity() | |
local s = self.fishEye * self.intScale | |
return s * v^q | |
end | |
function View:invertIntTransformation(v) | |
local q = self.baseRotation * self:getGravity() | |
local s = self.fishEye * self.intScale | |
local o = self.origin | |
q = q^"" | |
return (v / s - o)^q | |
end | |
function View:invertIntDirTransformation(v) | |
local q = self.baseRotation * self:getGravity() | |
local s = self.fishEye * self.intScale | |
q = q^"" | |
return v^q / s | |
end | |
--[[ | |
This applies the current external transformation to the given vector; | |
this is after stereographic projection has occured. | |
--]] | |
function View:applyExtTransformation(v) | |
return (self.extScale/self.fishEye) * (v + self.extTranslate) | |
end | |
function View:applyExtDirTransformation(v) | |
return (self.extScale/self.fishEye) * v | |
end | |
--[[ | |
This projects the vector onto the screen, taking into account the | |
current internal and external transformations. | |
--]] | |
function View:Project(v) | |
local u,w,r,l | |
-- u = self.intScale * ((v + self.intTranslate)^self.rotation) | |
u = self:applyIntTransformation(v) | |
w = u + Vec3.e1 | |
w = w:stereoProject(self.eye) - u:stereoProject(self.eye) | |
r = w:len() | |
l = u:stereoLevel(self.eye) | |
u = self:applyExtTransformation(u:stereoProject(self.eye)) | |
return {u,r,l,v:isInFront(self.eye)} | |
end | |
function View:ProjectDirection(v) | |
local u | |
u = self:applyIntDirTransformation(v) | |
return u | |
end | |
--[[ | |
This inverts the projection to the z-level of the second vector. | |
--]] | |
function View:invProject(v,w) | |
local u,q,h | |
u = v / self.extScale - self.extTranslate | |
q = self.rotation | |
w = self.intScale * (w^q + self.intTranslate) | |
u = Vec3.stereoInvProject(u,self.eye,w.z) | |
u = u / self.intScale - self.intTranslate | |
q = q^"" | |
u = u^q | |
return u | |
end | |
--[[ | |
We should be pretty far down the "touch" queue so we take anything we | |
can. | |
--]] | |
function View:isTouchedBy(touch) | |
return true | |
end | |
--[[ | |
Our possible touches and their actions are: | |
Single tap: freeze or unfreeze the gravitational effect. | |
Double tap: restore stuff to initial conditions. | |
Single move: rotate View, if it is short then we carry on spinning | |
for a bit. | |
Pinch: scale View, either internally or externally | |
Double swipe: translate internally | |
Triple swipe: translate externally | |
--]] | |
function View:processTouches(g) | |
if g.type.long and g.num == 1 then | |
self:singleLongTap(g.touchesArr[1]) | |
if g.type.ended then | |
g:reset() | |
end | |
elseif g.type.tap then | |
if g.type.finished then | |
if g.num == 1 then | |
self:singleShortTap() | |
elseif g.num == 2 then | |
self:doubleTap() | |
end | |
end | |
else | |
if g.numactives == 1 then | |
self:singleTouch(g.actives[1]) | |
if g.type.ended and g.type.short then | |
self:saveVelocity(g.actives[1]) | |
end | |
elseif g.numactives == 2 then | |
self:doubleTouch(g.actives[1],g.actives[2]) | |
elseif g.numactives == 3 then | |
if not g.type.ViewTriple then | |
g.type.ViewTripleType = self:isTriangle( | |
g.actives[1], | |
g.actives[2], | |
g.actives[3]) | |
g.type.ViewTriple = true | |
end | |
if g.type.ViewTripleType then | |
self:triplePinch( | |
g.actives[1], | |
g.actives[2], | |
g.actives[3]) | |
else | |
self:tripleSwipe( | |
g.actives[1], | |
g.actives[2], | |
g.actives[3]) | |
end | |
end | |
if g.type.ended then | |
g:reset() | |
end | |
end | |
g:noted() | |
if g.type.finished then | |
g:reset() | |
end | |
end | |
--[[ | |
Rotate View according to the movement, saving the velocity so that we | |
can carry on spinning for a bit if the total movement was short. | |
--]] | |
function View:singleTouch(thisTouch) | |
local r,p,v,lp,lv,ox,oy,u,qa,qg,touch | |
if not thisTouch.updated then | |
return | |
end | |
touch = thisTouch.touch | |
if touch.state == MOVING or touch.state == ENDED then | |
r =RectAnchorOf(Screen,"width")/2 | |
ox,oy = RectAnchorOf(Screen,"centre") | |
p = vec2(touch.prevX - ox,touch.prevY - oy)/r | |
v = vec2(touch.x - ox,touch.y - oy)/r | |
lv = v:lenSqr() | |
lp = p:lenSqr() | |
if lp > .9 or lv > .9 then | |
return | |
end | |
p = vec3(p.x,p.y,math.sqrt(1 - lp)) | |
v = vec3(v.x,v.y,math.sqrt(1 - lv)) | |
qa = v:rotateTo(p) | |
local b = SO3(self.eye,self.up) | |
qa = Quaternion(qa.q.x,-qa.q.y*b[3] + qa.q.z*b[2] + qa.q.w*b[1]) | |
qg = self:getGravity() | |
self.baseRotation = self.baseRotation * qg * qa * qg^"" | |
end | |
end | |
--[[ | |
Saves our velocity. | |
--]] | |
function View:saveVelocity(touch) | |
local ft,t,dt,r,p,ox,oy,v,lp,lv,qa,qg | |
ft = touch.firsttouch | |
t = touch.touch | |
dt = ElapsedTime - touch.createdat | |
r = RectAnchorOf(Screen,"width")/2 | |
ox,oy = RectAnchorOf(Screen,"centre") | |
p = vec2(ft.x - ox,ft.y - oy)/r | |
v = vec2(t.x - ox,t.y - oy)/r | |
v = p + (v-p)*2*DeltaTime/dt | |
lp = p:lenSqr() | |
lv = v:lenSqr() | |
if lp < .9 and lv < .9 then | |
-- raise both to sphere | |
p = vec3(p.x,p.y,math.sqrt(1 - lp)) | |
v = vec3(v.x,v.y,math.sqrt(1 - lv)) | |
qa = v:rotateTo(p) | |
local b = SO3(self.eye,self.up) | |
qa = Quaternion(qa.q.x,-qa.q.y*b[3] + qa.q.z*b[2] + qa.q.w*b[1]) | |
qg = self:getGravity() | |
qa = qg * qa * qg^"" | |
self.angVelocity = qa:make_slerp(Quaternion.unit()) | |
self.angvTime = ElapsedTime | |
touch.container.interrupt = self | |
end | |
end | |
--[[ | |
This uses the "interrupt" feature of the touch controller so that if | |
we are rotating then the next touch stops us. | |
--]] | |
function View:interruption(t) | |
if self.angVelocity then | |
self.angVelocity = nil | |
t.container.interrupt = nil | |
return true | |
else | |
return false | |
end | |
end | |
--[[ | |
General handling of double touches | |
--]] | |
function View:doubleTouch(ta,tb) | |
if not ta.updated and not tb.updated then | |
return | |
end | |
local sa,sb,ea,eb,o,n,u,v,A | |
ea = vec2(ta.touch.x,ta.touch.y) | |
eb = vec2(tb.touch.x,tb.touch.y) | |
if ta.updated then | |
sa = vec2(ta.touch.prevX,ta.touch.prevY) | |
else | |
sa = ea | |
end | |
if tb.updated then | |
sb = vec2(tb.touch.prevX,tb.touch.prevY) | |
else | |
sb = eb | |
end | |
local ed = eb - ea | |
local sd = sb - sa | |
local el = ed:len() | |
local sl = sd:len() | |
local s = 1 | |
local theta = 0 | |
if sl > 0.001 and el > 0.001 then | |
s = el/sl | |
theta = ed:angleBetween(sd) | |
end | |
o = (eb + ea)/2 | |
A = self.matrix | |
n = screennormal(o,A) | |
u = n:cross(self.up) | |
v = u:cross(n) | |
o = self.origin | |
sa = screentoplane(sa,o,u,v,A) | |
ea = screentoplane(ea,o,u,v,A) | |
sb = screentoplane(sb,o,u,v,A) | |
eb = screentoplane(eb,o,u,v,A) | |
ed = (eb + ea)/2 | |
sd = (sb + sa)/2 | |
local a = self.eye - ed | |
a = a:normalize() | |
local q = Quaternion.Rotation(theta,a) | |
self.intScale = self.intScale / s | |
local tr = ed - sd^q*s | |
local qg = self:getGravity() | |
local qa = qg * q * qg^"" | |
self.baseRotation = self.baseRotation * qa | |
self.origin = self.origin^qa - tr | |
end | |
--[[ | |
Triple touch handling - are the coordinates "triangular" or "linear" | |
--]] | |
function View:isTriangle(ta,tb,tc) | |
local a = vec2(ta.touch.x,ta.touch.y) | |
local b = vec2(tb.touch.x,tb.touch.y) | |
local c = vec2(tc.touch.x,tc.touch.y) | |
local phi = (b - a):angleBetween(c - a) | |
local psi = (a - b):angleBetween(c - b) | |
phi = phi - math.floor(phi/(2*math.pi))*2*math.pi | |
if phi > math.pi then | |
phi = 2*math.pi - phi | |
end | |
psi = psi - math.floor(psi/(2*math.pi))*2*math.pi | |
if psi > math.pi then | |
psi = 2*math.pi - psi | |
end | |
if phi > math.pi/2 | |
or psi > math.pi/2 | |
or phi + psi < math.pi/2 | |
then | |
return false | |
else | |
return true | |
end | |
end | |
--[[ | |
A triple swipe to an external translation. | |
--]] | |
function View:tripleSwipe(ta,tb,tc) | |
if not ta.updated and not tb.updated and not tc.updated then | |
return | |
end | |
local ea,eb,ec,sa,sb,sc,ed,sd | |
ea = vec2(ta.touch.x,ta.touch.y) | |
eb = vec2(tb.touch.x,tb.touch.y) | |
ec = vec2(tc.touch.x,tc.touch.y) | |
if ta.updated then | |
sa = vec2(ta.touch.prevX,ta.touch.prevY) | |
else | |
sa = ea | |
end | |
if tb.updated then | |
sb = vec2(tb.touch.prevX,tb.touch.prevY) | |
else | |
sb = eb | |
end | |
if tc.updated then | |
sc = vec2(tc.touch.prevX,tc.touch.prevY) | |
else | |
sc = ec | |
end | |
o = (ec + eb + ea)/2 | |
A = self.matrix | |
n = screennormal(o,A) | |
u = n:cross(self.up) | |
v = u:cross(n) | |
o = self.origin | |
sa = screentoplane(sa,o,u,v,A) | |
ea = screentoplane(ea,o,u,v,A) | |
sb = screentoplane(sb,o,u,v,A) | |
eb = screentoplane(eb,o,u,v,A) | |
sc = screentoplane(sc,o,u,v,A) | |
ec = screentoplane(ec,o,u,v,A) | |
ed = (ec + eb + ea)/2 | |
sd = (sc + sb + sa)/2 | |
self.origin = self.origin - ed + sd | |
end | |
--[[ | |
A triple pinch to alter the "fish eye" effect | |
--]] | |
function View:triplePinch(ta,tb,tc) | |
local a = vec2(ta.touch.x,ta.touch.y) | |
local b = vec2(tb.touch.x,tb.touch.y) | |
local c = vec2(tc.touch.x,tc.touch.y) | |
local pa,pb,pc | |
if ta.updated then | |
pa = vec2(ta.touch.prevX,ta.touch.prevY) | |
else | |
pa = a | |
end | |
if tb.updated then | |
pb = vec2(tb.touch.prevX,tb.touch.prevY) | |
else | |
pb = b | |
end | |
if tc.updated then | |
pc = vec2(tc.touch.prevX,tc.touch.prevY) | |
else | |
pc = c | |
end | |
local d = TriangleArea(a,b,c) | |
local pd = TriangleArea(pa,pb,pc) | |
self.fishEye = self.fishEye*(1+pd)/(1+d) | |
end | |
--[[ | |
A single tap toggles the use of gravity. | |
--]] | |
function View:singleShortTap() | |
self:gravityOnOff() | |
end | |
function View:singleLongTap(t) | |
if t.touch.state == ENDED then | |
self.moving = false | |
else | |
self.moving = true | |
end | |
end | |
--[[ | |
This is the actual toggle function. As well as toggling the use, it | |
saves the current rotation so that the effect is to freeze the object. | |
--]] | |
function View:gravityOnOff() | |
if self.useGravity then | |
self.currentGravity = self:getGravity() | |
else | |
self.currentGravity = self.currentGravity * self.orientRotation:Gravity() | |
end | |
self.useGravity = not self.useGravity | |
end | |
--[[ | |
This is the double tap function that calls the reset function. | |
--]] | |
function View:doubleTap() | |
self:restoreInitials() | |
end | |
return View | |
--]==] |
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
--[==[ | |
-- 2D Zoom | |
local Zoom = class() | |
function Zoom:init(t) | |
t:pushHandler(self) | |
self.ll = vec2(0,0) | |
self.size = vec2(1,1) | |
-- self.aspect = true | |
end | |
function Zoom:draw() | |
scale(self.size.x,self.size.y) | |
translate(self.ll.x,self.ll.y) | |
end | |
function Zoom:isTouchedBy(touch) | |
return true | |
end | |
function Zoom:processTouches(g) | |
if g.updated then | |
if g.numactives == 1 then | |
local dx = g.actives[1].touch.deltaX/self.size.x | |
local dy = g.actives[1].touch.deltaY/self.size.y | |
self.ll = self.ll + vec2(dx,dy) | |
elseif g.numactives == 2 then | |
local ta = g.actives[1] | |
local tb = g.actives[2] | |
local ea = vec2(ta.touch.x,ta.touch.y) | |
local eb = vec2(tb.touch.x,tb.touch.y) | |
local sa,sb | |
if ta.updated then | |
sa = vec2(ta.touch.prevX,ta.touch.prevY) | |
else | |
sa = ea | |
end | |
if tb.updated then | |
sb = vec2(tb.touch.prevX,tb.touch.prevY) | |
else | |
sb = eb | |
end | |
local sc = (sa + sb)/2 | |
sc.x = sc.x / self.size.x | |
sc.y = sc.y / self.size.y | |
local sl = (sb - sa):len() | |
local el = (eb - ea):len() | |
self.size = self.size * el / sl | |
local ec = (ea + eb)/2 | |
ec.x = ec.x / self.size.x | |
ec.y = ec.y / self.size.y | |
self.ll = self.ll + ec - sc | |
end | |
end | |
if g.type.finished then | |
g:reset() | |
else | |
g:noted() | |
end | |
end | |
return Zoom | |
--]==] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment