Skip to content

Instantly share code, notes, and snippets.

@loopspace loopspace/1aTabOrder
Created May 20, 2013

Embed
What would you like to do?
Library Utilities Release v2 -A library of utility classes and functions.
Library Utilities Tab Order Version: 2
------------------------------
This file should not be included in the Codea project.
#ChangeLog
#Main
#Lengths
#Boolean
#MathsUtilities
#MeshUtilities
#RoundedRectangle
#TouchUtilities
#Playlist
--[==[
-- Saving or reading a boolean value.
local Boolean = {}
function Boolean.readData(t,k,b)
local f
if t == "global" then
f = readGlobalData
elseif t == "project" then
f = readProjectData
else
f = readLocalData
end
local bol = f(k)
if bol then
if bol == 0 then
return false
else
return true
end
else
if b then
return true
else
return false
end
end
end
function Boolean.saveData(t,k,b)
local f
if t == "global" then
f = saveGlobalData
elseif t == "project" then
f = saveProjectData
else
f = saveLocalData
end
if b then
f(k,1)
else
f(k,0)
end
end
return Boolean
--]==]
--[[
ChangeLog
=========
v2.0 Split into separate projects: "Library Utilities" is for
useful things of a general nature.
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.
--]]
--[==[
function getLength(l)
local n = tonumber(l)
if n then
return n
end
local i,j,m,u = string.find(l,"^(%d*)(%D*)$")
if i then
if u == "px" or u == "pcx" then
return m
elseif u == "pt" then
return m * 3.653
elseif u == "in" then
return m * 264
elseif u == "cm" then
return m * 670.56
elseif u == "mm" then
return m * 6705.6
elseif u == "m" then
return m * 67.056
elseif u == "em" then
local t = fontMetrics()
return m * t.size
elseif u == "en" then
local t = fontMetrics()
return m * t.size/2
elseif u == "ex" then
local t = fontMetrics()
return m * t.xHeight
elseif u == "lh" then
local _,h = textSize("x")
return m * h
else
return nil
end
end
return nil
end
function evalLength(s)
if type(s) == "function" then
s = s()
end
s = string.gsub(s,"(%d+%a+)",
function(n) return getLength(n) or n end)
s = "local s = " .. s .. " return s"
local f = loadstring(s)
s = f()
return s
end
cmodule.gexport {
evalLength = evalLength
}
--]==]
VERSION = 2.0
clearProjectData()
-- DEBUG = true
-- Use this function to perform your initial setup
function setup()
if AutoGist then
autogist = AutoGist("Library Utilities","A library of utility classes and functions.",VERSION)
autogist:backup(true)
end
--displayMode(FULLSCREEN_NO_BUTTONS)
cmodule "Library CModule"
cimport "TestSuite"
local Touches = cimport "Touch"
local UI = cimport "UI"
local Debug = cimport "Debug"
local Playlist = cimport "Playlist"
local Colour = unpack(cimport "Colour",nil)
cimport "ColourNames"
cimport "PictureBrowser"
cimport "Menu"
cimport "Keypad"
cimport "Keyboard"
local TextNode = cimport "TextNode"
if cmodule.loaded("Menu") then
print("Menu loaded")
else
print("Menu not loaded")
end
local View = cimport "View"
local Quaternion = cimport "Quaternion"
local Explosion = cimport "Explosion"
--print(Quaternion(1,.56789,0,0))
touches = Touches()
ui = UI(touches)
debug = Debug({ui = ui})
ui:systemmenu()
ui:helpmenu()
ui:addMessage("This is a system message, hello everyone.")
playlist = Playlist({ ui = ui})
testsuite.initialise({ui = ui})
--[[
colfn = function(col)
print(col)
ui:getColour(col,colfn)
return true
end
colfn(Colour.svg.Black)
--]]
debug:log({
name = "Screen north west",
message = function() local x,y = RectAnchorOf(Screen,"north west") return x .. ", " .. y end
})
--debug:activate()
ui:setPictureList({directory = "Documents", camera = true, filter = function(n,w,h) return math.min(w,h) > 500 end})
--ui:getPicture(function(i) img = i return true end)
--print(USRotateCCW(vec2(1,0)))
ui:declareKeyboard({name = "ArialMT", type = "fullqwerty"})
ui:useKeyboard("fullqwerty",
function(k) print(k) return false end)
print("Textnode")
tn = TextNode({
pos = function() return WIDTH/2,800 end,
anchor = "centre",
--angle = 30,
ui = ui,
fit = true,
maxHeight = HEIGHT,
})
watch("tn.numlines")
watch("dbug")
touches:pushHandler(tn)
--
print(PORTRAIT)
print(PORTRAIT_UPSIDE_DOWN)
print(PORTRAIT_ANY)
print(LANDSCAPE_LEFT)
print(LANDSCAPE_RIGHT)
print(LANDSCAPE_ANY)
print(ANY)
--ui:setOrientation(PORTRAIT_UPSIDE_DOWN)
-- ui:supportedOrientations(PORTRAIT_UPSIDE_DOWN)
view = View(ui,touches)
parameter.watch("view.baseRotation")
parameter.watch("stuff[1]")
parameter.watch("stack")
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
perspective(40,WIDTH/HEIGHT)
print(projectionMatrix())
camera(0,0,15,0,0,0,0,1,0)
print(viewMatrix())
camera(0,0,-15,0,0,0,0,1,0)
print(viewMatrix())
--watch("dbg")
--ui:getCurve(vec4(0,0,3,-2),function(v) print(v) return true end)
--ui:getColour(function(c) print(c) return true end)
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)
print(Quaternion.Rotation(1,3,4,5))
parameter.watch("vm")
parameter.watch("qt")
qslp = Quaternion.unit():make_slerp(Quaternion(1,0,0,0))
qlp = Quaternion.unit():make_lerp(Quaternion(-1,0,0,0))
explosion = Explosion({
image = "Cargo Bot:Codea Icon",
trails = true,
centre = vec2(WIDTH/2,HEIGHT/2)
})
--explosion:activate(1,5)
--ui:addNotice({text = "Watch carefully"})
ui:getNumber(function(n) print(n) end)
end
-- This function gets called once every frame
function draw()
-- process touches and taps
touches:draw()
background(34, 47, 53, 255)
--testsuite.draw()
--playlist:draw()
--[[
fill(255, 255, 255, 255)
pushMatrix()
translate(WIDTH/2,HEIGHT/2)
rotate(45)
text("hello",0,0)
popMatrix()
--]]
if img then
local w,h = img.width,img.height
local asp = math.min(WIDTH/w,HEIGHT/h,1)
--sprite(img,WIDTH/2,HEIGHT/2,w*asp,h*asp)
end
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()
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
--[==[
--[[
Some mathematical utilities.
--]]
function Ordinal(n)
local k = n%10
local th = "th"
if k == 1 then
th = "st"
elseif k == 2 then
th = "nd"
elseif k == 3 then
th = "rd"
end
return n .. th
end
function Regression(t)
local n, xy, x, y, xx, yy = 0,0,0,0,0,0
for k,v in ipairs(t) do
n = n + 1
xy = xy + v.x*v.y
x = x + v.x
y = y + v.y
xx = xx + v.x*v.x
yy = yy + v.y*v.y
end
local d = n*xx - x*x
if d == 0 then
return false,false,Matrix({{0,0},{0,0}})
end
local matrix
if Matrix then
matrix = Matrix({{xx,x},{x,n}})
end
return (n*xy - x*y)/d,
(-x*xy + xx*y)/d,
matrix,
(n*xy -x*y)^2/((n*xx-x*x)*(n*yy-y*y))
end
function ApplyAffine(m,v)
if v then
return v.x*m[1] + v.y*m[2] + m[3]
else
applyMatrix(matrix(
m[1].x, m[1].y, 0, 0,
m[2].x, m[2].y, 0, 0,
0,0,1,0,
m[3].x, m[3].y, 0, 1
))
end
end
function USRotateCW(v)
return ApplyAffine({vec2(0,-1),vec2(1,0),vec2(0,1)},v)
end
function USRotateCCW(v)
return ApplyAffine({vec2(0,1),vec2(-1,0),vec2(1,0)},v)
end
function USReflectV(v)
return ApplyAffine({vec2(-1,0),vec2(0,1),vec2(1,0)},v)
end
function USReflectH(v)
return ApplyAffine({vec2(1,0),vec2(0,-1),vec2(0,1)},v)
end
local USCoordinates = {}
USCoordinates[PORTRAIT] = {vec2(1,0),vec2(0,1),vec2(0,0)}
USCoordinates[PORTRAIT_UPSIDE_DOWN] = {vec2(-1,0),vec2(0,-1),vec2(1,1)}
USCoordinates[LANDSCAPE_LEFT] = {vec2(0,-1),vec2(1,0),vec2(0,1)}
USCoordinates[LANDSCAPE_RIGHT] = {vec2(0,1),vec2(-1,0),vec2(1,0)}
function USOrientation(o,v)
return ApplyAffine(USCoordinates[o],v)
end
local InvUSCoordinates = {}
InvUSCoordinates[PORTRAIT] = {vec2(1,0),vec2(0,1),vec2(0,0)}
InvUSCoordinates[PORTRAIT_UPSIDE_DOWN] = {vec2(-1,0),vec2(0,-1),vec2(1,1)}
InvUSCoordinates[LANDSCAPE_LEFT] = {vec2(0,1),vec2(-1,0),vec2(1,0)}
InvUSCoordinates[LANDSCAPE_RIGHT] = {vec2(0,-1),vec2(1,0),vec2(0,1)}
function InvUSOrientation(o,v)
return ApplyAffine(InvUSCoordinates[o],v)
end
function TriangleArea(a,b,c)
return math.abs(
a:dot(b:rotate90())
+ b:dot(c:rotate90())
+ c:dot(a:rotate90())
)/2
end
function applymatrix4(v,m)
local u = {}
u[1] = m[1]*v[1] + m[5]*v[2] + m[09]*v[3] + m[13]*v[4]
u[2] = m[2]*v[1] + m[6]*v[2] + m[10]*v[3] + m[14]*v[4]
u[3] = m[3]*v[1] + m[7]*v[2] + m[11]*v[3] + m[15]*v[4]
u[4] = m[4]*v[1] + m[8]*v[2] + m[12]*v[3] + m[16]*v[4]
return u
end
function cofactor4(m)
local rm = matrix()
local sgn,l
local fm = {}
for k=1,16 do
fm = {}
l = math.floor((k-1)/4) + 1 + 4*((k-1)%4)
sgn = (-1)^(math.floor((k-1)/4))*(-1)^((k-1)%4)
for j=1,16 do
if j%4 ~= k%4
and math.floor((j-1)/4) ~= math.floor((k-1)/4)
then
table.insert(fm,m[j])
end
end
rm[l] = sgn*Det3(fm)
end
return rm
end
function Det3(t)
return t[1]*t[5]*t[9]
+ t[2]*t[6]*t[7]
+ t[3]*t[4]*t[8]
- t[3]*t[5]*t[7]
- t[2]*t[4]*t[9]
- t[1]*t[6]*t[8]
end
function applymatrix3(v,m)
local u = {}
u[1] = m[1]*v[1] + m[4]*v[2] + m[7]*v[3]
u[2] = m[2]*v[1] + m[5]*v[2] + m[8]*v[3]
u[3] = m[3]*v[1] + m[6]*v[2] + m[9]*v[3]
return u
end
function cofactor3(m)
local rm = {}
local sgn,l
local fm = {}
for k=1,9 do
fm = {}
l = math.floor((k-1)/3) + 1 + 3*((k-1)%3)
sgn = (-1)^(math.floor((k-1)/3))*(-1)^((k-1)%3)
for j=1,9 do
if j%3 ~= k%3
and math.floor((j-1)/3) ~= math.floor((k-1)/3)
then
table.insert(fm,m[j])
end
end
rm[l] = sgn*Det2(fm)
end
return rm
end
function Det2(t)
return t[1]*t[4] - t[2]*t[3]
end
function RandomVec3()
local th = 2*math.pi*math.random()
local z = 2*math.random() - 1
local r = math.sqrt(1 - z*z)
return vec3(r*math.cos(th),r*math.sin(th),z)
end
function RandomBasisR3()
local th = 2*math.pi*math.random()
local cth = math.cos(th)
local sth = math.sin(th)
local a = vec3(cth,sth,0)
local b = vec3(-sth,cth,0)
local c = vec3(0,0,1)
local v = RandomVec3()
a = a - 2*v:dot(a)*v
b = b - 2*v:dot(b)*v
c = c - 2*v:dot(c)*v
return {a,b,c}
end
function SO3(u,v)
if u == vec3(0,0,0) then
if v == vec3(0,0,0) then
return {vec3(1,0,0),vec3(0,1,0),vec3(0,0,1)}
end
u,v = v,u
end
if u:cross(v) == vec3(0,0,0) then
if u.x == 0 and u.y == 0 then
v = vec3(1,0,0)
else
v = vec3(u.y,-u.x,0)
end
end
local t = GramSchmidt({u,v})
t[3] = t[1]:cross(t[2])
return t
end
function GramSchmidt(t)
local o = {}
local w
for k,v in ipairs(t) do
w = v
for l,u in ipairs(o) do
w = w - w:dot(u)*u
end
if w ~= vec3(0,0,0) then
w = w:normalize()
table.insert(o,w)
end
end
return o
end
cmodule.gexport {
Ordinal = Ordinal,
Regression = Regression,
ApplyAffine = ApplyAffine,
USRotateCW = USRotateCW,
USRotateCCW = USRotateCCW,
USReflectV = USReflectV,
USReflectH = USReflectH,
USOrientation = USOrientation,
InvUSOrientation = InvUSOrientation,
TriangleArea = TriangleArea,
applymatrix4 = applymatrix4,
cofactor4 = cofactor4,
Det3 = Det3,
applymatrix3 = applymatrix3,
cofactor3 = cofactor3,
Det2 = Det2,
RandomVec3 = RandomVec3,
RandomBasisR3 = RandomBasisR3,
SO3 = SO3,
GramSchmidt = GramSchmidt
}
--]==]
--[==[
-- Mesh Utilities
--[[
Utility functions for meshes. These all add shapes to a mesh, sort of.
The older routines modified tables for vertices and colours whereas the newer ones modified the mesh directly. They should be standardised.
--]]
--[[
Vertex ordering:
1 -- 2
| |
3 -- 4
--]]
function addQuad(t)
local m = t.mesh
local n = t.position or 0
if n > m.size - 12 then
m:resize(n + 300)
end
local v = t.vertices
for k,l in ipairs({1,2,3,2,3,4}) do
m:vertex(n+k,v[l][1])
if v[l][2] then
m:color(n+k,v[l][2])
end
if v[l][3] then
m:texCoord(n+k,v[l][3])
end
end
return n + 6
end
function addTriangle(t)
local m = t.mesh
local n = t.position or 0
if n > m.size - 12 then
m:resize(n + 300)
end
local v = t.vertices
for k,l in ipairs({1,2,3}) do
m:vertex(n+k,v[l][1])
m:color(n+k,v[l][2])
end
return n + 3
end
--[[
Adds a rounded rectangle to an existing mesh
--]]
function addRoundedRect(t)
local m = t.mesh
local x = t.x
local y = t.y
local w = t.width or 0
local h = t.height or 0
local s = t.radius or 10
local c = t.corners or 0
local a = t.anchor
local fc = t.colour or fill()
if a then
x,y = RectAnchorAt(x,y,w,h,a)
end
local v = {}
local nv = 0
local ce = vec2(x + w/2,y + h/2)
local n = 4
local o,dx,dy
for j = 1,4 do
dx = -1 + 2*(j%2)
dy = -1 + 2*(math.floor(j/2)%2)
o = ce + vec2(dx * (w/2 - s), dy * (h/2 - s))
if math.floor(c/2^(j-1))%2 == 0 then
for i = 1,n do
table.insert(v,o)
table.insert(v,o + vec2(dx * s * math.cos((i-1) * math.pi/(2*n)), dy * s * math.sin((i-1) * math.pi/(2*n))))
table.insert(v,o + vec2(dx * s * math.cos(i * math.pi/(2*n)), dy * s * math.sin(i * math.pi/(2*n))))
end
nv = nv + 3*n
else
table.insert(v,o)
table.insert(v,o + vec2(dx * s,0))
table.insert(v,o + vec2(dx * s,dy * s))
table.insert(v,o)
table.insert(v,o + vec2(0,dy * s))
table.insert(v,o + vec2(dx * s,dy * s))
nv = nv + 6
end
end
local nrv = m.size
m:resize(nrv + nv)
for k,ve in ipairs(v) do
m:vertex(nrv + k, ve)
m:color(nrv+k,fc)
end
local ri = m:addRect(ce.x,ce.y,w,h-2*s)
m:setRectColor(ri,fc)
ri = m:addRect(ce.x,ce.y + (h-s)/2,w-2*s,s)
m:setRectColor(ri,fc)
ri = m:addRect(ce.x,ce.y - (h-s)/2,w-2*s,s)
m:setRectColor(ri,fc)
end
function AddPlank(t)
local w = t.width
local h = t.height
local d = t.depth
local o = t.origin
local cube = {}
local j,k,l
for i=0,7 do
j = 2*(i%2)-1
k = 2*(math.floor(i/2)%2)-1
l = 2*(math.floor(i/4)%2)-1
table.insert(cube, o + j*w + k*h + l* d)
end
return AddCube({
vertices = t.vertices,
colours = t.colours,
light = t.light,
cube = cube,
colour = t.colour or Colour.x11.Burlywood3
})
end
function AddSlab(t)
local sc = t.startCentre
local sh = t.startHeight
local sw = t.startWidth
local ec = t.endCentre
local eh = t.endHeight
local ew = t.endWidth
local cube = {}
local j,k
for i=0,3 do
j = 2*(i%2)-1
k = 2*(math.floor(i/2)%2)-1
table.insert(cube,sc + j*sh + k*sw)
table.insert(cube,ec + j*eh + k*ew)
end
return AddCube({
vertices = t.vertices,
colours = t.colours,
light = t.light,
cube = cube,
colour = t.colour or Colour.x11.Burlywood3
})
end
function AddHalfTube(t)
local r = t.radius
local b = {
{t.startCentre,t.startWidth,t.startHeight},
{t.endCentre,t.endWidth,t.endHeight}
}
local vertices = t.vertices
local colours = t.colours
local c = t.colour or Colour.svg.DarkSlateBlue
local l = t.light:normalize()
local n = t.number or 36
local step = math.pi/n
local ang,pang,lc,ver
for i = 1,n do
ang = i*step
pang = (i-1)*step
for k,v in ipairs({
{1,pang},
{1,ang},
{2,ang},
{2,ang},
{1,pang},
{2,pang}
}) do
ver = math.cos(v[2]) * b[v[1]][2] + math.sin(v[2]) * b[v[1]][3]
table.insert(vertices,b[v[1]][1] + ver)
lc = l:dot(ver)
table.insert(colours,Colour.shade(c,50 + 25*lc))
end
end
return vertices, colours
end
function AddTube(t)
local r = t.radius
local b = {
{t.startCentre,t.startWidth,t.startHeight,t.startTexture},
{t.endCentre,t.endWidth,t.endHeight,t.endTexture}
}
local vertices = t.vertices
local colours = t.colours
local texcoords = t.texCoords
local normals = t.normals
local c = t.colour or Colour.svg.DarkSlateBlue
local l = t.light
if l then
l = l:normalize()
end
local n = t.number or 36
local step = 2*math.pi/n
local ang,pang,lc,ver
for i = 1,n do
ang = i*step
pang = (i-1)*step
for k,v in ipairs({
{1,pang},
{1,ang},
{2,ang},
{2,ang},
{1,pang},
{2,pang}
}) do
ver = math.cos(v[2]) * b[v[1]][2] + math.sin(v[2]) * b[v[1]][3]
table.insert(vertices,b[v[1]][1] + ver)
if l then
lc = l:dot(ver)
table.insert(colours,Colour.shade(c,50 + 25*lc))
else
table.insert(colours,c)
end
if texcoords then
table.insert(texcoords,
vec2(b[v[1]][4],v[2]/(2*math.pi)))
end
if normals then
table.insert(normals,
ver:normalize())
end
end
end
return vertices, colours, texcoords, normals
end
-- cube faces are in binary order: 000, 001, 010, 011 etc
local CubeFaces = {
{1,2,3,4},
{5,7,6,8},
{1,5,2,6},
{3,4,7,8},
{2,6,4,8},
{1,3,5,7}
}
function AddCube(t)
local cube = t.cube
local vertices = t.vertices or {}
local colours = t.colours or {}
local normals = t.normals or {}
local c = t.colour or Colour.x11.Burlywood3
local l = t.light:normalize()
local faces = t.faces or CubeFaces
local lc,n
for k,v in ipairs(faces) do
n = (cube[v[3]] - cube[v[1]]):cross(cube[v[2]] - cube[v[1]])
if n ~= vec3(0,0,0) then
n = n:normalize()
lc = n:dot(l)
end
for i,u in ipairs({1,2,3,2,3,4}) do
table.insert(vertices,cube[v[u]])
table.insert(normals,n)
table.insert(colours,
Colour.shade(c,75 + 25*lc)
)
end
end
return vertices,colours,normals
end
function AddJewel(t)
local o = t.origin
local a = SO3(t.axis,vec3(0,0,1))
local n = t.sides
local la = t.axis:len()
for i = 1,3 do
a[i] = la*a[i]
end
local vertices = t.vertices or {}
local colours = t.colours or {}
local clr = t.colour or Colour.svg.IndianRed
local l = t.light:normalize()
local th = math.pi/n
local cs = math.cos(th)
local sn = math.sin(th)
local h = (1 - cs)/(1 + cs)
local nh = 1/(1+h)
local nhl = 1/math.sqrt(1 + nh^2)
local k,b,c,d,nml,cl,nv
b = a[2]
c = cs*a[2] + sn*a[3]
d = -sn*a[2] + cs*a[3]
nv = 0
for i = 1,2*n do
k = 2*(i%2) - 1
for j = -1,1,2 do
table.insert(vertices,o + j*a[1])
table.insert(vertices,o + h*k*a[1] + b)
table.insert(vertices,o - h*k*a[1] + c)
nml = j*nh*a[1] + .5*(1 - j*k)*b + .5*(1 + j*k)*c
cl = nml:dot(l)/nhl
for m=1,3 do
table.insert(colours,Colour.shade(clr,75 + 25*cl))
end
nv = nv + 3
end
b = c
c = cs*c + sn*d
d = -sn*b + cs*d
end
return vertices,colours,nv
end
function AddStar(t)
local o = t.origin
local s = t.size
local l = t.light:normalize()/(math.sqrt(3))
local vertices = t.vertices or {}
local colours = t.colours or {}
local b = RandomBasisR3()
local v,n,c
local bb = {}
for i=0,7 do
bb[1] = 2*(i%2)-1
bb[2] = 2*(math.floor(i/2)%2)-1
bb[3] = 2*(math.floor(i/4)%2)-1
v = bb[1]*b[1] + bb[2]*b[2] + bb[3]*b[3]
n = math.abs(v:dot(l))
c = Colour.shade(Colour.x11["LemonChiffon" .. math.random(1,4)], 70+n*30)
for m=1,3 do
table.insert(colours,c)
table.insert(vertices,o+s*(v - 2*bb[m]*b[m]))
end
end
return vertices,colours
end
cmodule.gexport {
addQuad = addQuad,
addTriangle = addTriangle,
addRoundedRect = addRoundedRect,
AddPlank = AddPlank,
AddSlab = AddSlab,
AddTube = AddTube,
AddHalfTube = AddHalfTube,
AddCube = AddCube,
AddJewel = AddJewel,
AddStar = AddStar
}
--]==]
--[==[
-- Playlist
local Playlist = class()
cimport "Menu"
function Playlist:init(t)
t = t or {}
self.startat = t.startTime or 0
if t.skipTo then
self.skip = t.skipTo
self.startat = self.startat - t.skipTo
end
self.current = t.startTime or 0
self.lastaction = t.lastAction
self.pauseaction = t.pauseAction
self.resumeaction = t.resumeAction
self.events = {}
local attach = true
if t.standalone then
attach = false
end
local mopts = t.menuOpts or {}
local title = t.title or "Playlist"
local m = t.ui:addMenu({title = title, attach = attach, menuOpts = mopts})
m:addItem({title = "Start",
action = function()
self:start()
return true
end,
highlight = function()
return self.running
end
})
m:addItem({title = "Stop",
action = function()
self:stop()
return true
end
})
m:addItem({title = "Pause",
action = function()
self:pause()
return true
end,
highlight = function()
return self.paused
end
})
m:addItem({title = "Resume",
action = function()
self:resume()
return true
end
})
end
function Playlist:addEvent(t)
t = t or {}
local ti = t.time or 0
if t.relative then
ti = ti + self.current
end
if t.duration then
self.current = ti + t.duration
table.insert(self.events,{ti,t.event})
elseif t.step and t.number then
for k = 1,t.number do
table.insert(self.events,{ti,t.event})
ti = ti + t.step
end
self.current = ti
end
end
function Playlist:wait(t)
if t then
self.current = self.current + t
end
end
function Playlist:draw()
if self.active then
for k,e in ipairs(self.events) do
if self.skip and e[1] < self.skip then
table.remove(self.events,k)
else
if ElapsedTime - self.startat > e[1] and e[2]() then
table.remove(self.events,k)
end
end
end
end
end
function Playlist:start()
self.active = true
self.running = true
self.startat = ElapsedTime
if self.skip then
self.startat = self.startat - self.skip
end
end
function Playlist:pause()
self.active = false
self.paused = true
self.pausedat = ElapsedTime
if self.pauseaction then
self.pauseaction()
end
end
function Playlist:resume()
self.active = true
self.paused = false
if self.pausedat then
self.startat = self.startat + ElapsedTime - self.pausedat
end
if self.resumeaction then
self.resumeaction()
end
end
function Playlist:stop()
self.active = false
self.running = false
if self.lastaction then
self.lastaction()
end
end
return Playlist
--]==]
--[==[
-- Rounded Rectangles
--[[
This is an auxilliary function for drawing a rectangle with possibly
curved cornders. The first four parameters specify the rectangle.
The fifth is the radius of the corner rounding. The optional sixth is
a way for specifying which corners should be rounded by passing a
number between 0 and 15. The first bit corresponds to the lower-left
corner and it procedes clockwise from there.
--]]
local __RRects = {}
function RoundedRectangle(x,y,w,h,s,c,a)
c = c or 0
w = w or 0
h = h or 0
if w < 0 then
x = x + w
w = -w
end
if h < 0 then
y = y + h
h = -h
end
w = math.max(w,2*s)
h = math.max(h,2*s)
a = a or 0
pushMatrix()
translate(x,y)
rotate(a)
local label = table.concat({w,h,s,c},",")
if __RRects[label] then
__RRects[label]:setColors(fill())
__RRects[label]:draw()
else
local rr = mesh()
local v = {}
local ce = vec2(w/2,h/2)
local n = 4
local o,dx,dy
for j = 1,4 do
dx = -1 + 2*(j%2)
dy = -1 + 2*(math.floor(j/2)%2)
o = ce + vec2(dx * (w/2 - s), dy * (h/2 - s))
if math.floor(c/2^(j-1))%2 == 0 then
for i = 1,n do
table.insert(v,o)
table.insert(v,o + vec2(dx * s * math.cos((i-1) * math.pi/(2*n)), dy * s * math.sin((i-1) * math.pi/(2*n))))
table.insert(v,o + vec2(dx * s * math.cos(i * math.pi/(2*n)), dy * s * math.sin(i * math.pi/(2*n))))
end
else
table.insert(v,o)
table.insert(v,o + vec2(dx * s,0))
table.insert(v,o + vec2(dx * s,dy * s))
table.insert(v,o)
table.insert(v,o + vec2(0,dy * s))
table.insert(v,o + vec2(dx * s,dy * s))
end
end
rr.vertices = v
rr:addRect(ce.x,ce.y,w,h-2*s)
rr:addRect(ce.x,ce.y + (h-s)/2,w-2*s,s)
rr:addRect(ce.x,ce.y - (h-s)/2,w-2*s,s)
rr:setColors(fill())
rr:draw()
__RRects[label] = rr
end
popMatrix()
end
cmodule.gexport {
RoundedRectangle = RoundedRectangle
}
--]==]
--[==[
--[[
Functions for converting a touch to amd from the screen.
--]]
function converttouch(z,t,A)
A = A or modelMatrix() * viewMatrix() * projectionMatrix()
t = t or CurrentTouch or vec2(0,0)
z = z or 0
local m = cofactor4(A)
local ndc = {}
local a
ndc[1] = (t.x/WIDTH - .5)*2
ndc[2] = (t.y/HEIGHT - .5)*2
ndc[3] = z
ndc[4] = 1
a = applymatrix4(ndc,m)
if (a[4] == 0) then return end
a = vec3(a[1], a[2], a[3])/a[4]
return a
end
function getzlevel(v,A)
A = A or modelMatrix() * viewMatrix() * projectionMatrix()
v = v or vec3(0,0,0)
local u = applymatrix4(vec4(v.x,v.y,v.z,1),A)
if u[4] == 0 then return end
return u[3]/u[4]
end
function __planetoscreen(o,u,v,A)
A = A or modelMatrix() * viewMatrix() * projectionMatrix()
o = o or vec3(0,0,0)
u = u or vec3(1,0,0)
v = v or vec3(0,1,0)
-- promote to 4-vectors
o = vec4(o.x,o.y,o.z,1)
u = vec4(u.x,u.y,u.z,0)
v = vec4(v.x,v.y,v.z,0)
local oA, uA, vA
oA = applymatrix4(o,A)
uA = applymatrix4(u,A)
vA = applymatrix4(v,A)
return { uA[1], uA[2], uA[4],
vA[1], vA[2], vA[4],
oA[1], oA[2], oA[4]}
end
function screentoplane(t,o,u,v,A)
A = A or modelMatrix() * viewMatrix() * projectionMatrix()
o = o or vec3(0,0,0)
u = u or vec3(1,0,0)
v = v or vec3(0,1,0)
t = t or CurrentTouch
local m = __planetoscreen(o,u,v,A)
m = cofactor3(m)
local ndc = {}
local a
ndc[1] = (t.x/WIDTH - .5)*2
ndc[2] = (t.y/HEIGHT - .5)*2
ndc[3] = 1
a = applymatrix3(ndc,m)
if (a[3] == 0) then return end
a = vec2(a[1], a[2])/a[3]
return o + a.x*u + a.y*v
end
function screennormal(t,A)
A = A or modelMatrix() * viewMatrix() * projectionMatrix()
t = t or CurrentTouch
local u,v,w,x,y
u = vec3(A[1],A[5],A[9])
v = vec3(A[2],A[6],A[10])
w = vec3(A[4],A[8],A[12])
x = (t.x/WIDTH - .5)*2
y = (t.y/HEIGHT - .5)*2
u = u - x*w
v = v - y*w
return u:cross(v)
end
cmodule.gexport {
screennormal = screennormal,
screentoplane = screentoplane,
converttouch = converttouch,
getzlevel = getzlevel
}
--]==]
--[==[
-- Utilities
--[[
This is for little bits of code that are needed by more than one class but
don't really belong to any one thing.
--]]
--[[
This is an auxilliary function for drawing a rectangle with possibly
curved cornders. The first four parameters specify the rectangle.
The fifth is the radius of the corner rounding. The optional sixth is
a way for specifying which corners should be rounded by passing a
number between 0 and 15. The first bit corresponds to the lower-left
corner and it procedes clockwise from there.
--]]
local __RRects = {}
function RoundedRectangle(x,y,w,h,s,c,a)
c = c or 0
w = w or 0
h = h or 0
if w < 0 then
x = x + w
w = -w
end
if h < 0 then
y = y + h
h = -h
end
w = math.max(w,2*s)
h = math.max(h,2*s)
a = a or 0
pushMatrix()
translate(x,y)
rotate(a)
local label = table.concat({w,h,s,c},",")
if __RRects[label] then
__RRects[label]:setColors(fill())
__RRects[label]:draw()
else
local rr = mesh()
local v = {}
local ce = vec2(w/2,h/2)
local n = 4
local o,dx,dy
for j = 1,4 do
dx = -1 + 2*(j%2)
dy = -1 + 2*(math.floor(j/2)%2)
o = ce + vec2(dx * (w/2 - s), dy * (h/2 - s))
if math.floor(c/2^(j-1))%2 == 0 then
for i = 1,n do
table.insert(v,o)
table.insert(v,o + vec2(dx * s * math.cos((i-1) * math.pi/(2*n)), dy * s * math.sin((i-1) * math.pi/(2*n))))
table.insert(v,o + vec2(dx * s * math.cos(i * math.pi/(2*n)), dy * s * math.sin(i * math.pi/(2*n))))
end
else
table.insert(v,o)
table.insert(v,o + vec2(dx * s,0))
table.insert(v,o + vec2(dx * s,dy * s))
table.insert(v,o)
table.insert(v,o + vec2(0,dy * s))
table.insert(v,o + vec2(dx * s,dy * s))
end
end
rr.vertices = v
rr:addRect(ce.x,ce.y,w,h-2*s)
rr:addRect(ce.x,ce.y + (h-s)/2,w-2*s,s)
rr:addRect(ce.x,ce.y - (h-s)/2,w-2*s,s)
rr:setColors(fill())
rr:draw()
__RRects[label] = rr
end
popMatrix()
end
function OldRoundedRectangle(x,y,w,h,r,c)
if not c then
c = 0
end
if w < 0 then
x = x + w
w = -w
end
if h < 0 then
y = y + h
h = -h
end
pushStyle()
strokeWidth(-1)
ellipseMode(RADIUS)
rectMode(RADIUS)
CircleOrSquare(x+r,y+r,r,c%2)
CircleOrSquare(x+r,y+h-r,r,math.floor(c/2)%2)
CircleOrSquare(x+w-r,y+h-r,r,math.floor(c/4)%2)
CircleOrSquare(x+w-r,y+r,r,math.floor(c/8)%2)
rectMode(CORNER)
rect(x+r,y,w - 2 * r,h)
rect(x,y + r,w,h - 2 * r)
popStyle()
end
--[[
This is for rounding or not rounding the corners.
--]]
function CircleOrSquare(x,y,r,c)
if c == 0 then
ellipse(x,y,r)
else
rect(x,y,r,r)
end
end
function Ordinal(n)
local k = n%10
local th = "th"
if k == 1 then
th = "st"
elseif k == 2 then
th = "nd"
elseif k == 3 then
th = "rd"
end
return n .. th
end
function Regression(t)
local n, xy, x, y, xx, yy = 0,0,0,0,0,0
for k,v in ipairs(t) do
n = n + 1
xy = xy + v.x*v.y
x = x + v.x
y = y + v.y
xx = xx + v.x*v.x
yy = yy + v.y*v.y
end
local d = n*xx - x*x
if d == 0 then
return false,false,Matrix({{0,0},{0,0}})
end
local matrix
if Matrix then
matrix = Matrix({{xx,x},{x,n}})
end
return (n*xy - x*y)/d,
(-x*xy + xx*y)/d,
matrix,
(n*xy -x*y)^2/((n*xx-x*x)*(n*yy-y*y))
end
--[[
Vertex ordering:
1 -- 2
| |
3 -- 4
--]]
function addQuad(t)
local m = t.mesh
local n = t.position or 0
if n > m.size - 12 then
m:resize(n + 300)
end
local v = t.vertices
for k,l in ipairs({1,2,3,2,3,4}) do
m:vertex(n+k,v[l][1])
if v[l][2] then
m:color(n+k,v[l][2])
end
if v[l][3] then
m:texCoord(n+k,v[l][3])
end
end
return n + 6
end
function addTriangle(t)
local m = t.mesh
local n = t.position or 0
if n > m.size - 12 then
m:resize(n + 300)
end
local v = t.vertices
for k,l in ipairs({1,2,3}) do
m:vertex(n+k,v[l][1])
m:color(n+k,v[l][2])
end
return n + 3
end
--[[
Adds a rounded rectangle to an existing mesh
--]]
function addRoundedRect(t)
local m = t.mesh
local x = t.x
local y = t.y
local w = t.width or 0
local h = t.height or 0
local s = t.radius or 10
local c = t.corners or 0
local a = t.anchor
local fc = t.colour or fill()
if a then
x,y = RectAnchorAt(x,y,w,h,a)
end
local v = {}
local nv = 0
local ce = vec2(x + w/2,y + h/2)
local n = 4
local o,dx,dy
for j = 1,4 do
dx = -1 + 2*(j%2)
dy = -1 + 2*(math.floor(j/2)%2)
o = ce + vec2(dx * (w/2 - s), dy * (h/2 - s))
if math.floor(c/2^(j-1))%2 == 0 then
for i = 1,n do
table.insert(v,o)
table.insert(v,o + vec2(dx * s * math.cos((i-1) * math.pi/(2*n)), dy * s * math.sin((i-1) * math.pi/(2*n))))
table.insert(v,o + vec2(dx * s * math.cos(i * math.pi/(2*n)), dy * s * math.sin(i * math.pi/(2*n))))
end
nv = nv + 3*n
else
table.insert(v,o)
table.insert(v,o + vec2(dx * s,0))
table.insert(v,o + vec2(dx * s,dy * s))
table.insert(v,o)
table.insert(v,o + vec2(0,dy * s))
table.insert(v,o + vec2(dx * s,dy * s))
nv = nv + 6
end
end
local nrv = m.size
m:resize(nrv + nv)
for k,ve in ipairs(v) do
m:vertex(nrv + k, ve)
m:color(nrv+k,fc)
end
local ri = m:addRect(ce.x,ce.y,w,h-2*s)
m:setRectColor(ri,fc)
ri = m:addRect(ce.x,ce.y + (h-s)/2,w-2*s,s)
m:setRectColor(ri,fc)
ri = m:addRect(ce.x,ce.y - (h-s)/2,w-2*s,s)
m:setRectColor(ri,fc)
end
function ApplyAffine(m,v)
if v then
return v.x*m[1] + v.y*m[2] + m[3]
else
applyMatrix(matrix(
m[1].x, m[1].y, 0, 0,
m[2].x, m[2].y, 0, 0,
0,0,1,0,
m[3].x, m[3].y, 0, 1
))
end
end
function USRotateCW(v)
return ApplyAffine({vec2(0,-1),vec2(1,0),vec2(0,1)},v)
end
function USRotateCCW(v)
return ApplyAffine({vec2(0,1),vec2(-1,0),vec2(1,0)},v)
end
function USReflectV(v)
return ApplyAffine({vec2(-1,0),vec2(0,1),vec2(1,0)},v)
end
function USReflectH(v)
return ApplyAffine({vec2(1,0),vec2(0,-1),vec2(0,1)},v)
end
USCoordinates = {}
USCoordinates[PORTRAIT] = {vec2(1,0),vec2(0,1),vec2(0,0)}
USCoordinates[PORTRAIT_UPSIDE_DOWN] = {vec2(-1,0),vec2(0,-1),vec2(1,1)}
USCoordinates[LANDSCAPE_LEFT] = {vec2(0,-1),vec2(1,0),vec2(0,1)}
USCoordinates[LANDSCAPE_RIGHT] = {vec2(0,1),vec2(-1,0),vec2(1,0)}
function USOrientation(o,v)
return ApplyAffine(USCoordinates[o],v)
end
function getLength(l)
local n = tonumber(l)
if n then
return n
end
local i,j,m,u = string.find(l,"^(%d*)(%D*)$")
if i then
if u == "px" or u == "pcx" then
return m
elseif u == "pt" then
return m * 3.653
elseif u == "in" then
return m * 264
elseif u == "cm" then
return m * 670.56
elseif u == "mm" then
return m * 6705.6
elseif u == "m" then
return m * 67.056
elseif u == "em" then
local t = fontMetrics()
return m * t.size
elseif u == "en" then
local t = fontMetrics()
return m * t.size/2
elseif u == "ex" then
local t = fontMetrics()
return m * t.xHeight
elseif u == "lh" then
local _,h = textSize("x")
return m * h
else
return nil
end
end
return nil
end
function evalLength(s)
if type(s) == "function" then
s = s()
end
s = string.gsub(s,"(%d+%a+)",
function(n) return getLength(n) or n end)
s = "local s = " .. s .. " return s"
local f = loadstring(s)
s = f()
return s
end
function TriangleArea(a,b,c)
return math.abs(
a:dot(b:rotate90())
+ b:dot(c:rotate90())
+ c:dot(a:rotate90())
)/2
end
function converttouch(z,t,A)
A = A or modelMatrix() * viewMatrix() * projectionMatrix()
t = t or CurrentTouch or vec2(0,0)
z = z or 0
local m = cofactor4(A)
local ndc = {}
local a
ndc[1] = (t.x/WIDTH - .5)*2
ndc[2] = (t.y/HEIGHT - .5)*2
ndc[3] = z
ndc[4] = 1
a = applymatrix4(ndc,m)
if (a[4] == 0) then return end
a = vec3(a[1], a[2], a[3])/a[4]
return a
end
function getzlevel(v,A)
A = A or modelMatrix() * viewMatrix() * projectionMatrix()
v = v or vec3(0,0,0)
local u = applymatrix4(vec4(v.x,v.y,v.z,1),A)
if u[4] == 0 then return end
return u[3]/u[4]
end
function applymatrix4(v,m)
local u = {}
u[1] = m[1]*v[1] + m[5]*v[2] + m[09]*v[3] + m[13]*v[4]
u[2] = m[2]*v[1] + m[6]*v[2] + m[10]*v[3] + m[14]*v[4]
u[3] = m[3]*v[1] + m[7]*v[2] + m[11]*v[3] + m[15]*v[4]
u[4] = m[4]*v[1] + m[8]*v[2] + m[12]*v[3] + m[16]*v[4]
return u
end
function cofactor4(m)
local rm = matrix()
local sgn,l
local fm = {}
for k=1,16 do
fm = {}
l = math.floor((k-1)/4) + 1 + 4*((k-1)%4)
sgn = (-1)^(math.floor((k-1)/4))*(-1)^((k-1)%4)
for j=1,16 do
if j%4 ~= k%4
and math.floor((j-1)/4) ~= math.floor((k-1)/4)
then
table.insert(fm,m[j])
end
end
rm[l] = sgn*Det3(fm)
end
return rm
end
function Det3(t)
return t[1]*t[5]*t[9]
+ t[2]*t[6]*t[7]
+ t[3]*t[4]*t[8]
- t[3]*t[5]*t[7]
- t[2]*t[4]*t[9]
- t[1]*t[6]*t[8]
end
function applymatrix3(v,m)
local u = {}
u[1] = m[1]*v[1] + m[4]*v[2] + m[7]*v[3]
u[2] = m[2]*v[1] + m[5]*v[2] + m[8]*v[3]
u[3] = m[3]*v[1] + m[6]*v[2] + m[9]*v[3]
return u
end
function cofactor3(m)
local rm = {}
local sgn,l
local fm = {}
for k=1,9 do
fm = {}
l = math.floor((k-1)/3) + 1 + 3*((k-1)%3)
sgn = (-1)^(math.floor((k-1)/3))*(-1)^((k-1)%3)
for j=1,9 do
if j%3 ~= k%3
and math.floor((j-1)/3) ~= math.floor((k-1)/3)
then
table.insert(fm,m[j])
end
end
rm[l] = sgn*Det2(fm)
end
return rm
end
function Det2(t)
return t[1]*t[4] - t[2]*t[3]
end
function __planetoscreen(o,u,v,A)
A = A or modelMatrix() * viewMatrix() * projectionMatrix()
o = o or vec3(0,0,0)
u = u or vec3(1,0,0)
v = v or vec3(0,1,0)
-- promote to 4-vectors
o = vec4(o.x,o.y,o.z,1)
u = vec4(u.x,u.y,u.z,0)
v = vec4(v.x,v.y,v.z,0)
local oA, uA, vA
oA = applymatrix4(o,A)
uA = applymatrix4(u,A)
vA = applymatrix4(v,A)
return { uA[1], uA[2], uA[4],
vA[1], vA[2], vA[4],
oA[1], oA[2], oA[4]}
end
function screentoplane(t,o,u,v,A)
A = A or modelMatrix() * viewMatrix() * projectionMatrix()
o = o or vec3(0,0,0)
u = u or vec3(1,0,0)
v = v or vec3(0,1,0)
t = t or CurrentTouch
local m = __planetoscreen(o,u,v,A)
m = cofactor3(m)
local ndc = {}
local a
ndc[1] = (t.x/WIDTH - .5)*2
ndc[2] = (t.y/HEIGHT - .5)*2
ndc[3] = 1
a = applymatrix3(ndc,m)
if (a[3] == 0) then return end
a = vec2(a[1], a[2])/a[3]
return o + a.x*u + a.y*v
end
function screennormal(t,A)
A = A or modelMatrix() * viewMatrix() * projectionMatrix()
t = t or CurrentTouch
local u,v,w,x,y
u = vec3(A[1],A[5],A[9])
v = vec3(A[2],A[6],A[10])
w = vec3(A[4],A[8],A[12])
x = (t.x/WIDTH - .5)*2
y = (t.y/HEIGHT - .5)*2
u = u - x*w
v = v - y*w
return u:cross(v)
end
function AddPlank(t)
local w = t.width
local h = t.height
local d = t.depth
local o = t.origin
local cube = {}
local j,k,l
for i=0,7 do
j = 2*(i%2)-1
k = 2*(math.floor(i/2)%2)-1
l = 2*(math.floor(i/4)%2)-1
table.insert(cube, o + j*w + k*h + l* d)
end
return AddCube({
vertices = t.vertices,
colours = t.colours,
light = t.light,
cube = cube,
colour = t.colour or Colour.x11.Burlywood3
})
end
function AddSlab(t)
local sc = t.startCentre
local sh = t.startHeight
local sw = t.startWidth
local ec = t.endCentre
local eh = t.endHeight
local ew = t.endWidth
local cube = {}
local j,k
for i=0,3 do
j = 2*(i%2)-1
k = 2*(math.floor(i/2)%2)-1
table.insert(cube,sc + j*sh + k*sw)
table.insert(cube,ec + j*eh + k*ew)
end
return AddCube({
vertices = t.vertices,
colours = t.colours,
light = t.light,
cube = cube,
colour = t.colour or Colour.x11.Burlywood3
})
end
function AddHalfTube(t)
local r = t.radius
local b = {
{t.startCentre,t.startWidth,t.startHeight},
{t.endCentre,t.endWidth,t.endHeight}
}
local vertices = t.vertices
local colours = t.colours
local c = t.colour or Colour.svg.DarkSlateBlue
local l = t.light:normalize()
local n = t.number or 36
local step = math.pi/n
local ang,pang,lc,ver
for i = 1,n do
ang = i*step
pang = (i-1)*step
for k,v in ipairs({
{1,pang},
{1,ang},
{2,ang},
{2,ang},
{1,pang},
{2,pang}
}) do
ver = math.cos(v[2]) * b[v[1]][2] + math.sin(v[2]) * b[v[1]][3]
table.insert(vertices,b[v[1]][1] + ver)
lc = l:dot(ver)
table.insert(colours,Colour.shade(c,50 + 25*lc))
end
end
return vertices, colours
end
function AddTube(t)
local r = t.radius
local b = {
{t.startCentre,t.startWidth,t.startHeight,t.startTexture},
{t.endCentre,t.endWidth,t.endHeight,t.endTexture}
}
local vertices = t.vertices
local colours = t.colours
local texcoords = t.texCoords
local normals = t.normals
local c = t.colour or Colour.svg.DarkSlateBlue
local l = t.light
if l then
l = l:normalize()
end
local n = t.number or 36
local step = 2*math.pi/n
local ang,pang,lc,ver
for i = 1,n do
ang = i*step
pang = (i-1)*step
for k,v in ipairs({
{1,pang},
{1,ang},
{2,ang},
{2,ang},
{1,pang},
{2,pang}
}) do
ver = math.cos(v[2]) * b[v[1]][2] + math.sin(v[2]) * b[v[1]][3]
table.insert(vertices,b[v[1]][1] + ver)
if l then
lc = l:dot(ver)
table.insert(colours,Colour.shade(c,50 + 25*lc))
else
table.insert(colours,c)
end
if texcoords then
table.insert(texcoords,
vec2(b[v[1]][4],v[2]/(2*math.pi)))
end
if normals then
table.insert(normals,
ver:normalize())
end
end
end
return vertices, colours, texcoords, normals
end
-- cube faces are in binary order: 000, 001, 010, 011 etc
local CubeFaces = {
{1,2,3,4},
{5,7,6,8},
{1,5,2,6},
{3,4,7,8},
{2,6,4,8},
{1,3,5,7}
}
function AddCube(t)
local cube = t.cube
local vertices = t.vertices or {}
local colours = t.colours or {}
local normals = t.normals or {}
local c = t.colour or Colour.x11.Burlywood3
local l = t.light:normalize()
local faces = t.faces or CubeFaces
local lc,n
for k,v in ipairs(faces) do
n = (cube[v[3]] - cube[v[1]]):cross(cube[v[2]] - cube[v[1]])
if n ~= vec3(0,0,0) then
n = n:normalize()
lc = n:dot(l)
end
for i,u in ipairs({1,2,3,2,3,4}) do
table.insert(vertices,cube[v[u]])
table.insert(normals,n)
table.insert(colours,
Colour.shade(c,75 + 25*lc)
)
end
end
return vertices,colours,normals
end
function AddJewel(t)
local o = t.origin
local a = SO3(t.axis,vec3(0,0,1))
local n = t.sides
local la = t.axis:len()
for i = 1,3 do
a[i] = la*a[i]
end
local vertices = t.vertices or {}
local colours = t.colours or {}
local clr = t.colour or Colour.svg.IndianRed
local l = t.light:normalize()
local th = math.pi/n
local cs = math.cos(th)
local sn = math.sin(th)
local h = (1 - cs)/(1 + cs)
local nh = 1/(1+h)
local nhl = 1/math.sqrt(1 + nh^2)
local k,b,c,d,nml,cl,nv
b = a[2]
c = cs*a[2] + sn*a[3]
d = -sn*a[2] + cs*a[3]
nv = 0
for i = 1,2*n do
k = 2*(i%2) - 1
for j = -1,1,2 do
table.insert(vertices,o + j*a[1])
table.insert(vertices,o + h*k*a[1] + b)
table.insert(vertices,o - h*k*a[1] + c)
nml = j*nh*a[1] + .5*(1 - j*k)*b + .5*(1 + j*k)*c
cl = nml:dot(l)/nhl
for m=1,3 do
table.insert(colours,Colour.shade(clr,75 + 25*cl))
end
nv = nv + 3
end
b = c
c = cs*c + sn*d
d = -sn*b + cs*d
end
return vertices,colours,nv
end
function TrackPoints(a)
a = a or {}
local pts = a.points or {}
local t = a.start or 0
local r = a.step or .1
r = r*r
local s = a.delta or .1
local f = a.pathFunction or function(q) return q*vec3(1,0,0) end
local nf = a.normalFunction or function(q) return vec3(0,1,0) end
local b = a.finish or 1
local tpt = f(t)
table.insert(pts,{tpt,
tangent({delta = s, pathFunction = f, time = t}),
nf(t),t})
local dis
local p
while t < b do
dis = 0
while dis < r do
t = t + s
p = f(t)
dis = dis + p:distSqr(tpt)
tpt = p
end
if t > b then
t = b
p = f(b)
end
table.insert(pts,{p,
tangent({delta = s, pathFunction = f, time = t}),
nf(t),t})
tpt = p
end
return pts
end
function AddStar(t)
local o = t.origin
local s = t.size
local l = t.light:normalize()/(math.sqrt(3))
local vertices = t.vertices or {}
local colours = t.colours or {}
local b = RandomBasisR3()
local v,n,c
local bb = {}
for i=0,7 do
bb[1] = 2*(i%2)-1
bb[2] = 2*(math.floor(i/2)%2)-1
bb[3] = 2*(math.floor(i/4)%2)-1
v = bb[1]*b[1] + bb[2]*b[2] + bb[3]*b[3]
n = math.abs(v:dot(l))
c = Colour.shade(Colour.x11["LemonChiffon" .. math.random(1,4)], 70+n*30)
for m=1,3 do
table.insert(colours,c)
table.insert(vertices,o+s*(v - 2*bb[m]*b[m]))
end
end
return vertices,colours
end
function tangent(a)
local s = a.delta/2 or .1
local f = a.pathFunction or function(q) return q*vec3(1,0,0) end
local t = a.time or 0
local u = f(t-s)
local v = f(t+s)
return (v-u)/(2*s)
end
local Tracks = {}
function Tracks.torus(p,q)
local innerRa = 10
local innerRb = 10
local outerR = 30
local trackFunction = function(t)
local it = p*t*2*math.pi
local ot = q*t*2*math.pi
return vec3(
(outerR + innerRb*math.cos(it))*math.cos(ot),
innerRa*math.sin(it),
(outerR + innerRb*math.cos(it))*math.sin(ot)
)
end
local trackNormal = function(t)
local it = p*t*2*math.pi
local ot = q*t*2*math.pi
return vec3(
innerRa*math.cos(it)*math.cos(ot),
innerRb*math.sin(it),
innerRa*math.cos(it)*math.sin(ot)
)
end
local coreFunction = function(t)
local ot = q*t*2*math.pi
return vec3(
outerR*math.cos(ot),
0,
outerR*math.sin(ot)
)
end
local maxHeight = innerRa
local minHeight = -innerRa
return trackFunction, trackNormal, maxHeight, minHeight
end
function Tracks.mobius()
local r = 30
local trackFunction = function(t)
local a = 2*math.pi*t
return vec3(r*math.cos(a),0,r*math.sin(a))
end
local trackNormal = function(t)
local a = math.pi*t
return vec3(
math.sin(a)*math.cos(2*a),
math.cos(a),
math.sin(a)*math.sin(2*a))
end
return trackFunction,trackNormal,0.5,-0.5
end
function Tracks.loop(p,q,r,h)
local r = r or 30
local h = h or 10
local w = vec3(0,50,0)
local trackFunction = function(t)
local a = 2*math.pi*t
return vec3(
r*math.cos(a),
h*math.sin(p*a),
r*math.sin(q*a))
end
local trackNormal = function(t)
return w - trackFunction(t)
end
return trackFunction,trackNormal,h,-h
end
function RandomVec3()
local th = 2*math.pi*math.random()
local z = 2*math.random() - 1
local r = math.sqrt(1 - z*z)
return vec3(r*math.cos(th),r*math.sin(th),z)
end
function RandomBasisR3()
local th = 2*math.pi*math.random()
local cth = math.cos(th)
local sth = math.sin(th)
local a = vec3(cth,sth,0)
local b = vec3(-sth,cth,0)
local c = vec3(0,0,1)
local v = RandomVec3()
a = a - 2*v:dot(a)*v
b = b - 2*v:dot(b)*v
c = c - 2*v:dot(c)*v
return {a,b,c}
end
function SO3(u,v)
if u == vec3(0,0,0) then
if v == vec3(0,0,0) then
return {vec3(1,0,0),vec3(0,1,0),vec3(0,0,1)}
end
u,v = v,u
end
if u:cross(v) == vec3(0,0,0) then
if u.x == 0 and u.y == 0 then
v = vec3(1,0,0)
else
v = vec3(u.y,-u.x,0)
end
end
local t = GramSchmidt({u,v})
t[3] = t[1]:cross(t[2])
return t
end
function GramSchmidt(t)
local o = {}
local w
for k,v in ipairs(t) do
w = v
for l,u in ipairs(o) do
w = w - w:dot(u)*u
end
if w ~= vec3(0,0,0) then
w = w:normalize()
table.insert(o,w)
end
end
return o
end
local Boolean = {}
function Boolean.readData(t,k,b)
local f
if t == "global" then
f = readGlobalData
elseif t == "project" then
f = readProjectData
else
f = readLocalData
end
local bol = f(k)
if bol then
if bol == 0 then
return false
else
return true
end
else
return b
end
end
function Boolean.saveData(t,k,b)
local f
if t == "global" then
f = saveGlobalData
elseif t == "project" then
f = saveProjectData
else
f = saveLocalData
end
if b then
f(k,1)
else
f(k,0)
end
end
cmodule.gexport {
RoundedRectangle = RoundedRectangle,
CircleOrSquare = CircleOrSquare,
Ordinal = Ordinal,
Regression = Regression,
addQuad = addQuad,
addTriangle = addTriangle,
addRoundedRect = addRoundedRect,
ApplyAffine = ApplyAffine,
USRotateCW = USRotateCW,
USRotateCCW = USRotateCCW,
USReflectV = USReflectV,
USReflectH = USReflectH,
USOrientation = USOrientation,
getLength = getLength,
evalLength = evalLength,
TriangleArea = TriangleArea,
converttouch = converttouch,
getzlevel = getzlevel,
applymatrix4 = applymatrix4,
cofactor4 = cofactor4,
Det3 = Det3,
applymatrix3 = applymatrix3,
cofactor3 = cofactor3,
Det2 = Det2,
screentoplane = screentoplane,
screennormal = screennormal,
AddPlank = AddPlank,
AddSlab = AddSlab,
AddHalfTube = AddHalfTube,
AddTube = AddTube,
AddCube = AddCube,
AddJewel = AddJewel,
TrackPoints = TrackPoints,
AddStar = AddStar,
tangent = tangent,
Tracks = Tracks,
RandomVec3 = RandomVec3,
RandomBasisR3 = RandomBasisR3,
SO3 = SO3,
GramSchmidt = GramSchmidt,
Boolean = Boolean
}
--]==]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.