Skip to content

Instantly share code, notes, and snippets.

@loopspace loopspace/1aTabOrder
Created May 20, 2013

Embed
What would you like to do?
Library Base Release v2.1 -A library of classes and functions forming basic functionality.
Library Base Tab Order
------------------------------
This file should not be included in the Codea project.
#ChangeLog
#Main
#Rectangle
#Font
#Coordinates
#BinDecHex
#Colour
#ColourNames
#TestSuite
#Touch
#utf8Case
#utf8
#Debug
--[==[
-- Binary, Decimal, Hexadecimal, Octal conversion
-- Author: Tim Kelly and Andrew Stacey
-- Websites: http://www.dialectronics.com/Lua/code/BinDecHex.shtml
-- http://www.math.ntnu.no/~stacey/HowDidIDoThat/iPad/Codea.html
-- Licence: See below
--[[
/*
* Copyright (c) 2007 Tim Kelly/Dialectronics
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit
* persons to whom the Software is furnished to do so, subject to the
* following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
--]]
--[[
Formatting suitable for Codea on iPad together with octal functions by
Andrew Stacey. Additions released under the original license.
--]]
local hex2bin = {
["0"] = "0000",
["1"] = "0001",
["2"] = "0010",
["3"] = "0011",
["4"] = "0100",
["5"] = "0101",
["6"] = "0110",
["7"] = "0111",
["8"] = "1000",
["9"] = "1001",
["a"] = "1010",
["b"] = "1011",
["c"] = "1100",
["d"] = "1101",
["e"] = "1110",
["f"] = "1111"
}
local bin2hex = {
["0000"] = "0",
["0001"] = "1",
["0010"] = "2",
["0011"] = "3",
["0100"] = "4",
["0101"] = "5",
["0110"] = "6",
["0111"] = "7",
["1000"] = "8",
["1001"] = "9",
["1010"] = "A",
["1011"] = "B",
["1100"] = "C",
["1101"] = "D",
["1110"] = "E",
["1111"] = "F"
}
local bin2oct = {
["000"] = "0",
["001"] = "1",
["010"] = "2",
["011"] = "3",
["100"] = "4",
["101"] = "5",
["110"] = "6",
["111"] = "7"
}
local oct2bin = {
["0"] = "000",
["1"] = "001",
["2"] = "010",
["3"] = "011",
["4"] = "100",
["5"] = "101",
["6"] = "110",
["7"] = "111"
}
-- These functions are big-endian and take up to 32 bits
-- Hex2Bin
-- Bin2Hex
-- Hex2Dec
-- Dec2Hex
-- Bin2Dec
-- Dec2Bin
function Hex2Bin(s)
-- s -> hexadecimal string
local ret = ""
local i = 0
for i in string.gfind(s, ".") do
i = string.lower(i)
ret = ret..hex2bin[i]
end
return ret
end
function Bin2Hex(s)
-- s -> binary string
local l = 0
local h = ""
local b = ""
local rem
l = string.len(s)
rem = l % 4
l = l-1
h = ""
-- need to prepend zeros to eliminate mod 4
if (rem > 0) then
s = string.rep("0", 4 - rem)..s
end
for i = 1, l, 4 do
b = string.sub(s, i, i+3)
h = h..bin2hex[b]
end
return h
end
function Bin2Oct(s)
-- s -> binary string
local l = 0
local h = ""
local b = ""
local rem
l = string.len(s)
rem = l % 3
l = l-1
h = ""
-- need to prepend zeros to eliminate mod 3
if (rem > 0) then
s = string.rep("0", 3 - rem)..s
end
for i = 1, l, 3 do
b = string.sub(s, i, i+2)
h = h..bin2oct[b]
end
return h
end
function Oct2Bin(s)
-- s -> octal string
local ret = ""
local i = 0
for i in string.gfind(s, ".") do
i = string.lower(i)
ret = ret..oct2bin[i]
end
return ret
end
function Bin2Dec(s)
-- s -> binary string
local num = 0
local ex = string.len(s) - 1
local l = 0
l = ex + 1
for i = 1, l do
b = string.sub(s, i, i)
if b == "1" then
num = num + 2^ex
end
ex = ex - 1
end
return string.format("%u", num)
end
function Dec2Bin(s, num)
-- s -> Base10 string
-- num -> string length to extend to
local n
if (num == nil) then
n = 0
else
n = num
end
s = string.format("%x", s)
s = Hex2Bin(s)
while string.len(s) < n do
s = "0"..s
end
while string.len(s) > n and string.sub(s,1,1) == "0" do
s = string.sub(s,2)
end
return s
end
function Hex2Dec(s)
-- s -> hexadecimal string
local s = Hex2Bin(s)
return Bin2Dec(s)
end
function Dec2Hex(s)
-- s -> Base10 string
s = string.format("%x", s)
return s
end
function Hex2Oct(s)
-- s -> hexadecimal string
local s = Hex2Bin(s)
return Bin2Oct(s)
end
function Oct2Hex(s)
-- s -> Base8 string
local s = Oct2Bin(s)
return Bin2Hex(s)
end
function Dec2Oct(s)
-- s -> decimal string
s = string.format("%o", s)
return s
end
function Oct2Dec(s)
-- s -> Base10 string
local s = Oct2Bin(s)
return Bin2Dec(s)
end
-- These functions are big-endian and will extend to 32 bits
-- BMAnd
-- BMNAnd
-- BMOr
-- BMXOr
-- BMNot
function BMAnd(v, m)
-- v -> hex string to be masked
-- m -> hex string mask
-- s -> hex string as masked
-- bv -> binary string of v
-- bm -> binary string mask
local bv = Hex2Bin(v)
local bm = Hex2Bin(m)
local i = 0
local s = ""
while (string.len(bv) < 32) do
bv = "0000"..bv
end
while (string.len(bm) < 32) do
bm = "0000"..bm
end
for i = 1, 32 do
cv = string.sub(bv, i, i)
cm = string.sub(bm, i, i)
if cv == cm then
if cv == "1" then
s = s.."1"
else
s = s.."0"
end
else
s = s.."0"
end
end
return Bin2Hex(s)
end
function BMNAnd(v, m)
-- v -> hex string to be masked
-- m -> hex string mask
-- s -> hex string as masked
-- bv -> binary string of v
-- bm -> binary string mask
local bv = Hex2Bin(v)
local bm = Hex2Bin(m)
local i = 0
local s = ""
while (string.len(bv) < 32) do
bv = "0000"..bv
end
while (string.len(bm) < 32) do
bm = "0000"..bm
end
for i = 1, 32 do
cv = string.sub(bv, i, i)
cm = string.sub(bm, i, i)
if cv == cm then
if cv == "1" then
s = s.."0"
else
s = s.."1"
end
else
s = s.."1"
end
end
return Bin2Hex(s)
end
function BMOr(v, m)
-- v -> hex string to be masked
-- m -> hex string mask
-- s -> hex string as masked
-- bv -> binary string of v
-- bm -> binary string mask
local bv = Hex2Bin(v)
local bm = Hex2Bin(m)
local i = 0
local s = ""
while (string.len(bv) < 32) do
bv = "0000"..bv
end
while (string.len(bm) < 32) do
bm = "0000"..bm
end
for i = 1, 32 do
cv = string.sub(bv, i, i)
cm = string.sub(bm, i, i)
if cv == "1" then
s = s.."1"
elseif cm == "1" then
s = s.."1"
else
s = s.."0"
end
end
return Bin2Hex(s)
end
function BMXOr(v, m)
-- v -> hex string to be masked
-- m -> hex string mask
-- s -> hex string as masked
-- bv -> binary string of v
-- bm -> binary string mask
local bv = Hex2Bin(v)
local bm = Hex2Bin(m)
local i = 0
local s = ""
while (string.len(bv) < 32) do
bv = "0000"..bv
end
while (string.len(bm) < 32) do
bm = "0000"..bm
end
for i = 1, 32 do
cv = string.sub(bv, i, i)
cm = string.sub(bm, i, i)
if cv == "1" then
if cm == "0" then
s = s.."1"
else
s = s.."0"
end
elseif cm == "1" then
if cv == "0" then
s = s.."1"
else
s = s.."0"
end
else
-- cv and cm == "0"
s = s.."0"
end
end
return Bin2Hex(s)
end
function BMNot(v, m)
-- v -> hex string to be masked
-- m -> hex string mask
-- s -> hex string as masked
-- bv -> binary string of v
-- bm -> binary string mask
local bv = Hex2Bin(v)
local bm = Hex2Bin(m)
local i = 0
local s = ""
while (string.len(bv) < 32) do
bv = "0000"..bv
end
while (string.len(bm) < 32) do
bm = "0000"..bm
end
for i = 1, 32 do
cv = string.sub(bv, i, i)
cm = string.sub(bm, i, i)
if cm == "1" then
if cv == "1" then
-- turn off
s = s.."0"
else
-- turn on
s = s.."1"
end
else
-- leave untouched
s = s..cv
end
end
return Bin2Hex(s)
end
-- these functions shift right and left, adding zeros to lost or gained bits
-- returned values are 32 bits long
-- BShRight(v, nb)
-- BShLeft(v, nb)
function BShRight(v, nb)
-- v -> hexstring value to be shifted
-- nb -> number of bits to shift to the right
-- s -> binary string of v
local s = Hex2Bin(v)
while (string.len(s) < 32) do
s = "0000"..s
end
s = string.sub(s, 1, 32 - nb)
while (string.len(s) < 32) do
s = "0"..s
end
return Bin2Hex(s)
end
function BShLeft(v, nb)
-- v -> hexstring value to be shifted
-- nb -> number of bits to shift to the right
-- s -> binary string of v
local s = Hex2Bin(v)
while (string.len(s) < 32) do
s = "0000"..s
end
s = string.sub(s, nb + 1, 32)
while (string.len(s) < 32) do
s = s.."0"
end
return Bin2Hex(s)
end
function BinLength(n)
return math.floor(math.log(n)/math.log(2) + .5)
end
function BinReverse(n,m)
local s = Dec2Bin(n,m)
s = string.reverse(s)
return Bin2Dec(s)
end
--]==]
--[[
ChangeLog
=========
v2.2 Latest version of autogist
v2.1 Updated to latest version of cmodule
v2.0 Split into separate libraries: "Library Base" consists of the
modules that just about every project will import.
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.
--]]
--[==[
-- Colour manipulation
-- Author: Andrew Stacey
-- Website: http://www.math.ntnu.no/~stacey/HowDidIDoThat/iPad/Codea.html
-- Licence: CC0 (http://wiki.creativecommons.org/CC0)
--[[
This provides some functions for basic colour manipulation such as
colour blending. The functions act on "color" objects and also return
"color" objects.
--]]
--[[
Although we are not a class, we work in the "Colour" namespace to keep
ourselves from interfering with other classes.
--]]
cache = true
local Colour = {}
-- Should we modify the alpha of our colours?
local ModifyAlpha = false
--[[
This blends the two specified colours according to the parameter given
as the middle argument (the syntax is based on that of the "xcolor"
LaTeX package) which is the percentage of the first colour.
--]]
function Colour.blend(cc,t,c,m)
local s,r,g,b,a
m = m or ModifyAlpha
s = t / 100
r = s * cc.r + (1 - s) * c.r
g = s * cc.g + (1 - s) * c.g
b = s * cc.b + (1 - s) * c.b
if m then
a = s * cc.a + (1 - s) * c.a
else
a = cc.a
end
return color(r,g,b,a)
end
--[[
This "tints" the specified colour which means blending it with white.
The parameter is the percentage of the specified colour.
--]]
function Colour.tint(c,t,m)
local s,r,g,b,a
m = m or ModifyAlpha
s = t / 100
r = s * c.r + (1 - s) * 255
g = s * c.g + (1 - s) * 255
b = s * c.b + (1 - s) * 255
if m then
a = s * c.a + (1 - s) * 255
else
a = c.a
end
return color(r,g,b,a)
end
--[[
This "shades" the specified colour which means blending it with black.
The parameter is the percentage of the specified colour.
--]]
function Colour.shade(c,t,m)
local s,r,g,b,a
m = m or ModifyAlpha
s = t / 100
r = s * c.r
g = s * c.g
b = s * c.b
if m then
a = s * c.a
else
a = c.a
end
return color(r,g,b,a)
end
--[[
This "tones" the specified colour which means blending it with gray.
The parameter is the percentage of the specified colour.
--]]
function Colour.tone(c,t,m)
local s,r,g,b,a
m = m or ModifyAlpha
s = t / 100
r = s * c.r + (1 - s) * 127
g = s * c.g + (1 - s) * 127
b = s * c.b + (1 - s) * 127
if m then
a = s * c.a + (1 - s) * 127
else
a = c.a
end
return color(r,g,b,a)
end
--[[
This returns the complement of the given colour.
--]]
function Colour.complement(c,m)
local r,g,b,a
m = m or ModifyAlpha
r = 255 - c.r
g = 255 - c.g
b = 255 - c.b
if m then
a = 255 - c.a
else
a = c.a
end
return color(r,g,b,a)
end
--[[
This forces each channel to an on/off state.
--]]
function Colour.posterise(c,t,m)
local r,g,b,a
m = m or ModifyAlpha
t = t or 127
if c.r > t then
r = 255
else
r = 0
end
if c.g > t then
g = 255
else
g = 0
end
if c.b > t then
b = 255
else
b = 0
end
if m then
if c.a > t then
a = 255
else
a = 0
end
else
a = c.a
end
return color(r,g,b,a)
end
--[[
These functions adjust the alpha.
--]]
function Colour.opacity(c,t)
return color(c.r,c.g,c.b,t*c.a/100)
end
function Colour.opaque(c)
return color(c.r,c.g,c.b,255)
end
--[[
This "pretty prints" the colour, converting it to a string.
--]]
function Colour.tostring(c)
return "R:" .. c.r .. " G:" .. c.g .. " B:" .. c.b .. " A:" .. c.a
end
function Colour.fromstring(c)
local r,g,b,a
r = string.match(c,"R:(%d+)")
g = string.match(c,"G:(%d+)")
b = string.match(c,"B:(%d+)")
a = string.match(c,"A:(%d+)")
return color(r,g,b,a)
end
function Colour.readData(t,k,c)
local f
if t == "global" then
f = readGlobalData
elseif t == "project" then
f = readProjectData
else
f = readLocalData
end
local col = f(k)
if col then
return Colour.fromstring(col)
else
return c
end
end
function Colour.saveData(t,k,c)
local f
if t == "global" then
f = saveGlobalData
elseif t == "project" then
f = saveProjectData
else
f = saveLocalData
end
f(k,Colour.tostring(c))
end
--[[
This searches for a colour by name from a specified list (such as
"svg" or "x11"). It looks for a match for the given string at the
start of the name of the colour, without regard for case.
--]]
function Colour.byName(t,n)
local ln,k,v,s,lk
ln = "^" .. string.lower(n)
for k,v in pairs(Colour[t]) do
lk = string.lower(k)
s = string.find(lk,ln)
if s then
return v
end
end
print("Colour Error: No colour of name " .. n .. " exists in type " .. t)
end
--[[
Get a random colour, either from a list or random.
But not black.
--]]
local __colourlists = {}
function Colour.random(s)
if s then
if __colourlists[s] then
return __colourlists[s][math.random(#__colourlists[s])]
elseif Colour[s] then
__colourlists[s] = {}
for k,v in pairs(Colour[s]) do
if k ~= "Black" then
table.insert(__colourlists[s],v)
end
end
return __colourlists[s][math.random(#__colourlists[s])]
end
end
local r,g,b = 0,0,0
while r+g+b < 20 do
r,g,b = math.random(256)-1,
math.random(256)-1,math.random(256)-1
end
return color(r,g,b,255)
end
--[[
The "ColourPicker" class is a module for the "User Interface" class
(though it can be used independently). It defines a grid of colours
(drawn from a list) which the user can select from. When the user
selects a colour then a "call-back" function is called with the given
colour as its argument.
--]]
local ColourPicker = class()
--[[
There is nothing to do on initialisation.
--]]
function ColourPicker:init()
end
--[[
This is the real initialisation code, but it can be called at any
time. It sets up the list from which the colours will be displayed
for the user to select from. At the moment, it can deal with the
"x11" and "svg" lists, though allowing more is simple enough: the main
issue is deciding how many rows and columns to use to display the grid
of colours.
--]]
function ColourPicker:setList(t)
local c,m,n
if t == "x11" then
-- 317 colours
c = Colour.x11
n = 20
m = 16
else
-- 151 colours
c = Colour.svg
n = 14
m = 11
end
local l = {}
for k,v in pairs(c) do
table.insert(l,v)
end
table.sort(l,ColourSort)
self.m = m
self.n = n
self.colours = l
end
--[[
This is a crude sort routine for the colours. It is not a good one.
--]]
function ColourSort(a,b)
local c,d
c = 2 * a.r + 4 * a.g + a.b
d = 2 * b.r + 4 * b.g + b.b
return c < d
end
--[[
This draws a grid of rounded rectangles (see the "Font" class) of each
colour.
--]]
function ColourPicker:draw()
if self.active then
pushStyle()
strokeWidth(-1)
local w = WIDTH/self.n
local h = HEIGHT/self.m
local s = math.min(w/4,h/4,10)
local c = self.colours
w = w - s
h = h - s
local i = 0
local j = 1
for k,v in ipairs(c) do
fill(v)
RoundedRectangle(s/2 + i*(w+s),HEIGHT + s/2 - j*(h+s),w,h,s)
i = i + 1
if i == self.n then
i = 0
j = j + 1
end
end
popStyle()
end
end
--[[
If we are active, we claim all touches.
--]]
function ColourPicker:isTouchedBy(touch)
if self.active then
return true
end
end
--[[
The touch information is used to select a colour. We wait until the
gesture has ended and then look at the xy coordinates of the first
touch. This tells us which colour was selected and this is passed to
the call-back function which is stored as the "action" attribute.
The action attribute should be an anonymous function which takes one
argument, which will be a "color" object.
--]]
function ColourPicker:processTouches(g)
if g.updated then
if g.type.ended then
local t = g.touchesArr[1]
local w = WIDTH/self.n
local h = HEIGHT/self.m
local i = math.floor(t.touch.x/w) + 1
local j = math.floor((HEIGHT - t.touch.y)/h)
local n = i + j*self.n
if self.colours[n] then
if self.action then
local a = self.action(self.colours[n])
if a then
self:deactivate()
end
end
else
self:deactivate()
end
g:reset()
else
g:noted()
end
end
end
--[[
This activates the colour picker, making it active and setting the
call-back function to whatever was passed to the activation function.
--]]
function ColourPicker:activate(f)
self.active = true
self.action = f
end
function ColourPicker:deactivate()
self.active = false
self.action = nil
end
ColourPicker.help = "The colour picker is used to choose a colour from a given range. To choose a colour, touch one of the coloured rectangles. You can cancel the colour picker by touching some part of the screen where there isn't a coloured rectangle (but where there would be one if there were more colours)."
local ColourWheel = class()
function ColourWheel:init()
self.meshsqr = mesh()
self.meshhex = mesh()
self.colour = color(255, 0, 0, 255)
self.rimcolour = color(255, 0, 0, 255)
local l = .9
self.meshsqr.vertices = {
l*vec2(1,1),
l*vec2(-1,1),
l*vec2(-1,-1),
l*vec2(1,1),
l*vec2(1,-1),
l*vec2(-1,-1),
}
self.meshsqr.colors = {
color(255, 255, 255, 255),
self.rimcolour,
color(0, 0, 0, 255),
color(255, 255, 255, 255),
Colour.complement(self.rimcolour,false),
color(0, 0, 0, 255),
}
local t = {}
local c = {}
local cc = {
color(255, 0, 0, 255),
color(255, 255, 0, 255),
color(0, 255, 0, 255),
color(0, 255, 255, 255),
color(0, 0, 255, 255),
color(255, 0, 255, 255),
}
self.rimcolours = cc
for i = 1,6 do
table.insert(t,
2*vec2(
math.cos(i*math.pi/3),
math.sin(i*math.pi/3)
)
)
table.insert(t,
1.5*vec2(
math.cos(i*math.pi/3),
math.sin(i*math.pi/3)
)
)
table.insert(t,
2*vec2(
math.cos((i+1)*math.pi/3),
math.sin((i+1)*math.pi/3)
)
)
table.insert(t,
2*vec2(
math.cos(i*math.pi/3),
math.sin(i*math.pi/3)
)
)
table.insert(t,
1.5*vec2(
math.cos(i*math.pi/3),
math.sin(i*math.pi/3)
)
)
table.insert(t,
1.5*vec2(
math.cos((i-1)*math.pi/3),
math.sin((i-1)*math.pi/3)
)
)
table.insert(c,cc[i])
table.insert(c,cc[i])
table.insert(c,cc[i%6+1])
table.insert(c,cc[i])
table.insert(c,cc[i])
table.insert(c,cc[(i-2)%6+1])
end
self.meshhex.vertices = t
self.meshhex.colors = c
self.ratio = vec2(0,1)
self.alpha = 255
self:setRimColour(1,0)
end
function ColourWheel:setRimColour(x,y)
self.angle = math.atan2(y,x)
local a = 3*self.angle/math.pi
local i = math.floor(a)+1
a = 100*(i - a)
i = (i-2)%6 + 1
local j = i%6 + 1
self.rimcolour = Colour.blend(
self.rimcolours[i],a,self.rimcolours[j])
self.meshsqr:color(2,self.rimcolour)
self.meshsqr:color(5,Colour.complement(self.rimcolour,false))
self:setColour()
end
function ColourWheel:setFromColour(rc)
local c = color(rc.r,rc.g,rc.b,rc.a)
self.alpha = c.a
local x,y = math.min(c.r,c.g,c.b)/255,math.max(c.r,c.g,c.b)/255
self.ratio = vec2(x,y)
local i,j,ar
if x == y then
self.rimcolour = Colour.svg.Red
self.angle = math.pi/3
else
c.r = (c.r - x*255)/(y - x)
c.g = (c.g - x*255)/(y - x)
c.b = (c.b - x*255)/(y - x)
c.a = 255
self.rimcolour = c
ar = (c.r + c.g + c.b)/255 - 1
if c.r >= c.g and c.r >= c.b then
i = 1
if c.g >= c.b then
j = 2
else
j = 6
end
elseif c.g >= c.b then
i = 3
if c.b >= c.r then
j = 4
else
j = 2
end
else
i = 5
if c.r >= c.g then
j = 6
else
j = 4
end
end
self.angle = (i*(1-ar) + ar*j)*math.pi/3
end
self.meshsqr:color(2,self.rimcolour)
self.meshsqr:color(5,Colour.complement(self.rimcolour,false))
self:setColour()
end
function ColourWheel:setColour()
local x,y = self.ratio.x,self.ratio.y
if y > x then
self.colour =
Colour.shade(
Colour.tint(
self.rimcolour,100*(1-x/y),false),100*y,false)
elseif x > y then
self.colour =
Colour.shade(
Colour.tint(
Colour.complement(self.rimcolour,false)
,100*(1-y/x),false),100*x,false)
else
self.colour = color(255*x,255*x,255*x,255)
end
self.colour.a = self.alpha
end
function ColourWheel:draw()
if not self.active then
return false
end
pushStyle()
pushMatrix()
resetMatrix()
resetStyle()
translate(WIDTH/2,HEIGHT/2)
pushMatrix()
scale(100)
fill(71, 71, 71, 255)
RoundedRectangle(-2.1,-3.1,4.6,5.2,.1)
fill(Colour.opaque(self.colour))
RoundedRectangle(-1.1,-2.9,2.2,.8,.1)
self.meshsqr:draw()
self.meshhex:draw()
lineCapMode(SQUARE)
strokeWidth(.05)
--noSmooth()
stroke(255, 255, 255, 255)
line(2.05,self.alpha*3/255-1.5,2.35,self.alpha*3/255-1.5)
stroke(127, 127, 127, 255)
line(2.2,-1.55,2.2,1.55)
stroke(Colour.complement(self.rimcolour,false))
local a = self.angle - (math.floor(
3*self.angle/math.pi) + .5)*math.pi/3
local r = math.cos(math.pi/6)/math.cos(a)
line(1.53*r*math.cos(self.angle),1.53*r*math.sin(self.angle),
1.97*r*math.cos(self.angle),1.97*r*math.sin(self.angle))
stroke(255, 255, 255, 255)
noFill()
noSmooth()
popMatrix()
strokeWidth(5)
ellipseMode(RADIUS)
ellipse(self.ratio.x*180-90,self.ratio.y*180-90,20)
ellipse(220,self.alpha*300/255-150,20)
fill(Colour.opaque(
Colour.complement(
Colour.posterise(self.colour,127,false),false
)
)
)
font("Courier-Bold")
textMode(CENTER)
fontSize(48)
text("Select",0,-250)
popMatrix()
popStyle()
end
--[[
If we are active, we claim all touches.
--]]
function ColourWheel:isTouchedBy(touch)
if self.active then
return true
end
end
--[[
The touch information is used to select a colour. We wait until the
gesture has ended and then look at the xy coordinates of the first
touch. This tells us which colour was selected and this is passed to
the call-back function which is stored as the "action" attribute.
The action attribute should be an anonymous function which takes one
argument, which will be a "color" object.
--]]
function ColourWheel:processTouches(g)
if g.updated then
local t = g.touchesArr[1]
local x = (t.touch.x - WIDTH/2)/100
local y = (t.touch.y - HEIGHT/2)/100
if t.touch.state == BEGAN then
if math.abs(x) < .9 and math.abs(y) < .9 then
self.touchedon = 0
elseif vec2(x,y):lenSqr() > 1 and vec2(x,y):lenSqr() < 4 then
self.touchedon = 1
elseif math.abs(x) < 1.1 and math.abs(y+2.5) < .4 then
self.touchedon = 2
elseif math.abs(x-2.2) < .1 and math.abs(y) < 1.6 then
self.touchedon = 3
elseif math.abs(x-.2) < 2.3 and math.abs(y+.5) < 2.6 then
self.touchedon = 4
else
self.touchedon = 5
end
end
if self.touchedon == 0 then
x = math.min(math.max((x+.9)/1.8,0),1)
y = math.min(math.max((y+.9)/1.8,0),1)
self.ratio = vec2(x,y)
self:setColour()
end
if self.touchedon == 1 then
self:setRimColour(x,y)
end
if self.touchedon == 3 then
self.alpha = math.min(math.max((y+1.5)*255/3,0),255)
self:setColour()
end
if t.touch.state == ENDED then
if self.touchedon == 5 then
if math.abs(x) > 2.1 or math.abs(y+.5) > 2.6 then
self:deactivate()
end
elseif self.touchedon == 2 then
if math.abs(x) < 1.1 and math.abs(y+2.5) < .4 then
local a = self.action(self.colour)
if a then
self:deactivate()
end
end
end
end
g:noted()
end
if g.type.ended then
g:reset()
end
end
--[[
This activates the colour wheel, making it active and setting the
call-back function to whatever was passed to the activation function.
--]]
function ColourWheel:activate(c,f)
self.active = true
if c then
self:setFromColour(c)
end
self.action = f
end
function ColourWheel:deactivate()
self.active = false
self.action = nil
end
ColourWheel.help = "The colour wheel is used to choose a colour. The outer wheel selects the dominant colour then the inner square allows you to blend it or its complement with white and black. Click on the Select button to choose the colour or outside the wheel region to cancel the colour change."
return {Colour, ColourPicker, ColourWheel}
--]==]
--[==[
-- Colour definitions
-- Author: Andrew Stacey
-- Website: http://www.math.ntnu.no/~stacey/HowDidIDoThat/iPad/Codea.html
-- Licence: LPPL (http://www.latex-project.org/lppl.txt)
--[[
This file defines two arrays of colours one according to the SVG
definitions and one according to the x11 definitions. The precise
numbers were taken from the definitions in the "xcolor" LaTeX package.
The xcolor package can be found at http://www.ctan.org/pkg/xcolor
--]]
local Colour = unpack(cimport "Colour",nil)
Colour.transparent = color(0,0,0,0)
Colour.svg = {}
Colour.svg.AliceBlue = color(239,247,255,255)
Colour.svg.AntiqueWhite = color(249,234,215,255)
Colour.svg.Aqua = color(0,255,255,255)
Colour.svg.Aquamarine = color(126,255,211,255)
Colour.svg.Azure = color(239,255,255,255)
Colour.svg.Beige = color(244,244,220,255)
Colour.svg.Bisque = color(255,227,196,255)
Colour.svg.Black = color(0,0,0,255)
Colour.svg.BlanchedAlmond = color(255,234,205,255)
Colour.svg.Blue = color(0,0,255,255)
Colour.svg.BlueViolet = color(137,43,226,255)
Colour.svg.Brown = color(165,42,42,255)
Colour.svg.BurlyWood = color(221,183,135,255)
Colour.svg.CadetBlue = color(94,158,160,255)
Colour.svg.Chartreuse = color(126,255,0,255)
Colour.svg.Chocolate = color(210,104,29,255)
Colour.svg.Coral = color(255,126,79,255)
Colour.svg.CornflowerBlue = color(99,149,237,255)
Colour.svg.Cornsilk = color(255,247,220,255)
Colour.svg.Crimson = color(220,20,59,255)
Colour.svg.Cyan = color(0,255,255,255)
Colour.svg.DarkBlue = color(0,0,138,255)
Colour.svg.DarkCyan = color(0,138,138,255)
Colour.svg.DarkGoldenrod = color(183,133,11,255)
Colour.svg.DarkGray = color(169,169,169,255)
Colour.svg.DarkGreen = color(0,99,0,255)
Colour.svg.DarkGrey = color(169,169,169,255)
Colour.svg.DarkKhaki = color(188,182,107,255)
Colour.svg.DarkMagenta = color(138,0,138,255)
Colour.svg.DarkOliveGreen = color(84,107,47,255)
Colour.svg.DarkOrange = color(255,140,0,255)
Colour.svg.DarkOrchid = color(183,49,204,255)
Colour.svg.DarkRed = color(138,0,0,255)
Colour.svg.DarkSalmon = color(232,150,122,255)
Colour.svg.DarkSeaGreen = color(142,187,142,255)
Colour.svg.DarkSlateBlue = color(72,61,138,255)
Colour.svg.DarkSlateGray = color(47,79,79,255)
Colour.svg.DarkSlateGrey = color(47,79,79,255)
Colour.svg.DarkTurquoise = color(0,206,209,255)
Colour.svg.DarkViolet = color(147,0,211,255)
Colour.svg.DeepPink = color(255,20,146,255)
Colour.svg.DeepSkyBlue = color(0,191,255,255)
Colour.svg.DimGray = color(104,104,104,255)
Colour.svg.DimGrey = color(104,104,104,255)
Colour.svg.DodgerBlue = color(29,144,255,255)
Colour.svg.FireBrick = color(177,33,33,255)
Colour.svg.FloralWhite = color(255,249,239,255)
Colour.svg.ForestGreen = color(33,138,33,255)
Colour.svg.Fuchsia = color(255,0,255,255)
Colour.svg.Gainsboro = color(220,220,220,255)
Colour.svg.GhostWhite = color(247,247,255,255)
Colour.svg.Gold = color(255,215,0,255)
Colour.svg.Goldenrod = color(218,165,31,255)
Colour.svg.Gray = color(127,127,127,255)
Colour.svg.Green = color(0,127,0,255)
Colour.svg.GreenYellow = color(173,255,47,255)
Colour.svg.Grey = color(127,127,127,255)
Colour.svg.Honeydew = color(239,255,239,255)
Colour.svg.HotPink = color(255,104,179,255)
Colour.svg.IndianRed = color(205,91,91,255)
Colour.svg.Indigo = color(74,0,130,255)
Colour.svg.Ivory = color(255,255,239,255)
Colour.svg.Khaki = color(239,229,140,255)
Colour.svg.Lavender = color(229,229,249,255)
Colour.svg.LavenderBlush = color(255,239,244,255)
Colour.svg.LawnGreen = color(124,252,0,255)
Colour.svg.LemonChiffon = color(255,249,205,255)
Colour.svg.LightBlue = color(173,216,229,255)
Colour.svg.LightCoral = color(239,127,127,255)
Colour.svg.LightCyan = color(224,255,255,255)
Colour.svg.LightGoldenrod = color(237,221,130,255)
Colour.svg.LightGoldenrodYellow = color(249,249,210,255)
Colour.svg.LightGray = color(211,211,211,255)
Colour.svg.LightGreen = color(144,237,144,255)
Colour.svg.LightGrey = color(211,211,211,255)
Colour.svg.LightPink = color(255,181,192,255)
Colour.svg.LightSalmon = color(255,160,122,255)
Colour.svg.LightSeaGreen = color(31,177,170,255)
Colour.svg.LightSkyBlue = color(135,206,249,255)
Colour.svg.LightSlateBlue = color(132,112,255,255)
Colour.svg.LightSlateGray = color(119,135,153,255)
Colour.svg.LightSlateGrey = color(119,135,153,255)
Colour.svg.LightSteelBlue = color(175,196,221,255)
Colour.svg.LightYellow = color(255,255,224,255)
Colour.svg.Lime = color(0,255,0,255)
Colour.svg.LimeGreen = color(49,205,49,255)
Colour.svg.Linen = color(249,239,229,255)
Colour.svg.Magenta = color(255,0,255,255)
Colour.svg.Maroon = color(127,0,0,255)
Colour.svg.MediumAquamarine = color(102,205,170,255)
Colour.svg.MediumBlue = color(0,0,205,255)
Colour.svg.MediumOrchid = color(186,84,211,255)
Colour.svg.MediumPurple = color(146,112,219,255)
Colour.svg.MediumSeaGreen = color(59,178,113,255)
Colour.svg.MediumSlateBlue = color(123,104,237,255)
Colour.svg.MediumSpringGreen = color(0,249,154,255)
Colour.svg.MediumTurquoise = color(72,209,204,255)
Colour.svg.MediumVioletRed = color(198,21,132,255)
Colour.svg.MidnightBlue = color(24,24,112,255)
Colour.svg.MintCream = color(244,255,249,255)
Colour.svg.MistyRose = color(255,227,225,255)
Colour.svg.Moccasin = color(255,227,181,255)
Colour.svg.NavajoWhite = color(255,221,173,255)
Colour.svg.Navy = color(0,0,127,255)
Colour.svg.NavyBlue = color(0,0,127,255)
Colour.svg.OldLace = color(252,244,229,255)
Colour.svg.Olive = color(127,127,0,255)
Colour.svg.OliveDrab = color(107,141,34,255)
Colour.svg.Orange = color(255,165,0,255)
Colour.svg.OrangeRed = color(255,68,0,255)
Colour.svg.Orchid = color(218,112,214,255)
Colour.svg.PaleGoldenrod = color(237,232,170,255)
Colour.svg.PaleGreen = color(151,251,151,255)
Colour.svg.PaleTurquoise = color(175,237,237,255)
Colour.svg.PaleVioletRed = color(219,112,146,255)
Colour.svg.PapayaWhip = color(255,238,212,255)
Colour.svg.PeachPuff = color(255,218,184,255)
Colour.svg.Peru = color(205,132,63,255)
Colour.svg.Pink = color(255,191,202,255)
Colour.svg.Plum = color(221,160,221,255)
Colour.svg.PowderBlue = color(175,224,229,255)
Colour.svg.Purple = color(127,0,127,255)
Colour.svg.Red = color(255,0,0,255)
Colour.svg.RosyBrown = color(187,142,142,255)
Colour.svg.RoyalBlue = color(65,104,225,255)
Colour.svg.SaddleBrown = color(138,68,19,255)
Colour.svg.Salmon = color(249,127,114,255)
Colour.svg.SandyBrown = color(243,164,95,255)
Colour.svg.SeaGreen = color(45,138,86,255)
Colour.svg.Seashell = color(255,244,237,255)
Colour.svg.Sienna = color(160,81,44,255)
Colour.svg.Silver = color(191,191,191,255)
Colour.svg.SkyBlue = color(135,206,234,255)
Colour.svg.SlateBlue = color(105,89,205,255)
Colour.svg.SlateGray = color(112,127,144,255)
Colour.svg.SlateGrey = color(112,127,144,255)
Colour.svg.Snow = color(255,249,249,255)
Colour.svg.SpringGreen = color(0,255,126,255)
Colour.svg.SteelBlue = color(70,130,179,255)
Colour.svg.Tan = color(210,179,140,255)
Colour.svg.Teal = color(0,127,127,255)
Colour.svg.Thistle = color(216,191,216,255)
Colour.svg.Tomato = color(255,99,71,255)
Colour.svg.Turquoise = color(63,224,207,255)
Colour.svg.Violet = color(237,130,237,255)
Colour.svg.VioletRed = color(208,31,144,255)
Colour.svg.Wheat = color(244,221,178,255)
Colour.svg.White = color(255,255,255,255)
Colour.svg.WhiteSmoke = color(244,244,244,255)
Colour.svg.Yellow = color(255,255,0,255)
Colour.svg.YellowGreen = color(154,205,49,255)
Colour.x11 = {}
Colour.x11.AntiqueWhite1 = color(255,238,219,255)
Colour.x11.AntiqueWhite2 = color(237,223,204,255)
Colour.x11.AntiqueWhite3 = color(205,191,175,255)
Colour.x11.AntiqueWhite4 = color(138,130,119,255)
Colour.x11.Aquamarine1 = color(126,255,211,255)
Colour.x11.Aquamarine2 = color(118,237,197,255)
Colour.x11.Aquamarine3 = color(102,205,170,255)
Colour.x11.Aquamarine4 = color(68,138,116,255)
Colour.x11.Azure1 = color(239,255,255,255)
Colour.x11.Azure2 = color(224,237,237,255)
Colour.x11.Azure3 = color(192,205,205,255)
Colour.x11.Azure4 = color(130,138,138,255)
Colour.x11.Bisque1 = color(255,227,196,255)
Colour.x11.Bisque2 = color(237,212,182,255)
Colour.x11.Bisque3 = color(205,182,158,255)
Colour.x11.Bisque4 = color(138,124,107,255)
Colour.x11.Blue1 = color(0,0,255,255)
Colour.x11.Blue2 = color(0,0,237,255)
Colour.x11.Blue3 = color(0,0,205,255)
Colour.x11.Blue4 = color(0,0,138,255)
Colour.x11.Brown1 = color(255,63,63,255)
Colour.x11.Brown2 = color(237,58,58,255)
Colour.x11.Brown3 = color(205,51,51,255)
Colour.x11.Brown4 = color(138,34,34,255)
Colour.x11.Burlywood1 = color(255,211,155,255)
Colour.x11.Burlywood2 = color(237,196,145,255)
Colour.x11.Burlywood3 = color(205,170,124,255)
Colour.x11.Burlywood4 = color(138,114,84,255)
Colour.x11.CadetBlue1 = color(151,244,255,255)
Colour.x11.CadetBlue2 = color(141,228,237,255)
Colour.x11.CadetBlue3 = color(122,196,205,255)
Colour.x11.CadetBlue4 = color(82,133,138,255)
Colour.x11.Chartreuse1 = color(126,255,0,255)
Colour.x11.Chartreuse2 = color(118,237,0,255)
Colour.x11.Chartreuse3 = color(102,205,0,255)
Colour.x11.Chartreuse4 = color(68,138,0,255)
Colour.x11.Chocolate1 = color(255,126,35,255)
Colour.x11.Chocolate2 = color(237,118,33,255)
Colour.x11.Chocolate3 = color(205,102,28,255)
Colour.x11.Chocolate4 = color(138,68,19,255)
Colour.x11.Coral1 = color(255,114,85,255)
Colour.x11.Coral2 = color(237,105,79,255)
Colour.x11.Coral3 = color(205,90,68,255)
Colour.x11.Coral4 = color(138,62,47,255)
Colour.x11.Cornsilk1 = color(255,247,220,255)
Colour.x11.Cornsilk2 = color(237,232,205,255)
Colour.x11.Cornsilk3 = color(205,200,176,255)
Colour.x11.Cornsilk4 = color(138,135,119,255)
Colour.x11.Cyan1 = color(0,255,255,255)
Colour.x11.Cyan2 = color(0,237,237,255)
Colour.x11.Cyan3 = color(0,205,205,255)
Colour.x11.Cyan4 = color(0,138,138,255)
Colour.x11.DarkGoldenrod1 = color(255,184,15,255)
Colour.x11.DarkGoldenrod2 = color(237,173,14,255)
Colour.x11.DarkGoldenrod3 = color(205,149,12,255)
Colour.x11.DarkGoldenrod4 = color(138,100,7,255)
Colour.x11.DarkOliveGreen1 = color(201,255,112,255)
Colour.x11.DarkOliveGreen2 = color(187,237,104,255)
Colour.x11.DarkOliveGreen3 = color(161,205,89,255)
Colour.x11.DarkOliveGreen4 = color(109,138,61,255)
Colour.x11.DarkOrange1 = color(255,126,0,255)
Colour.x11.DarkOrange2 = color(237,118,0,255)
Colour.x11.DarkOrange3 = color(205,102,0,255)
Colour.x11.DarkOrange4 = color(138,68,0,255)
Colour.x11.DarkOrchid1 = color(191,62,255,255)
Colour.x11.DarkOrchid2 = color(177,58,237,255)
Colour.x11.DarkOrchid3 = color(154,49,205,255)
Colour.x11.DarkOrchid4 = color(104,33,138,255)
Colour.x11.DarkSeaGreen1 = color(192,255,192,255)
Colour.x11.DarkSeaGreen2 = color(179,237,179,255)
Colour.x11.DarkSeaGreen3 = color(155,205,155,255)
Colour.x11.DarkSeaGreen4 = color(104,138,104,255)
Colour.x11.DarkSlateGray1 = color(150,255,255,255)
Colour.x11.DarkSlateGray2 = color(140,237,237,255)
Colour.x11.DarkSlateGray3 = color(121,205,205,255)
Colour.x11.DarkSlateGray4 = color(81,138,138,255)
Colour.x11.DeepPink1 = color(255,20,146,255)
Colour.x11.DeepPink2 = color(237,17,136,255)
Colour.x11.DeepPink3 = color(205,16,118,255)
Colour.x11.DeepPink4 = color(138,10,79,255)
Colour.x11.DeepSkyBlue1 = color(0,191,255,255)
Colour.x11.DeepSkyBlue2 = color(0,177,237,255)
Colour.x11.DeepSkyBlue3 = color(0,154,205,255)
Colour.x11.DeepSkyBlue4 = color(0,104,138,255)
Colour.x11.DodgerBlue1 = color(29,144,255,255)
Colour.x11.DodgerBlue2 = color(28,133,237,255)
Colour.x11.DodgerBlue3 = color(23,116,205,255)
Colour.x11.DodgerBlue4 = color(16,77,138,255)
Colour.x11.Firebrick1 = color(255,48,48,255)
Colour.x11.Firebrick2 = color(237,43,43,255)
Colour.x11.Firebrick3 = color(205,38,38,255)
Colour.x11.Firebrick4 = color(138,25,25,255)
Colour.x11.Gold1 = color(255,215,0,255)
Colour.x11.Gold2 = color(237,201,0,255)
Colour.x11.Gold3 = color(205,173,0,255)
Colour.x11.Gold4 = color(138,117,0,255)
Colour.x11.Goldenrod1 = color(255,192,36,255)
Colour.x11.Goldenrod2 = color(237,179,33,255)
Colour.x11.Goldenrod3 = color(205,155,28,255)
Colour.x11.Goldenrod4 = color(138,104,20,255)
Colour.x11.Green1 = color(0,255,0,255)
Colour.x11.Green2 = color(0,237,0,255)
Colour.x11.Green3 = color(0,205,0,255)
Colour.x11.Green4 = color(0,138,0,255)
Colour.x11.Honeydew1 = color(239,255,239,255)
Colour.x11.Honeydew2 = color(224,237,224,255)
Colour.x11.Honeydew3 = color(192,205,192,255)
Colour.x11.Honeydew4 = color(130,138,130,255)
Colour.x11.HotPink1 = color(255,109,179,255)
Colour.x11.HotPink2 = color(237,105,167,255)
Colour.x11.HotPink3 = color(205,95,144,255)
Colour.x11.HotPink4 = color(138,58,98,255)
Colour.x11.IndianRed1 = color(255,105,105,255)
Colour.x11.IndianRed2 = color(237,99,99,255)
Colour.x11.IndianRed3 = color(205,84,84,255)
Colour.x11.IndianRed4 = color(138,58,58,255)
Colour.x11.Ivory1 = color(255,255,239,255)
Colour.x11.Ivory2 = color(237,237,224,255)
Colour.x11.Ivory3 = color(205,205,192,255)
Colour.x11.Ivory4 = color(138,138,130,255)
Colour.x11.Khaki1 = color(255,246,142,255)
Colour.x11.Khaki2 = color(237,229,132,255)
Colour.x11.Khaki3 = color(205,197,114,255)
Colour.x11.Khaki4 = color(138,133,77,255)
Colour.x11.LavenderBlush1 = color(255,239,244,255)
Colour.x11.LavenderBlush2 = color(237,224,228,255)
Colour.x11.LavenderBlush3 = color(205,192,196,255)
Colour.x11.LavenderBlush4 = color(138,130,133,255)
Colour.x11.LemonChiffon1 = color(255,249,205,255)
Colour.x11.LemonChiffon2 = color(237,232,191,255)
Colour.x11.LemonChiffon3 = color(205,201,165,255)
Colour.x11.LemonChiffon4 = color(138,136,112,255)
Colour.x11.LightBlue1 = color(191,238,255,255)
Colour.x11.LightBlue2 = color(177,223,237,255)
Colour.x11.LightBlue3 = color(154,191,205,255)
Colour.x11.LightBlue4 = color(104,130,138,255)
Colour.x11.LightCyan1 = color(224,255,255,255)
Colour.x11.LightCyan2 = color(209,237,237,255)
Colour.x11.LightCyan3 = color(179,205,205,255)
Colour.x11.LightCyan4 = color(122,138,138,255)
Colour.x11.LightGoldenrod1 = color(255,235,138,255)
Colour.x11.LightGoldenrod2 = color(237,220,130,255)
Colour.x11.LightGoldenrod3 = color(205,189,112,255)
Colour.x11.LightGoldenrod4 = color(138,128,75,255)
Colour.x11.LightPink1 = color(255,174,184,255)
Colour.x11.LightPink2 = color(237,161,173,255)
Colour.x11.LightPink3 = color(205,140,149,255)
Colour.x11.LightPink4 = color(138,94,100,255)
Colour.x11.LightSalmon1 = color(255,160,122,255)
Colour.x11.LightSalmon2 = color(237,149,114,255)
Colour.x11.LightSalmon3 = color(205,128,98,255)
Colour.x11.LightSalmon4 = color(138,86,66,255)
Colour.x11.LightSkyBlue1 = color(175,226,255,255)
Colour.x11.LightSkyBlue2 = color(164,211,237,255)
Colour.x11.LightSkyBlue3 = color(140,181,205,255)
Colour.x11.LightSkyBlue4 = color(95,123,138,255)
Colour.x11.LightSteelBlue1 = color(201,225,255,255)
Colour.x11.LightSteelBlue2 = color(187,210,237,255)
Colour.x11.LightSteelBlue3 = color(161,181,205,255)
Colour.x11.LightSteelBlue4 = color(109,123,138,255)
Colour.x11.LightYellow1 = color(255,255,224,255)
Colour.x11.LightYellow2 = color(237,237,209,255)
Colour.x11.LightYellow3 = color(205,205,179,255)
Colour.x11.LightYellow4 = color(138,138,122,255)
Colour.x11.Magenta1 = color(255,0,255,255)
Colour.x11.Magenta2 = color(237,0,237,255)
Colour.x11.Magenta3 = color(205,0,205,255)
Colour.x11.Magenta4 = color(138,0,138,255)
Colour.x11.Maroon1 = color(255,52,178,255)
Colour.x11.Maroon2 = color(237,48,167,255)
Colour.x11.Maroon3 = color(205,40,144,255)
Colour.x11.Maroon4 = color(138,28,98,255)
Colour.x11.MediumOrchid1 = color(224,102,255,255)
Colour.x11.MediumOrchid2 = color(209,94,237,255)
Colour.x11.MediumOrchid3 = color(179,81,205,255)
Colour.x11.MediumOrchid4 = color(122,54,138,255)
Colour.x11.MediumPurple1 = color(170,130,255,255)
Colour.x11.MediumPurple2 = color(159,121,237,255)
Colour.x11.MediumPurple3 = color(136,104,205,255)
Colour.x11.MediumPurple4 = color(93,71,138,255)
Colour.x11.MistyRose1 = color(255,227,225,255)
Colour.x11.MistyRose2 = color(237,212,210,255)
Colour.x11.MistyRose3 = color(205,182,181,255)
Colour.x11.MistyRose4 = color(138,124,123,255)
Colour.x11.NavajoWhite1 = color(255,221,173,255)
Colour.x11.NavajoWhite2 = color(237,206,160,255)
Colour.x11.NavajoWhite3 = color(205,178,138,255)
Colour.x11.NavajoWhite4 = color(138,121,94,255)
Colour.x11.OliveDrab1 = color(191,255,62,255)
Colour.x11.OliveDrab2 = color(178,237,58,255)
Colour.x11.OliveDrab3 = color(154,205,49,255)
Colour.x11.OliveDrab4 = color(104,138,33,255)
Colour.x11.Orange1 = color(255,165,0,255)
Colour.x11.Orange2 = color(237,154,0,255)
Colour.x11.Orange3 = color(205,132,0,255)
Colour.x11.Orange4 = color(138,89,0,255)
Colour.x11.OrangeRed1 = color(255,68,0,255)
Colour.x11.OrangeRed2 = color(237,63,0,255)
Colour.x11.OrangeRed3 = color(205,54,0,255)
Colour.x11.OrangeRed4 = color(138,36,0,255)
Colour.x11.Orchid1 = color(255,130,249,255)
Colour.x11.Orchid2 = color(237,122,232,255)
Colour.x11.Orchid3 = color(205,104,201,255)
Colour.x11.Orchid4 = color(138,71,136,255)
Colour.x11.PaleGreen1 = color(154,255,154,255)
Colour.x11.PaleGreen2 = color(144,237,144,255)
Colour.x11.PaleGreen3 = color(124,205,124,255)
Colour.x11.PaleGreen4 = color(84,138,84,255)
Colour.x11.PaleTurquoise1 = color(186,255,255,255)
Colour.x11.PaleTurquoise2 = color(174,237,237,255)
Colour.x11.PaleTurquoise3 = color(150,205,205,255)
Colour.x11.PaleTurquoise4 = color(102,138,138,255)
Colour.x11.PaleVioletRed1 = color(255,130,170,255)
Colour.x11.PaleVioletRed2 = color(237,121,159,255)
Colour.x11.PaleVioletRed3 = color(205,104,136,255)
Colour.x11.PaleVioletRed4 = color(138,71,93,255)
Colour.x11.PeachPuff1 = color(255,218,184,255)
Colour.x11.PeachPuff2 = color(237,202,173,255)
Colour.x11.PeachPuff3 = color(205,175,149,255)
Colour.x11.PeachPuff4 = color(138,119,100,255)
Colour.x11.Pink1 = color(255,181,196,255)
Colour.x11.Pink2 = color(237,169,183,255)
Colour.x11.Pink3 = color(205,145,158,255)
Colour.x11.Pink4 = color(138,99,108,255)
Colour.x11.Plum1 = color(255,186,255,255)
Colour.x11.Plum2 = color(237,174,237,255)
Colour.x11.Plum3 = color(205,150,205,255)
Colour.x11.Plum4 = color(138,102,138,255)
Colour.x11.Purple1 = color(155,48,255,255)
Colour.x11.Purple2 = color(145,43,237,255)
Colour.x11.Purple3 = color(124,38,205,255)
Colour.x11.Purple4 = color(84,25,138,255)
Colour.x11.Red1 = color(255,0,0,255)
Colour.x11.Red2 = color(237,0,0,255)
Colour.x11.Red3 = color(205,0,0,255)
Colour.x11.Red4 = color(138,0,0,255)
Colour.x11.RosyBrown1 = color(255,192,192,255)
Colour.x11.RosyBrown2 = color(237,179,179,255)
Colour.x11.RosyBrown3 = color(205,155,155,255)
Colour.x11.RosyBrown4 = color(138,104,104,255)
Colour.x11.RoyalBlue1 = color(72,118,255,255)
Colour.x11.RoyalBlue2 = color(67,109,237,255)
Colour.x11.RoyalBlue3 = color(58,94,205,255)
Colour.x11.RoyalBlue4 = color(38,63,138,255)
Colour.x11.Salmon1 = color(255,140,104,255)
Colour.x11.Salmon2 = color(237,130,98,255)
Colour.x11.Salmon3 = color(205,112,84,255)
Colour.x11.Salmon4 = color(138,75,57,255)
Colour.x11.SeaGreen1 = color(84,255,159,255)
Colour.x11.SeaGreen2 = color(77,237,147,255)
Colour.x11.SeaGreen3 = color(67,205,127,255)
Colour.x11.SeaGreen4 = color(45,138,86,255)
Colour.x11.Seashell1 = color(255,244,237,255)
Colour.x11.Seashell2 = color(237,228,221,255)
Colour.x11.Seashell3 = color(205,196,191,255)
Colour.x11.Seashell4 = color(138,133,130,255)
Colour.x11.Sienna1 = color(255,130,71,255)
Colour.x11.Sienna2 = color(237,121,66,255)
Colour.x11.Sienna3 = color(205,104,57,255)
Colour.x11.Sienna4 = color(138,71,38,255)
Colour.x11.SkyBlue1 = color(135,206,255,255)
Colour.x11.SkyBlue2 = color(125,191,237,255)
Colour.x11.SkyBlue3 = color(108,165,205,255)
Colour.x11.SkyBlue4 = color(73,112,138,255)
Colour.x11.SlateBlue1 = color(130,110,255,255)
Colour.x11.SlateBlue2 = color(122,103,237,255)
Colour.x11.SlateBlue3 = color(104,89,205,255)
Colour.x11.SlateBlue4 = color(71,59,138,255)
Colour.x11.SlateGray1 = color(197,226,255,255)
Colour.x11.SlateGray2 = color(184,211,237,255)
Colour.x11.SlateGray3 = color(159,181,205,255)
Colour.x11.SlateGray4 = color(108,123,138,255)
Colour.x11.Snow1 = color(255,249,249,255)
Colour.x11.Snow2 = color(237,232,232,255)
Colour.x11.Snow3 = color(205,201,201,255)
Colour.x11.Snow4 = color(138,136,136,255)
Colour.x11.SpringGreen1 = color(0,255,126,255)
Colour.x11.SpringGreen2 = color(0,237,118,255)
Colour.x11.SpringGreen3 = color(0,205,102,255)
Colour.x11.SpringGreen4 = color(0,138,68,255)
Colour.x11.SteelBlue1 = color(99,183,255,255)
Colour.x11.SteelBlue2 = color(91,172,237,255)
Colour.x11.SteelBlue3 = color(79,147,205,255)
Colour.x11.SteelBlue4 = color(53,99,138,255)
Colour.x11.Tan1 = color(255,165,79,255)
Colour.x11.Tan2 = color(237,154,73,255)
Colour.x11.Tan3 = color(205,132,63,255)
Colour.x11.Tan4 = color(138,89,43,255)
Colour.x11.Thistle1 = color(255,225,255,255)
Colour.x11.Thistle2 = color(237,210,237,255)
Colour.x11.Thistle3 = color(205,181,205,255)
Colour.x11.Thistle4 = color(138,123,138,255)
Colour.x11.Tomato1 = color(255,99,71,255)
Colour.x11.Tomato2 = color(237,91,66,255)
Colour.x11.Tomato3 = color(205,79,57,255)
Colour.x11.Tomato4 = color(138,53,38,255)
Colour.x11.Turquoise1 = color(0,244,255,255)
Colour.x11.Turquoise2 = color(0,228,237,255)
Colour.x11.Turquoise3 = color(0,196,205,255)
Colour.x11.Turquoise4 = color(0,133,138,255)
Colour.x11.VioletRed1 = color(255,62,150,255)
Colour.x11.VioletRed2 = color(237,58,140,255)
Colour.x11.VioletRed3 = color(205,49,119,255)
Colour.x11.VioletRed4 = color(138,33,81,255)
Colour.x11.Wheat1 = color(255,230,186,255)
Colour.x11.Wheat2 = color(237,216,174,255)
Colour.x11.Wheat3 = color(205,186,150,255)
Colour.x11.Wheat4 = color(138,125,102,255)
Colour.x11.Yellow1 = color(255,255,0,255)
Colour.x11.Yellow2 = color(237,237,0,255)
Colour.x11.Yellow3 = color(205,205,0,255)
Colour.x11.Yellow4 = color(138,138,0,255)
Colour.x11.Gray0 = color(189,189,189,255)
Colour.x11.Green0 = color(0,255,0,255)
Colour.x11.Grey0 = color(189,189,189,255)
Colour.x11.Maroon0 = color(175,48,95,255)
Colour.x11.Purple0 = color(160,31,239,255)
--]==]
--[==[
-- Coordinate calculations
--[[
This defines various standard rectangles to be used with RectAnchorAt to
determine various anchors.
The Screen rectangle should change dynamically.
These only work with fullscreen mode, otherwise it gets a bit complicated.
Also, the fullscreen mode should be initialised before this library is
loaded.
--]]
Screen = {
0,
0,
function() return WIDTH end,
function() return HEIGHT end,
}
local _width,_height
if WIDTH > HEIGHT then
_width = HEIGHT
_height = WIDTH
else
_width = WIDTH
_height = HEIGHT
end
-- Rectangles defining the screen
Landscape = {0,0,_height,_width}
Portrait = {0,0,_width,_height}
-- Origin, x-vector, y-vector of orientation relative to Portrait
OrientationCoordinates = {}
OrientationCoordinates[PORTRAIT] = {vec2(0,0), vec2(1,0), vec2(0,1)}
OrientationCoordinates[PORTRAIT_UPSIDE_DOWN] = {vec2(_width,_height), vec2(-1,0), vec2(0,-1)}
OrientationCoordinates[LANDSCAPE_LEFT] = {vec2(_width,0), vec2(0,1), vec2(-1,0)}
OrientationCoordinates[LANDSCAPE_RIGHT] = {vec2(0,_height), vec2(0,-1), vec2(1,0)}
-- Origin, x-vector, y-vector of Portrait relative to orientation
OrientationInverseCoordinates = {}
OrientationInverseCoordinates[PORTRAIT] = {vec2(0,0), vec2(1,0), vec2(0,1)}
OrientationInverseCoordinates[PORTRAIT_UPSIDE_DOWN] = {vec2(_width,_height), vec2(-1,0), vec2(0,-1)}
OrientationInverseCoordinates[LANDSCAPE_LEFT] = {vec2(0,_width), vec2(0,-1), vec2(1,0)}
OrientationInverseCoordinates[LANDSCAPE_RIGHT] = {vec2(_height,0), vec2(0,1), vec2(-1,0)}
-- Transformation to translate following commands to Portrait view
OrientationTransform = {}
OrientationTransform[PORTRAIT] = function() end
OrientationTransform[PORTRAIT_UPSIDE_DOWN] = function() translate(_width,_height) rotate(180) end
OrientationTransform[LANDSCAPE_LEFT] = function() translate(_width,0) rotate(90) end
OrientationTransform[LANDSCAPE_RIGHT] = function() translate(0,_height) rotate(-90) end
-- Transformation to translate following commands from Portrait view
OrientationInverseTransform = {}
OrientationInverseTransform[PORTRAIT] = function() end
OrientationInverseTransform[PORTRAIT_UPSIDE_DOWN] = function() translate(_width,_height) rotate(180) end
OrientationInverseTransform[LANDSCAPE_LEFT] = function() rotate(-90) translate(-_width,0) end
OrientationInverseTransform[LANDSCAPE_RIGHT] = function() rotate(90) translate(0,-_height) end
--[[
Resolve an ambigous orientation
--]]
function ResolveOrientation(o,oo)
if o == PORTRAIT_ANY then
if oo == PORTRAIT_UPSIDE_DOWN then
return PORTRAIT_UPSIDE_DOWN
else
return PORTRAIT
end
elseif o == LANDSCAPE_ANY then
if oo == LANDSCAPE_RIGHT then
return LANDSCAPE_RIGHT
else
return LANDSCAPE_LEFT
end
end
return o
end
--[[
Transform vector x relative to orientation o into a vector relative to
the current orientation.
--]]
function Orientation(o,x)
o = ResolveOrientation(o,CurrentOrientation)
if CurrentOrientation == o then
return x
end
local y = x.x * OrientationCoordinates[o][2]
+ x.y * OrientationCoordinates[o][3]
+ OrientationCoordinates[o][1]
local z = y.x * OrientationInverseCoordinates[CurrentOrientation][2]
+ y.y * OrientationInverseCoordinates[CurrentOrientation][3]
+ OrientationInverseCoordinates[CurrentOrientation][1]
return z
end
--[[
Transform vector x relative to the current orientation o into a
vector relative to the current orientation.
--]]
function OrientationInverse(o,x)
o = ResolveOrientation(o,CurrentOrientation)
if CurrentOrientation == o then
return x
end
local y = x.x * OrientationCoordinates[CurrentOrientation][2]
+ x.y * OrientationCoordinates[CurrentOrientation][3]
+ OrientationCoordinates[CurrentOrientation][1]
local z = y.x * OrientationInverseCoordinates[o][2]
+ y.y * OrientationInverseCoordinates[o][3]
+ OrientationInverseCoordinates[o][1]
return z
end
function Orientations(o,oo,x)
o = ResolveOrientation(o,oo)
oo = ResolveOrientation(oo,o)
if oo == o then
return x
end
local y = x.x * OrientationCoordinates[o][2]
+ x.y * OrientationCoordinates[o][3]
+ OrientationCoordinates[o][1]
local z = y.x * OrientationInverseCoordinates[oo][2]
+ y.y * OrientationInverseCoordinates[oo][3]
+ OrientationInverseCoordinates[oo][1]
return z
end
function TransformTouch(o,t)
o = ResolveOrientation(o,CurrentOrientation)
local tt = {}
local ofn
if type(o) == "function" then
ofn = o
else
ofn = function(u) return OrientationInverse(o,u) end
end
local v = ofn(t)
tt.x = v.x
tt.y = v.y
v = ofn(vec2(t.prevX,t.prevY))
tt.prevX = v.x
tt.prevY = v.y
tt.deltaX = tt.x - v.x
tt.deltaY = tt.y - v.y
for _,u in ipairs({"state","tapCount","id"}) do
tt[u] = t[u]
end
return tt
end
--[[
Apply a transformation to transform the following drawing commands
relative to orientation o into commands relative to the current
orientation.
--]]
function TransformOrientation(o)
o = ResolveOrientation(o,CurrentOrientation)
if CurrentOrientation == o then
return
end
OrientationInverseTransform[CurrentOrientation]()
OrientationTransform[o]()
end
--[[
Apply a transformation to transform the following drawing commands
relative to the current orientation into commands relative to the
specified orientation.
--]]
function TransformInverseOrientation(o)
o = ResolveOrientation(o,CurrentOrientation)
if CurrentOrientation == o then
return
end
OrientationInverseTransform[o]()
OrientationTransform[CurrentOrientation]()
end
function TransformOrientations(o,oo)
o = ResolveOrientation(o,oo)
oo = ResolveOrientation(oo,o)
if oo == o then
return
end
OrientationInverseTransform[o]()
OrientationTransform[oo]()
end
--[[
This is an auxilliary function used by the "Textarea" class which
works out the coordinate of a rectangle as specified by an "anchor"
(the notions used here are inspired by the "TikZ/PGF" LaTeX package).
This returns the lower-left coordinate of a rectangle of width w
and height h so that the anchor is at the specified x,y coordinate.
--]]
function RectAnchorAt(x,y,w,h,a,A)
if type(x) == "table" then
a = y
x,y,w,h,A = unpack(x)
end
if type(x) == "function" then
x = x()
end
if type(y) == "function" then
y = y()
end
if type(w) == "function" then
w = w()
end
if type(h) == "function" then
h = h()
end
-- (x,y) is south-west
local v = vec2(0,0)
if a == "north" then
v = vec2(w/2,h)
elseif a == "south" then
v = vec2(w/2,0)
elseif a == "east" then
v = vec2(w,h/2)
elseif a == "west" then
v = vec2(0,h/2)
elseif a == "north west" then
v = vec2(0,h)
elseif a == "south east" then
v = vec2(w,0)
elseif a == "north east" then
v = vec2(w,h)
elseif a == "south west" then
v = vec2(0,0)
elseif a == "centre" then
v = vec2(w/2,h/2)
elseif a == "center" then
v = vec2(w/2,h/2)
end
if A and A ~= 0 then
v = v:rotate(math.rad(A))
end
return x - v.x,y - v.y
end
--[[
This is the reverse of the above: it gives the location of the anchor
on the rectangle of width w and height h with lower-left corner at x,y.
--]]
function RectAnchorOf(x,y,w,h,a)
if type(x) == "table" then
a = y
x,y,w,h = unpack(x)
end
--- "bake" the coordinates
if type(x) == "function" then
x = x()
end
if type(y) == "function" then
y = y()
end
if type(w) == "function" then
w = w()
end
if type(h) == "function" then
h = h()
end
-- not quite anchors, more dimensions
if a == "x" then
return x
end
if a == "y" then
return y
end
if a == "width" then
return w
end
if a == "height" then
return h
end
if a == "size" then
return w,h
end
-- (x,y) is south-west
if a == "north" then
return x + w/2, y + h
end
if a == "south" then
return x + w/2, y
end
if a == "east" then
return x + w, y + h/2
end
if a == "west" then
return x, y + h/2
end
if a == "north west" then
return x, y + h
end
if a == "south east" then
return x + w, y
end
if a == "north east" then
return x + w, y + h
end
if a == "south west" then
return x, y
end
if a == "centre" then
return x + w/2, y + h/2
end
if a == "center" then
return x + w/2, y + h/2
end
return x,y
end
--[[
This is for positioning text at a location, given by an anchor, but with an extra separation. The width and height are of the text.
--]]
function TextAnchorAt(x,y,w,h,a,s)
x,y = RectAnchorAt(x,y,w+2*s,h+2*s,a)
return x+s,y+s
end
-- Should use RectAnchorOf(__,"width/height")
function WidthOf(t)
if type(t[3]) == "function" then
return t[3]()
else
return t[3]
end
end
function HeightOf(t)
if type(t[4]) == "function" then
return t[4]()
else
return t[4]
end
end
function RectTouchedBy(x,y,w,h,t)
if type(x) == "table" then
t = y
x,y,w,h = unpack(x)
end
--- "bake" the coordinates
if type(x) == "function" then
x = x()
end
if type(y) == "function" then
y = y()
end
if type(w) == "function" then
w = w()
end
if type(h) == "function" then
h = h()
end
if t.x < x or t.x > x + w then
return false
end
if t.y < y or t.y > y + h then
return false
end
return true
end
cmodule.gexport {
Screen = Screen,
Landscape = Landscape,
Portrait = Portrait,
OrientationCoordinates = OrientationCoordinates,
OrientationInverseCoordinates = OrientationInverseCoordinates,
OrientationTransform = OrientationTransform,
OrientationInverseTransform = OrientationInverseTransform,
ResolveOrientation = ResolveOrientation,
Orientation = Orientation,
OrientationInverse = OrientationInverse,
Orientations = Orientations,
TransformTouch = TransformTouch,
TransformOrientation = TransformOrientation,
TransformInverseOrientation = TransformInverseOrientation,
TransformOrientations = TransformOrientations,
RectAnchorAt = RectAnchorAt,
RectAnchorOf = RectAnchorOf,
TextAnchorAt = TextAnchorAt,
WidthOf = WidthOf,
HeightOf = HeightOf,
RectTouchedBy = RectTouchedBy
}
--]==]
--[==[
-- Debug
Debug = class()
local Colour = unpack(cimport "Colour",nil)
local Font,_,Textarea = unpack(cimport "Font",nil)
cimport "Menu"
cimport "ColourNames"
function Debug:init(t)
t = t or {}
-- you can accept and set parameters here
self.textarea = Textarea({
font = Font({name = "Courier", size = 32}),
width = WIDTH,
height = HEIGHT,
colour = Colour.opacity(Colour.svg.Black,25),
textColour = Colour.opacity(Colour.svg.White,25),
--pos = Screen["centre"],
title = "Debug",
--anchor = "centre"
})
self.messages = {}
self.fps = {}
self.fpsn = 100
for i=1,self.fpsn do
self.fps[i] = 60
end
self.fpsi = 1
self:log({
name = "IFPS",
message = function() return math.floor(1/DeltaTime*10)/10 end
})
self:log({
name = "AFPS",
message = function()
self.fps[self.fpsi] = 1/DeltaTime
local tfps = 0
for i=1,self.fpsn do
tfps = tfps + self.fps[i]
end
self.fpsi = self.fpsi%self.fpsn + 1
return math.floor(tfps/self.fpsn*10)/10 end
})
if t.ui then
local m = t.ui:addMenu({
title = "Debug",
attach = true,
atEnd = true
})
m:addItem({
title = "Debugging",
action = function()
if self.active then
self:deactivate()
else
self:activate()
end
return true
end,
highlight = function() return self.active end
})
end
end
function Debug:draw()
if self.active then
pushStyle()
pushMatrix()
resetStyle()
resetMatrix()
self.textarea:setLines(self:processMessages())
self.textarea:draw()
popMatrix()
popStyle()
end
end
function Debug:log(t)
table.insert(self.messages,t)
end
function Debug:processMessages()
local t = {}
for k,v in ipairs(self.messages) do
if type(v.message) == "function" then
table.insert(t, v.name .. ": " .. v.message())
else
table.insert(t, v.name .. ": " .. v.message)
end
end
if displayMode() == FULLSCREEN then
table.insert(t," ")
end
return unpack(t)
end
function Debug:activate()
self.active = true
self.textarea:activate()
end
function Debug:deactivate()
self.active = false
self.textarea:deactivate()
end
function Debug:orientationChanged(o)
self.textarea:setSize({width = WIDTH, height = HEIGHT})
end
function Debug:hide()
self.pstate = self.active
if self.active then
self:deactivate()
end
end
function Debug:unhide()
if self.pstate then
self:activate()
end
end
return Debug
--]==]
--[==[
-- Bitmap font class
-- Author: Andrew Stacey
-- Website: http://www.math.ntnu.no/~stacey/HowDidIDoThat/iPad/Codea.html
-- Licence: CC0 http://wiki.creativecommons.org/CC0
--[[
The "Font", "Sentence", "Char", and "Textarea" classes are all
designed to aid getting text onto the screen. Their purposes are as
follows:
"Font": This contains a list of characters which make up a "font".
The methods are to do with rendering these characters on the screen in
a reasonable way (ensuring that characters line up correctly, for
example).
"Sentence": This class holds a single line of text to be drawn in a
given font. Its purpose is that it be used when a string is to be
rendered several time (for example, over the course of many draw
iterations) as it saves various pieces of information needed to render
it to the screen to avoid computing them every time.
"Char": This class contains the information about a single character.
The initial information is based on the BDF font format; the first
time a Char object is drawn then this is converted to a sprite which
is then used on subsequent occasions. In particular, sprites are only
created for those characters that are actually used.
"Textarea": This is a box containing lines which can be added to to
present messages to the user. It handles line wrapping and scrolling,
and can be moved or hidden (except for the title).
Strings passed to the objects defined using these classes are assumed
to be in utf8 format (using the "utf8" functionality from the
corresponding file). However, to get the full use of this one should
use a font supporting all the necessary characters.
--]]
local UTF8 = cimport "utf8"
local Colour = unpack(cimport "Colour",nil)
cimport "RoundedRectangle"
cimport "Lengths"
cimport "Coordinates"
cimport "ColourNames"
local Font = class()
local BitmapFont = class()
local InternalFont = class()
--[[
The "Fonts" table contains functions that define the fonts used (we
use functions so that fonts are only processed if they are actually
used).
--]]
local Fonts = {}
--[[
A "Font" is just a list of characters.
--]]
function Font:init(t)
local mf
if t then
mf = InternalFont(t)
else
mf = BitmapFont()
end
for k,v in pairs(mf) do
self[k] = v
end
local mt = getmetatable(mf)
setmetatable(self,mt)
end
function BitmapFont:init()
self.char = {}
end
function InternalFont:init(t)
self.name = t.name
self.size = t.size
self.tint = t.tint
pushStyle()
font(t.name)
fontSize(t.size)
local fm = fontMetrics()
local w,h = textSize("x")
self.bbx = {w,h}
self.descent = fm.descent
popStyle()
end
function BitmapFont:clone()
return self
end
function InternalFont:clone(t)
t = t or {}
t.name = t.name or self.name
t.size = t.size or self.size
t.tint = t.tint or self.tint
return Font(t)
end
--[[
This returns the line height (bounding box height) of the font.
--]]
function BitmapFont:lineheight()
return self.bbx[2]
end
function InternalFont:lineheight()
return self.bbx[2]
end
--[[
This returns the default character width (individual characters may
override this).
--]]
function BitmapFont:charWidth(c)
if c then
c = tostring(c)
c = UTF8(c):firstchar()
if self.char[c] then
return self.char[c].dwidth[1]
end
end
return self.bbx[1]
end
function InternalFont:charWidth(c)
if c then
pushStyle()
self:setstyle()
local w,_ = textSize(c)
popStyle()
return w
else
return self.bbx[1]
end
end
--[[
This sets the colour for the font.
--]]
function BitmapFont:setColour(c)
self.tint = c
end
function InternalFont:setColour(c)
self.tint = c
end
--[[
Builtin and rendered fonts have different methods of setting colours
--]]
function BitmapFont:colour(c)
tint(c)
end
function InternalFont:colour(c)
fill(c)
end
--[[
and of how to put a character on the screen
--]]
function BitmapFont:render_char(c,x,y)
sprite(c,x,y)
end
function InternalFont:render_char(c,x,y)
text(c,x,y)
end
--[[
and of what styles should apply
--]]
function BitmapFont:setstyle()
resetStyle()
noSmooth()
spriteMode(CORNER)
end
function InternalFont:setstyle()
resetStyle()
textMode(CORNER)
font(self.name)
fontSize(self.size)
end
function BitmapFont:exh()
return self.char[109].bbx[2]
end
function InternalFont:exh()
pushStyle()
self:setstyle()
local fm = fontMetrics()
popStyle()
return fm.xHeight
end
--[[
This is the basic method for writing a string to the screen. Its
inputs are a string (assumed to be utf8), the xy coordinates to start
at (being the start of the "writing line"), and the colour to use
(this is optional).
It parses the string, drawing each character in turn. The "draw_char"
function returns the xy coordinate at which to position the next
characters.
--]]
function BitmapFont:write(s,x,y,col)
x = math.floor(x + .5)
y = math.floor(y + .5)
s = tostring(s)
local u = UTF8(s)
pushStyle()
self:setstyle()
if col then
tint(col)
elseif self.tint then
tint(self.tint)
end
for c in u:chars() do
x,y = self:draw_char(c,x,y)
end
popStyle()
return x,y
end
function InternalFont:write(s,x,y,col)
x = math.floor(x + .5)
y = math.floor(y + .5)
s = tostring(s)
pushStyle()
self:setstyle()
if col then
fill(col)
elseif self.tint then
fill(self.tint)
end
local w,_ = textSize(s)
text(s,x,y - self.descent)
popStyle()
return x + w,y
end
--[[
This is the same as the "write" method except that we assume that the
input is a list of (decimal) numbers specifying characters via the
utf8 encoding. The input can either be a single number or a table.
--]]
function BitmapFont:write_utf8(s,x,y,col)
x = math.floor(x + .5)
y = math.floor(y + .5)
pushStyle()
self:setstyle()
if col then
tint(col)
elseif self.tint then
tint(self.tint)
end
if type(s) == "table" then
for k,c in ipairs(s) do
x,y = self:draw_char(c,x,y)
end
else
x,y = self:draw_char(s,x,y)
end
popStyle()
return x,y
end
function InternalFont:write_utf8(s,x,y,col)
x = math.floor(x + .5)
y = math.floor(y + .5)
if type(s) == "table" then
s = s:tostring()
else
s = utf8dec(s)
end
pushStyle()
self:setstyle()
if col then
fill(col)
elseif self.tint then
fill(self.tint)
end
local w,_ = textSize(s)
text(s,x,y - self.descent)
popStyle()
return x + w,y
end
--[[
Quick versions of the above: don't set the style
--]]
function BitmapFont:quick_write(s,x,y)
s = tostring(s)
local u = UTF8(s)
for c in u:chars() do
x,y = self:draw_char(c,x,y)
end
return x,y
end
function InternalFont:quick_write(s,x,y)
local w,_ = textSize(s)
text(s,x,y - self.descent)
return x + w,y
end
--[[
This is the same as the "write" method except that we assume that the
input is a list of (decimal) numbers specifying characters via the
utf8 encoding. The input can either be a single number or a table.
--]]
function BitmapFont:quick_write_utf8(s,x,y)
if type(s) == "table" then
for k,c in ipairs(s) do
x,y = self:draw_char(c,x,y)
end
else
x,y = self:draw_char(s,x,y)
end
return x,y
end
function InternalFont:quick_write_utf8(s,x,y)
if type(s) == "table" then
s = s:tostring()
else
s = utf8dec(s)
end
local w,_ = textSize(s)
text(s,x,y - self.descent)
return x + w,y
end
--[[
This function calls the "draw" function of a character at the
specified xy coordinate and works out the place to put the next
character from the bounding box of the current one.
--]]
function BitmapFont:draw_char(c,x,y)
x = math.floor(x + .5)
y = math.floor(y + .5)
c = tonumber(c)
if self.char[c] then
local ch,cx,cy
ch = self.char[c]
cx = x + ch.bbx[3]
cy = y + ch.bbx[4]
ch:draw(cx,cy)
x = x + ch.dwidth[1]
y = y + ch.dwidth[2]
end
return x,y
end
function InternalFont:draw_char(c,x,y)
x = math.floor(x + .5)
y = math.floor(y + .5)
c = tonumber(c)
c = string.char(c)
return self:write(c,x,y)
end
--[[
There are various places in the drawing method where information is
saved for use next time round. This does all the processing involved
without actually drawing the characters. It is useful for getting the
width of a string before rendering it to the screen.
--]]
function BitmapFont:prepare_char(c,x,y)
x = math.floor(x + .5)
y = math.floor(y + .5)
c = tonumber(c)
if self.char[c] then
local ch,cx,cy,nx,ny
ch = self.char[c]
cx = ch.bbx[3]
cy = ch.bbx[4]
ch:prepare()
nx = ch.dwidth[1]
ny = ch.dwidth[2]
return {ch.image,x,y,cx,cy,nx,ny},x + nx,y + ny
else
return {},x,y
end
end
function InternalFont:prepare_char(c,x,y)
x = math.floor(x + .5)
y = math.floor(y + .5)
pushStyle()
self:setstyle()
c = utf8dec(c)
local w,h = textSize(c)
popStyle()
return {c,x,y,0,- self.descent,w,0},x+w,y
end
--[[
A sentence is a string to be written to the screen in a specified
font. The purpose of this class is to ensure that repeated
calculations are only carried out once and then their values saved.
--]]
local Sentence = class()
--[[
A sentence consists of a UTF8 object, a font, the characters making
up the string (drawn from the font) and some information about how
much space the sentence takes up on the screen.
--]]
function Sentence:init(f,s,t)
self.font = f
self.chars = {}
self.width = 0
self.lastx = 0
self.lasty = 0
if type(s) == "string" then
self.utf8 = UTF8(s)
elseif type(s) == "table" and s.is_a and s:is_a(UTF8) then
self.utf8 = s:clone()
elseif type(s) == "table" and s.is_a and s:is_a(Sentence) then
self.utf8 = s.utf8:clone()
if t then
local fn = {}
fn.name = s.font.name
fn.size = s.font.size
for _,v in pairs(t) do
fn[v] = f[v]
end
self.font = Font(fn)
self.prepared = false
else
self.font = s.font
if s.prepared then
self.length = s.length
local c
for k=1,s.length do
c = s.chars[k]
table.insert(self.chars,
{c[1],c[2],c[3],c[4],c[5],c[6],c[7]})
end
end
self.prepared = s.prepared
self.width = s.width
self.lastx = s.lastx
self.lasty = s.lasty
end
self.colour = s.colour
elseif type(s) == "number" then
self.utf8 = UTF8(tostring(s))
else
self.utf8 = UTF8("")
end
end
--[[
This sets our string. As this is probably new, we will need to parse
it afresh so we unset the "prepared" flag.
--]]
function Sentence:setString(s)
if type(s) == "string" then
self.utf8 = UTF8(s)
elseif type(s) == "table" and s:is_a(UTF8) then
self.utf8 = s
elseif type(s) == "number" then
self.utf8 = UTF8(tostring(s))
else
self.utf8 = UTF8("")
end
self.prepared = false
self.chars = {}
self.width = 0
self.lastx = 0
self.lasty = 0
self.length = 0
end
--[[
This returns our current string.
--]]
function Sentence:getString()
return self.utf8:tostring()
end
--[[
This returns our current string as a UTF8 object.
--]]
function Sentence:getUTF8()
return self.utf8
end
function Sentence:toupper()
self.utf8:toupper()
self.prepared = false
end
--[[
This appends the given string to our stored string. If we have
already processed our stored string we process the new string as well
(thus ensuring that we only process the new information).
--]]
function Sentence:appendString(u)
if type(u) == "string" then
u = UTF8(u)
end
self.utf8:append(u)
if self.prepared then
local t,x,y,n
n = self.length
x = self.lastx
y = self.lasty
for c in u:chars() do
t,x,y = self.font:prepare_char(c,x,y)
if t[1] then
table.insert(self.chars,t)
n = n + 1
end
end
self.width = x
self.lastx = x
self.lasty = y
self.length = n
end
end
--[[
This is the same as "appendString" except that it prepends the string.
--]]
function Sentence:prependString(u)
if type(u) == "string" then
u = UTF8(u)
end
self.utf8:prepend(u)
if self.prepared then
local t,x,y,l
x = 0
y = 0
l = 0
for c in u:chars() do
t,x,y = self.font:prepare_char(c,x,y)
if t[1] then
table.insert(self.chars,t,1)
l = l + 1
end
end
self.length = self.length + l
for k = l+1,self.length do
self.chars[2] = self.chars[2] + x
self.chars[3] = self.chars[3] + y
end
self.width = self.width + x
self.lastx = self.lastx + x
self.lasty = self.lasty + y
end
end
--[[
In this case, the argument is another instance of the Sentence class
and the new one is appended to the current one.
--]]
function Sentence:append(s)
if self.prepared and s.prepared then
for k,v in ipairs(s.chars) do
v[2] = v[2] + self.lastx
v[3] = v[3] + self.lasty
table.insert(self.chars,v)
end
self.utf8:append(s.utf8)
self.width = self.width + s.width
self.lastx = self.lastx + s.lastx
self.lasty = self.lasty + s.lasty
self.length = self.length + s.length
return
elseif self.prepared then
self:appendString(s.utf8)
return
else
self.utf8:append(s.utf8)
end
end
--[[
Same as "append" except with prepending.
--]]
function Sentence:prepend(s)
if self.prepared and s.prepared then
for k,v in ipairs(self.chars) do
v[2] = v[2] + s.lastx
v[3] = v[3] + s.lasty
end
for k,v in ipairs(s.chars) do
table.insert(self.chars,k,v)
end
self.utf8:prepend(s.utf8)
self.width = self.width + s.width
self.lastx = self.lastx + s.lastx
self.lasty = self.lasty + s.lasty
self.length = self.length + s.length
return
elseif self.prepared then
self:prependString(s.utf8)
return
else
self.utf8:prepend(s.utf8)
end
end
--[[
The "push", "unshift", "pop", and "shift" functions are for removing
and inserting characters at the start and end of the Sentence. The
input for "push" and "unshift" is precisely that returned by "pop" and
"shift". If you need to know the exact make-up of this input then you
are probably using the wrong function and should use something like
"append" or "appendString" instead. The idea is that if one Sentence
has worked out the information required for a particular character
then there is no need for the other Sentence to work it out for
itself, so all of that information is passed with the character.
--]]
function Sentence:push(t)
if self.prepared then
if t[2] then
t[2][2] = t[2][2] + self.lastx
t[2][3] = t[2][3] + self.lasty
table.insert(self.chars,t[2])
self.width = self.width + t[2][6]
self.lastx = self.lastx + t[2][6]
self.lasty = self.lasty + t[2][7]
self.length = self.length + 1
end
end
if t[1] then
self.utf8:append(t[1])
end
end
function Sentence:unshift(t)
if self.prepared then
if t[2] then
if self.chars[1] then
t[2][2] = self.chars[1][2] - t[2][6]
t[2][3] = self.chars[1][3] - t[2][7]
end
table.insert(self.chars,1,t[2])
self.width = self.width + t[2][6]
self.lastx = self.lastx + t[2][6]
self.lasty = self.lasty + t[2][7]
self.length = self.length + 1
end
end
if t[1] then
self.utf8:prepend(t[1])
end
end
--[[
This sets our colour.
--]]
function Sentence:setColour(c)
self.colour = c
end
--[[
This sets our anchor
--]]
function Sentence:setAnchor(a)
self.anchor = a
end
--[[
This prepares the Sentence for rendering, stepping along the sentence
and working out what characters will be required and their relative
positions.
--]]
function Sentence:prepare()
if not self.prepared then
local t,x,y,n
n = 0
x = 0
y = 0
self.chars = {}
for c in self.utf8:chars() do
t,x,y = self.font:prepare_char(c,x,y)
if t[1] then
table.insert(self.chars,t)
n = n + 1
end
end
self.prepared = true
self.width = x
self.lastx = x
self.lasty = y
self.length = n
end
end
--[[
This is the function that actually draws the Sentence (or rather,
which calls the "draw" function of each of the characters). The
Sentence is meant to be anchored at (0,0) (so that when actually drawn
it is anchored at the given xy coordinate) but this might not be the
case due to some "shift" and "unshift" operations. To avoid
recalculating the offset each time, we allow for it here and measure
the relative offset of the first character, adjusting all other
characters accordingly.
--]]
function Sentence:draw(x,y,c,A)
local lx,ly
lx = 0
ly = 0
c = c or self.colour
pushStyle()
self.font:setstyle()
if c then
self.font:setColour(c)
end
self:prepare()
if self.anchor then
x,y =TextAnchorAt(
x,y,
self.width,
self.font:lineheight(),
self.anchor,
self.font:exh()/2,
A
)
end
if self.chars[1] then
lx = self.chars[1][2]
ly = self.chars[1][3]
if lx ~= 0 or ly ~= 0 then
for k,v in ipairs(self.chars) do
v[2] = v[2] - lx
v[3] = v[3] - ly
end
end
end
pushMatrix()
translate(x,y)
if A then
rotate(A)
end
self.font:write_utf8(self.utf8,0,0)
popMatrix()
--[[
for k,v in ipairs(self.chars) do
v[2] = v[2] - lx
v[3] = v[3] - ly
self.font:render_char(v[1],v[2] + v[4] + x,v[3] + v[5] + y)
end
--]]
popStyle()
return self.lastx + x, self.lasty + y
end
--[[
This resets us to a "blank slate".
--]]
function Sentence:clear()
self.utf8 = UTF8("")
self.chars = {}
self.width = 0
self.lastx = 0
self.lasty = 0
self.length = 0
end
--[[
See the comments before the "push" and "unshift" functions.
--]]
function Sentence:pop()
local a,b
a = table.remove(self.chars)
if a then
self.width = self.width - a[6]
self.lastx = self.lastx - a[6]
self.lasty = self.lasty - a[7]
b = self.utf8:sub(-1,-1)
self.utf8 = self.utf8:sub(1,-2)
self.length = self.length - 1
else
self.width = 0
self.lastx = 0
self.lasty = 0
self.length = 0
self.utf8 = UTF8("")
end
return {b,a}
end
function Sentence:shift()
local a
a = table.remove(self.chars,1)
if a then
self.width = self.width - a[6]
self.lastx = self.lastx - a[6]
self.lasty = self.lasty - a[7]
b = self.utf8:sub(1,1)
self.utf8 = self.utf8:sub(2,-1)
self.length = self.length - 1
else
self.width = 0
self.lastx = 0
self.lasty = 0
self.length = 0
self.utf8 = UTF8("")
end
return {b,a}
end
function Sentence:splitBy(w)
self:prepare()
local i = 1
local l = self.length
return function()
if i > l then
return nil
end
local p,q,j,ex,br,ps
ex = self.chars[i][2] + w
for k=i,l do
if self.utf8:char(k) == 10 then
p = k - 1
q = k + 1
br = true
break
end
if self.chars[k][2] > ex then
br = true
if q then
break
end
end
if self.utf8:char(k) == 32 then
q = k + 1
p = ps or i + 1
if k == l then
p = p + 1
end
if br then
break
end
else
ps = k
end
end
if not br or not q then
q = l + 1
p = l
end
j = i
i = q
return self:sub(j,p)
end
end
function Sentence:sub(i,j)
self:prepare()
if i < 0 then
i = i + l + 1
end
if j < 0 then
j = j + l + 1
end
if i == 0 then
i = 1
end
local s = Sentence(self.font)
if i <= j then
s.length = j - i + 1
s.utf8 = self.utf8:sub(i,j)
local t = self.chars[i]
local x,y = t[2],t[3]
local c
for k=i,j do
t = self.chars[k]
if t then
table.insert(s.chars,
{t[1],t[2]-x,t[3]-y,t[4],t[5],t[6],t[7]})
end
end
if t then
s.prepared = true
s.width = t[2] - x + t[6]
s.lastx = t[2] - x + t[6]
s.lasty = t[3] - y + t[7]
end
s.colour = self.colour
end
return s
end
--[[
A "Textarea" is a list of lines which are drawn on the screen in a box
with an optional title. The lines are scrolled so that, by default,
the last lines are displayed. The lines are wrapped to the width of
the area. The Textarea reacts to touches in the following way. It
can be moved by dragging the title, so long as the title stays on the
screen; a single tap moves it so that it is wholly on the screen; a
double tap either hides or shows the actual text (other than the
title); a moving touch in the main text area scrolls the text in the
opposite direction (this may change - I am not sure how intuitive this
is as yet).
--]]
local Textarea = class()
--[[
Line breaks can be hard or soft.
--]]
local HARDLINE = 1
local SOFTLINE = 2
--[[
Our initial information is a table defining a font, an xy coordinate,
our width and height in characters, an anchor, and a title.
The anchor is used to interpret the xy coordinate as a position on the
boundary of the area so that it can be positioned without needing to
compute beforehand its actual height and width (particularly as these
will depend on the font).
The width and height can be specified in terms of pixels or characters.
No units means characters (and lines). "pt", "px", "pcx" mean pixels
(the idea being to add support for physical dimensions).
We can also specify maximum values so that the box can adjust to fit
its contents.
--]]
function Textarea:init(t)
self.font = t.font
self.colour = t.colour or Colour.svg.DarkSlateBlue
self.textColour = t.textColour or Colour.svg.White
self.opos = t.pos or function() return 0,0 end
self.anchor = t.anchor
self.angle = t.angle
self.fade = t.fadeTime or 0
self.sep = 10
self.active = false
self.lh = self.font:lineheight()
self.lines = {}
self.numlines = 0
self.offset = 0
self.txtwidth = 0
self.vfill = t.vfill
self.valign = t.valign
self.fit = t.fit
if t.title then
self.title = Sentence(self.font,t.title)
self.title:prepare()
self.title:setColour(self.textColour)
end
self:setSize({
width = t.width,
height = t.height,
maxWidth = t.maxWidth or t.width,
maxHeight = t.maxHeight or t.height,
})
end
function Textarea:orientationChanged()
self:resetAnchor()
end
function Textarea:resetAnchor()
local x,y = self.opos()
if self.anchor then
self.x, self.y = RectAnchorAt(
x,y,self.width,self.height,self.anchor,self.angle)
else
self.x = x
self.y = y
end
end
function Textarea:setSize(t)
local w,h,mw,mh = t.width,t.height,
t.maxWidth or WIDTH,t.maxHeight or HEIGHT
pushStyle()
self.font:setstyle()
w = evalLength(w)
h = evalLength(h)
mw = evalLength(mw)
mh = evalLength(mh)
w = math.max(0,w)
h = math.max(0,h)
mw = math.max(0,mw)
mh = math.max(0,mh)
popStyle()
if w > mw then
w = mw
end
local adj = true
if h < self.lh + self.sep then
adj = false
h = self.lh + self.sep
end
if self.title then
h = h + self.lh
self.twidth = self.title.width + 2*self.sep
else
self.twidth = 0
end
if adj and h > mh then
h = mh
end
self.totlines = math.floor((h - self.sep)/self.lh)
if self.title then
self.totlines = self.totlines - 1
end
self.totlines = math.max(1,self.totlines)
self.height = h
self.width = w
self.mheight = mh
self.mwidth = mw
self:reflow()
self:resetAnchor()
end
--[[
This is our "draw" method which puts our information on the screen
assuming that we are "active". Exactly what is drawn depends on
whether or not we have a title and whether or not the main area has
been "hidden" so that only the title shows. If all is shown then we
figure out which lines to show and print only those. This depends on
the total number of lines, the number of lines that we can show, and
the "offset", which is usually specified by a touch.
--]]
function Textarea:draw()
if not self.active then
return
end
local col, tcol
if self.deactivationTime then
local dt = ElapsedTime - self.deactivationTime
if dt > self.fade then
self.active = false
return
else
--[[
local t = (1-dt/self.fade)*200
if t > 100 then
t = t - 100
col = Colour.shade(self.colour,t)
tcol = Colour.shade(self.textColour,t)
else
col = Colour.opacity(Colour.svg.Black,t)
tcol = Colour.transparent
end
--]]
local t = (1-dt/self.fade)*100
col = Colour.opacity(self.colour,t)
tcol = Colour.opacity(self.textColour,t)
end
end
if self.activationTime then
local dt = ElapsedTime - self.activationTime
if dt > self.fade then
self.activationTime = nil
else
--[[
local t = (dt/self.fade)*200
if t > 100 then
t = t - 100
col = Colour.shade(self.colour,t)
tcol = Colour.shade(self.textColour,t)
else
col = Colour.opacity(Colour.svg.Black,t)
tcol = Colour.transparent
end
--]]
local t = (dt/self.fade)*100
col = Colour.opacity(self.colour,t)
tcol = Colour.opacity(self.textColour,t)
end
end
col = col or self.colour
tcol = tcol or self.textColour
pushStyle()
local m,n,y,lh,x,v,A,sv
lh = self.lh
A = self.angle or 0
A = math.rad(A)
v = vec2(self.sep,self.sep):rotate(A)
x = self.x + v.x
y = self.y + v.y
v = vec2(0,lh):rotate(A)
sv = vec2(1,0):rotate(A)
n = self.numlines - self.offset
if n < 0 then
n = self.totlines
end
m = n - self.totlines + 1
if m < 1 then
if self.vfill and not self.fit then
y = y - (m - 1) * v.y
x = x - (m - 1) * v.x
elseif self.valign and not self.fit then
if self.valign == "top" then
y = y - (m - 1) * v.y
x = x - (m - 1) * v.x
elseif self.valign == "middle" then
y = y - (m - 2) * v.y/2
x = x - (m - 2) * v.x/2
end
end
m = 1
end
fill(col)
if self.onlyTitle then
if self.title then
RoundedRectangle(self.x,
self.y + self.height - self.lh - self.sep,
self.twidth,
self.lh + self.sep,
self.sep,
0,
self.angle)
else
self.active = false
end
else
RoundedRectangle(
self.x,self.y,self.width,self.height,
self.sep,0,self.angle)
for k = n,m,-1 do
if self.lines[k] then
self.lines[k][1]:draw(x,y,tcol,self.angle)
if self.lines[k][2] then
y = y + v.y
x = x + v.x
else
y = y + sv.y*self.lines[k][1].width
x = x + sv.x*self.lines[k][1].width
end
end
end
end
if self.title then
v = vec2(0,self.height - self.lh):rotate(A)
x,y = self.x + v.x,self.y + v.y
self.title:draw(x,y,tcol,self.angle)
end
end
--[[
This adds a line or lines to the stack. The lines are wrapped (using
code based on contributions to the lua-users wiki) to the Textarea
width with breaks inserted at spaces (if possible).
--]]
function Textarea:addLine(...)
local w,u,sw
w = self.mwidth - 2*self.sep
if self.numlines > 0 then
sw = self.width - 2*self.sep
else
sw = 0
end
for k,v in ipairs(arg) do
u = Sentence(self.font,v)
u:setColour(self.textColour)
u:prepare()
for s in u:splitBy(w) do
sw = math.max(sw,s.width)
table.insert(self.lines,{s,SOFTLINE})
self.numlines = self.numlines + 1
end
if u.utf8:lastchar() == 32 then
self.lines[self.numlines][2] = HARDLINE
end
end
if self.fit then
self.width = sw + 2*self.sep
local h = math.min(self.mheight,
self.numlines * self.lh + self.sep)
local adj = true
if h < self.lh + self.sep then
adj = false
h = self.lh + self.sep
end
if self.title then
h = h + self.lh
end
if adj and h > self.mheight then
h = self.mheight
end
self.totlines = math.floor((h - self.sep)/self.lh)
if self.title then
self.totlines = self.totlines - 1
end
self.totlines = math.max(1,self.totlines)
self.height = h
self:resetAnchor()
end
end
function Textarea:setLines(...)
self.lines = {}
self.numlines = 0
self:addLine(...)
end
function Textarea:addChar(c)
local s
if self.numlines > 0 then
s = self.lines[self.numlines][1]:getUTF8()
s:append(UTF8(c))
self.lines[self.numlines] = nil
self.numlines = self.numlines - 1
else
s = UTF8(c)
end
self:addLine(s)
end
function Textarea:delChar()
if self.numlines > 0 then
local a = self.lines[self.numlines][1]:pop()
if not a[2] then
self.lines[self.numlines] = nil
self.numlines = self.numlines - 1
end
end
end
function Textarea:reflow()
if self.numlines > 0 then
local s,l
s = UTF8()
for i=1,self.numlines do
l = self.lines[i][1]:getUTF8()
if self.lines[i][2] == SOFTLINE then
l:chomp()
l:append(UTF8(" "))
elseif self.lines[i][2] == HARDLINE then
l:chomp()
l:append(UTF8("\n"))
end
s:append(l)
end
self:setLines(s)
end
end
--[[
This figures out if the touch was inside our "bounding box" and claims
it if it was.
--]]
function Textarea:isTouchedBy(t)
if not self.active or self.touchdisabled then
return false
end
t = vec2(t.x,t.y) - vec2(self.x,self.y)
if self.angle then
t = t:rotate(-math.rad(self.angle))
end
if t.x < 0 then
return false
end
if t.x > self.width then
return false
end
if self.onlyTitle then
if t.y < self.height - self.lh - self.sep then
return false
end
else
if t.y < 0 then
return false
end
end
if t.y > self.height then
return false
end
return true
end
function Textarea:disableTouches()
self.touchdisabled = true
end
--[[
This is our touch processor that figures out what action to take
according to the touch information available. The currently
understood gestures are:
Single tap: move so that the whole area is visible.
Double tap: toggle display of the main area (title is always shown).
Move on title: moves the text area around the screen, ensuring that
the title is always visible.
Move on main: scrolls the text.
--]]
function Textarea:processTouches(g)
local t = g.touchesArr[1]
local ty = t.touch.y
local y = self.y
local h = self.height
local lh = self.lh
if t.touch.state == BEGAN and self.title then
if ty > y + h - lh then
g.type.onTitle = true
end
end
if g.type.tap then
if g.type.finished then
if g.num == 1 then
self:makeVisible()
elseif g.num == 2 then
if self.onlyTitle then