Skip to content

Instantly share code, notes, and snippets.

@loopspace
Created May 20, 2013 10:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save loopspace/5611534 to your computer and use it in GitHub Desktop.
Save loopspace/5611534 to your computer and use it in GitHub Desktop.
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
self.onlyTitle = false
self.width = self.txtwidth
else
if self.title then
self.onlyTitle = true
self.txtwidth = self.width
self.width = self.twidth
else
self:deactivate()
end
end
end
end
elseif g.updated then
if g.type.onTitle then
local y = t.touch.deltaY
local x = t.touch.deltaX
self:ensureTitleVisible(x,y)
else
local tfy = t.firsttouch.y
local o = math.floor((ty - tfy)/lh)
self.offset = math.max(0,math.min(self.totlines,o))
end
end
g:noted()
if g.type.finished then
g:reset()
self.offset = 0
end
end
--[[
This function adjusts the xy coordinate to ensure that the whole
Textarea is visible.
--]]
function Textarea:makeVisible()
local x = self.x
local y = self.y
local w = self.width
local h = self.height
x = math.max(0,math.min(WIDTH - w,x))
y = math.max(0,math.min(HEIGHT - h,y))
self.x = x
self.y = y
end
--[[
This function adjusts the xy coordinate to ensure that the title
of the Textarea is visible.
--]]
function Textarea:ensureTitleVisible(x,y)
local w = self.width
local h = self.height
local lh = self.lh + self.sep
x = x + self.x
y = y + self.y
x = math.max(0,math.min(WIDTH - w,x))
y = math.max(lh - h,math.min(HEIGHT - h,y))
self.x = x
self.y = y
end
--[[
Our activation and deactivation routines
--]]
function Textarea:activate()
self.active = true
self.deactivationTime = nil
self.activationTime = ElapsedTime
end
function Textarea:deactivate()
self.activationTime = nil
self.deactivationTime = ElapsedTime
end
function Textarea:setColour(c)
self.colour = c
end
function Textarea:setTextColour(c)
self.textColour = c
for i=1,self.numlines do
self.lines[i]:setColour(c)
end
end
function Textarea:setFont(f,c)
if c then
self.textColour = c
end
self.font = f
self:reflow()
end
Textarea.help = "Text areas are used to display text. If a text area has a title then it can be moved by dragging the title. Double-tapping the box hides the body (if it has a title, that is left showing and can be double-tapped to bring the body of the text back again). Dragging inside the box scrolls the text (most of the time)."
--[[
This is a character in a font. The information is to be initially
constructed from a BDF font file. At present, this information is
specified by accessing the actual attributes. Later versions may be
able to read this information directly from the font file; at present,
the conversion is carried out by a Perl scrip.
Characters are rendered as sprites so the first time that a character
is drawn (or otherwise processed) then it converts the raw information
into a sprite which is then used on subsequent calls.
--]]
local Char = class()
function Char:init()
self.bitmap = {}
self.bbx = {}
self.dwidth = {}
end
--[[
This is a wrapper around the preparation function for the character so
that we only call that once.
--]]
function Char:prepare()
if not self.prepared then
self:mkImage()
end
self.prepared = true
end
--[[
This is the function that renders the BDF information to a sprite.
--]]
function Char:mkImage()
self.image = image(self.font.bbx[1], self.font.bbx[2])
local i,j,b
j = self.bbx[2]
for k,v in pairs(self.bitmap) do
i = 1
b = Hex2Bin(v)
for l in string.gfind(b, ".") do
if l == "1" then
self.image:set(i,j,255,255,255)
end
i = i + 1
end
j = j - 1
end
end
--[[
This draws the character at the specified coordinate.
--]]
function Char:draw(x,y)
self:prepare()
sprite(self.image,x,y)
end
return {Font, Sentence, Textarea, Char}
--]==]
VERSION = 2.1
clearProjectData()
-- DEBUG = true
-- Use this function to perform your initial setup
function setup()
autogist = AutoGist("Library Base","A library of classes and functions forming basic functionality.",VERSION)
autogist:backup(true)
--displayMode(FULLSCREEN_NO_BUTTONS)
cmodule "Library Base"
cmodule.path("Library Utilities")
local Touches = cimport "Touch"
local Colour = unpack(cimport "Colour",nil)
cimport "ColourNames"
touches = Touches()
end
-- This function gets called once every frame
function draw()
-- process touches and taps
touches:draw()
background(34, 47, 53, 255)
touches:show()
end
function touched(touch)
touches:addTouch(touch)
end
function orientationChanged(o)
end
function fullscreen()
end
function reset()
end
--[==[
-- Rectangle class
local __RectAnchors = {}
-- Order is l,d,r,u
__RectAnchors.lowerleft = function(v,l,d,r,u) return v.x,v.y,r,u end
__RectAnchors.southwest = __RectAnchors.lowerleft
__RectAnchors["south west"]= __RectAnchors.lowerleft
__RectAnchors["lower left"]= __RectAnchors.lowerleft
__RectAnchors.lowerright = function(v,l,d,r,u) return l,v.y,v.x,u end
__RectAnchors.southeast = __RectAnchors.lowerright
__RectAnchors["south east"]= __RectAnchors.lowerright
__RectAnchors["lower right"]= __RectAnchors.lowerright
__RectAnchors.upperleft = function(v,l,d,r,u) return v.x,d,r,v.y end
__RectAnchors.northwest = __RectAnchors.upperleft
__RectAnchors["north west"]= __RectAnchors.upperleft
__RectAnchors["upper left"]= __RectAnchors.upperleft
__RectAnchors.upperright = function(v,l,d,r,u) return l,d,v.x,v.y end
__RectAnchors.northeast = __RectAnchors.upperright
__RectAnchors["north east"]= __RectAnchors.upperright
__RectAnchors["upper right"]= __RectAnchors.upperright
__RectAnchors.north = function(v,l,d,r,u,s)
if l then
r = 2*v.x - l
elseif r then
l = 2*v.x - r
else
l = v.x - s.x/2
r = v.x + s.x/2
end
u = v.y
return l,d,r,u
end
__RectAnchors.top = __RectAnchors.north
__RectAnchors.south = function(v,l,d,r,u,s)
if l then
r = 2*v.x - l
elseif r then
l = 2*v.x - r
else
l = v.x - s.x/2
r = v.x + s.x/2
end
d = v.y
return l,d,r,u
end
__RectAnchors.bottom = __RectAnchors.south
__RectAnchors.west = function(v,l,d,r,u,s)
if d then
u = 2*v.y - d
elseif r then
d = 2*v.y - u
else
d = v.y - s.y/2
u = v.y + s.y/2
end
l = v.x
return l,d,r,u
end
__RectAnchors.left = __RectAnchors.west
__RectAnchors.east = function(v,l,d,r,u,s)
if d then
u = 2*v.y - d
elseif r then
d = 2*v.y - u
else
d = v.y - s.y/2
u = v.y + s.y/2
end
r = v.x
return l,d,r,u
end
__RectAnchors.right = __RectAnchors.east
__RectAnchors.centre = function(v,l,d,r,u,s)
if l then
r = 2*v.x - l
elseif r then
l = 2*v.x - r
else
l = v.x - s.x/2
r = v.x + s.x/2
end
if d then
u = 2*v.y - d
elseif u then
d = 2*v.y - u
else
d = v.y - s.y/2
u = v.y + s.y/2
end
return l,d,r,u
end
__RectAnchors.center = __RectAnchors.centre
__RectAnchors.middle = __RectAnchors.centre
local Rectangle = class()
function Rectangle:init(t)
t = t or {}
if t.size and t.ll then
-- quick clone/initialiser
self.size = t.size
self.ll = t.ll
return self
end
local s = vec2(1,1)
if t.width then
s.x = t.width
end
if t.height then
s.y = t.height
end
if t.size then
s = t.size
end
local l,r,u,d
for k,v in pairs(t) do
if __RectAnchors[k] then
l,d,r,u = __RectAnchors[k](v,l,d,r,u,s)
end
end
if l and r then
s.x = r - l
elseif r then
l = r - s.x
else
l = 0
end
if u and d then
s.y = u - d
elseif u then
d = u - s.y
else
d = 0
end
self.ll = vec2(l,d)
self.size = s
end
function Rectangle:clone()
return Rectangle(self)
end
function Rectangle:anchor(a)
if a == "width" then
return self.size.x
end
if a == "height" then
return self.size.y
end
if a == "size" then
return self.size
end
if a == "centre" then
return self.ll + .5*self.size
end
if a == "center" then
return self.ll + .5*self.size
end
if a == "south west" then
return self.ll
end
if a == "south east" then
return self.ll + vec2(self.size.x,0)
end
if a == "north west" then
return self.ll + vec2(0,self.size.y)
end
if a == "north east" then
return self.ll + self.size
end
if a == "south" then
return self.ll + vec2(.5*self.size.x,0)
end
if a == "north" then
return self.ll + vec2(.5*self.size.x,self.size.y)
end
if a == "west" then
return self.ll + vec2(0,.5*self.size.y)
end
if a == "east" then
return self.ll + vec2(self.size.x,.5*self.size.y)
end
end
function Rectangle:TransformToScreen(v)
if v then
return vec2(
(v.x - self.ll.x)/self.size.x*WIDTH,
(v.y - self.ll.y)/self.size.y*HEIGHT
)
else
scale(WIDTH/self.size.x,HEIGHT/self.size.y)
translate(-self.ll.x,-self.ll.y)
end
end
function Rectangle:TransformFromScreen(v)
if v then
return vec2(
v.x*self.size.x/WIDTH + self.ll.x,
v.y*self.size.y/HEIGHT + self.ll.y
)
else
translate(self.ll.x,self.ll.y)
scale(self.size.x/WIDTH,self.size.y/HEIGHT)
end
end
function Rectangle:TransformToOrientation(o,v)
local w,h
if o == LANDSCAPE_LEFT or o == LANDSCAPE_RIGHT then
w,h = RectAnchorOf(Landscape,"size")
else
w,h = RectAnchorOf(Portrait,"size")
end
if v then
return vec2(
(v.x - self.ll.x)/self.size.x*w,
(v.y - self.ll.y)/self.size.y*h
)
else
scale(w/self.size.x,h/self.size.y)
translate(-self.ll.x,-self.ll.y)
end
end
function Rectangle:TransformFromOrientation(o,v)
local w,h
if o == LANDSCAPE_LEFT or o == LANDSCAPE_RIGHT then
w,h = RectAnchorOf(Landscape,"size")
else
w,h = RectAnchorOf(Portrait,"size")
end
if v then
return vec2(
v.x*self.size.x/w + self.ll.x,
v.y*self.size.y/h + self.ll.y
)
else
translate(self.ll.x,self.ll.y)
scale(self.size.x/w,self.size.y/h)
end
end
function Rectangle:TransformDirToScreen(v)
if v then
return vec2(
v.x/self.size.x*WIDTH,
v.y/self.size.y*HEIGHT
)
else
scale(WIDTH/self.size.x,HEIGHT/self.size.y)
end
end
function Rectangle:TransformDirFromScreen(v)
if v then
return vec2(
v.x*self.size.x/WIDTH,
v.y*self.size.y/HEIGHT
)
else
scale(self.size.x/WIDTH,self.size.y/HEIGHT)
end
end
function Rectangle:Compose(r)
local ll = self.ll + vec2(r.ll.x*self.size.x, r.ll.y*self.size.y)
local s = vec2(r.size.x*self.size.x, r.size.y*self.size.y)
return Rectangle({ll = ll, size = s})
end
function Rectangle:Translate(v)
return Rectangle({ll = self.ll + v, size = self.size})
end
function Rectangle:ScaleAboutCentre(v)
local s
if type(v) == "number" then
s = vec2(self.size.x * v,self.size.y * v)
else
s = vec2(self.size.x * v.x,self.size.y * v.y)
end
local ll = self.ll + .5 * self.size - .5*s
return Rectangle({ll = ll, size = s})
end
function Rectangle:Scale(v)
local s
if type(v) == "number" then
s = vec2(self.size.x * v,self.size.y * v)
else
s = vec2(self.size.x * v.x,self.size.y * v.y)
end
return Rectangle({ll = self.ll, size = s})
end
function Rectangle:ScaleAboutPoint(v,p)
local px = (p.x - self.ll.x)/self.size.x
local py = (p.y - self.ll.y)/self.size.y
local s
if type(v) == "number" then
s = vec2(self.size.x * v,self.size.y * v)
else
s = vec2(self.size.x * v.x,self.size.y * v.y)
end
local ll = p - vec2(px*s.x,py*s.y)
return Rectangle({ll = ll, size = s})
end
return Rectangle
--]==]
--[==[
-- TestSuite code
testsuite = {}
testsuite.tests = {}
testsuite.testsdraw = {}
testsuite.test = "None"
testsuite.addTest = function (t)
table.insert(testsuite.tests,t)
end
testsuite.addTest({
name = "None",
draw = function() end,
setup = function() end
})
testsuite.draw = function ()
testsuite.testsdraw[testsuite.test]()
end
testsuite.initialise = function (t)
local m = t.ui:addMenu({title = "Tests", attach = true})
for k,v in ipairs(testsuite.tests) do
m:addItem({
title = v.name,
action = function() testsuite.test = v.name v.setup() end,
highlight = function()
return testsuite.test == v.name
end
})
testsuite.testsdraw[v.name] = v.draw
end
end
cmodule.gexport { testsuite = testsuite }
--]==]
--[==[
-- Touch handler class
-- Author: Andrew Stacey
-- Website: http://www.math.ntnu.no/~stacey/HowDidIDoThat/iPad/Codea.html
-- Licence: CC0 http://wiki.creativecommons.org/CC0
--[[
The classes in this file are for handling touches. The classes are:
Touches: this is the controller class which gathers touches, figures
out which object they belong to, and passes the information to those
objects in an orderly manner as a "gesture".
Gesture: a gesture is a collection of touches that are "alive" and
claimed by the same object. A gesture is analysed before being passed
to an object to help provide some information as to what type of
gesture it is.
Touch: this represents a touch as a single object (from start to
finish) and is updated by the controller as new information comes in.
--]]
local Touches = class()
local Gesture = class()
local Touch = class()
--[[
The controlling object has an array of touches, an array of handlers,
and an array of "active touches".
When a "touch" object is registered by Codea, the handler takes it and
tries to work out what to do with it. When a touch begins, the
handler creates a "Touch" object surrounding it. As new information
comes in, via new "touch" objects, this needs to be linked to the
correct "Touch" object. The handler uses the "touch.id" to do this,
but this creates a problem: although the "touch.id" is guaranteed to
be unique in the lifetime of the touch, it might be reused afterwards.
For complicated gestures, it is useful to have "Touch" objects persist
beyond the liftetime of the actual touch. So more than one Touch can
correspond to the same "touch.id". To get round this, we maintain a
list of "active" touches: ie ones that have not officially ended.
These are the ones that accept new information.
--]]
function Touches:init()
self.touches = {}
self.handlers = {}
self.numTouches = 0
self.actives = {}
local img = image(100,100)
setContext(img)
ellipseMode(RADIUS)
fill(255, 223, 0, 21)
strokeWidth(0)
local r
for i=0,90,5 do
r = 10 + 40*math.sin(math.rad(i))
ellipse(50,50,r)
end
self.img = img
setContext()
end
--[[
This is where a touch is initially analysed. If it is a new touch
then a Touch object is created. Then each of the handlers in turn is
asked if it wants to "claim" the touch. The first one to do so is
assigned the touch. If none do, it is consigned to the bin.
There is a slight wrinkle in the above: it is possible for an object
to specify an "interrupt" which is given first priority in the
claimant queue. Sometimes an object might want to take some action
"until the next touch", wherever that touch may be: this is to allow
for that.
If a touch is not new, its information is used to update the
corresponding active Touch object.
--]]
function Touches:addTouch(touch)
local retval = false
if touch.state == BEGAN then
self.numTouches = self.numTouches + 1
self.touches[self.numTouches] = Touch(touch)
self.touches[self.numTouches].container = self
self.touches[self.numTouches].id = self.numTouches
self.actives[touch.id] = self.numTouches
retval = true -- in case the interrupt gets it
if
not self.interrupt
or
not self.interrupt:interruption(
self.touches[self.actives[touch.id]]
)
then
retval = false -- interrupt didn't
retval = self:checkHandlers(touch,self.handlers)
if not self.touches[self.actives[touch.id]].gesture then
self.touches[self.actives[touch.id]] = nil
self.actives[touch.id] = nil
retval = false -- gesture rejected it for some reason
end
end
elseif touch.state == MOVING then
if self.actives[touch.id] then
self.touches[self.actives[touch.id]]:update(touch)
retval = true
end
elseif touch.state == ENDED then
if self.actives[touch.id] then
self.touches[self.actives[touch.id]]:update(touch)
retval = true
end
end
return retval
end
--[[
Create a handler for adding to the relevant code.
--]]
function Touches:registerHandler(h)
local g = Gesture()
return {h,g}
end
--[[
This adds a new handler at the end of the list, creating a gesture to
contain its touches.
--]]
function Touches:pushHandler(h)
table.insert(self.handlers,self:registerHandler(h))
end
--[[
This adds a new handler at the start of the list, creating a gesture to
contain its touches.
--]]
function Touches:unshiftHandler(h)
local g = Gesture()
table.insert(self.handlers,1,self:registerHandler(h))
end
function Touches:removeHandler(h)
for k,v in ipairs(self.handlers) do
if v[1] == h then
table.remove(self.handlers,k)
break
end
end
end
--[[
This adds a new table of handlers at the end of the list.
--]]
function Touches:pushHandlers(t)
local t = t or {}
table.insert(self.handlers,{t})
return t
end
--[[
This adds a new table of handlers at the start of the list.
--]]
function Touches:unshiftHandlers(t)
local t = t or {}
table.insert(self.handlers,1,{t})
return t
end
--[[
Check the handlers to see who claims the touch
--]]
function Touches:checkHandlers(touch,t)
for k,v in ipairs(t) do
if v[2] then
if v[1]:isTouchedBy(touch) then
v[2]:addTouch(self.touches[self.actives[touch.id]])
return true
end
else
if self:checkHandlers(touch,v[1]) then
return true
end
end
end
return false
end
--[[
The draw function is used to process the touch information gathered in
the current cycle. Gestures are analysed and then passed to the
corresponding handers.
--]]
function Touches:show()
if self.showtouch then
pushStyle()
pushMatrix()
resetMatrix()
resetStyle()
for k,v in pairs(self.touches) do
v:draw(self.img)
end
popMatrix()
popStyle()
end
end
function Touches:showTouches(s)
self.showtouch = s
end
function Touches:draw()
self:processHandlers(self.handlers)
end
function Touches:processHandlers(t)
if t then
for k,v in pairs(t) do
if v[2] then
if v[2].num > 0 then
v[2]:analyse()
v[1]:processTouches(v[2])
if v[2].type.finished then
v[2]:reset()
end
end
else
self:processHandlers(v[1])
end
end
end
end
function Touches:reset()
self.touches = {}
self.numTouches = 0
self.actives = {}
end
function Touches:pause()
end
--[[
A gesture is a group of touches that are handled by the same object
and are "alive" at the same time. A gesture is analysed before being
passed to its object and certain basic information is gathered that
can be analysed by the handling object. Most of this information is
stored in the "type" array with two notable exceptions. The main list
is as follows (there are some others which are used for implementation
reasons that can nonetheless be used, but the following contains all
the available information):
num: the number of touches in the gesture.
updated: has there been new information since the gesture was last
looked at?
type.tap: this is true if none of the touches have moved
type.long: this is true if all of the touches waited a significant
length of time (currently .5s) between starting and ending or moving.
type.short: this is true if all of the touches were of short duration
(less that .5s). Note that "short" and "long" are not opposites.
type.ended: if all the touches have ended.
type.finished: if all the touches ended at least .5s ago. The
distinction between "ended" and "finished" is to allow for things like
multiple taps to be registered as a single gesture.
type.pinch: if the gesture consists of multiple movements, the gesture
tries to see if they are moving towards each other or parallel. It
does this by looking at the relative movement of the barycentre of the
touch compared to the average magnitude of the movements of the
individual touches.
--]]
--[[
Initialise our data.
--]]
function Gesture:init()
self.touches = {}
self.num = 0
self.updated = true
self.updatedat = ElapsedTime
self.type = {}
end
--[[
Add a touch to the list.
--]]
function Gesture:addTouch(touch)
self.num = self.num + 1
self.touches[touch.id] = touch
touch.gesture = self
self.updated = true
self.updatedat = ElapsedTime
return true
end
--[[
Remove a touch from the list.
--]]
function Gesture:removeTouch(touch)
self.touches[touch.id] = nil
touch.gesture = nil
self.num = self.num - 1
self.updated = true
self.updatedat = ElapsedTime
self.type = {}
return true
end
--[[
Resets us to a "blank" state. Called by the touch controller at the
end of the cycle if we are "finished" but can be called by our object
at an earlier stage.
--]]
function Gesture:reset()
for k,v in pairs(self.touches) do
v:destroy()
end
self.touches = {}
self.num = 0
self.updated = true
self.updatedat = ElapsedTime
self.type = {}
end
--[[
The "updated" field is so that the object knows that new information
has come in. So it is for the object to say "I am done with this
information, wake me again when there is new stuff" by calling this
routine.
--]]
function Gesture:noted()
self.updated = false
for k,v in pairs(self.touches) do
v.updated = false
end
end
--[[
This is the analyser that works out the basic information about the
gesture. Most of the information is of the "if all touches are X, so
are we" or "if at least one touch is X, so are we" (which are
equivalent via negation). The exception is the "pinch".
--]]
function Gesture:analyse()
local b = vec2(0,0)
local d = 0
local c
self.touchesArr = {}
self.actives = {}
local na = 0
self.type.ended = true
self.type.notlong = false
for k,v in pairs(self.touches) do
table.insert(self.touchesArr,v)
if v.updated or v.touch.state ~= ENDED then
table.insert(self.actives,v)
na = na + 1
end
if v.moved then
self.type.moved = true
end
if v.touch.state ~= ENDED then
self.type.ended = false
end
if not v:islong() then
self.type.notlong = true
end
if not v:isshort() then
self.type.notshort = true
end
c = vec2(v.touch.deltaX,v.touch.deltaY)
b = b + c
d = d + c:lenSqr()
end
self.numactives = na
if not self.type.moved then
-- some sort of tap
self.type.tap = true
else
self.type.tap = false
end
if self.type.ended and (ElapsedTime - self.updatedat) > .5 then
self.type.finished = true
end
self.type.long = not self.type.notlong
self.type.short = not self.type.notshort
if self.num > 1 and not self.type.tap then
if d * self.num < 1.5 * b:lenSqr() then
self.type.pinch = false
else
self.type.pinch = true
end
end
end
function Gesture:transformTouches(o)
for _,t in pairs(self.touches) do
if t.updated then
t.touch = TransformTouch(o,t.touch)
if t.state == BEGAN then
t.firsttouch = TransformTouch(o,t.firsttouch)
end
t.velocities[t.ntouches][1] = t.touch.x
t.velocities[t.ntouches][2] = t.touch.y
end
end
end
--[[
This is an extension of the "touch" class, providing a single object
that corresponds to what a user would call a "touch". It also
provides more information than is contained in a single "touch"
object.
--]]
--[[
Initialiser function.
--]]
function Touch:init(touch)
self.id = touch.id
self.touch = touch
self.firsttouch = touch
self.updated = true
self.updatedat = ElapsedTime
self.createdat = ElapsedTime
self.startedat = ElapsedTime
self.deltatime = 0
self.laststate = 0
self.moved = false -- did we move?
self.long = false -- long time before we did anything?
self.short = false -- active lifetime was short?
self.keepalive = false
self.velDelta = .5 -- for computing a more realistic velocity
self.velocities = {{touch.x,touch.y,ElapsedTime}}
self.ntouches = 1
end
--[[
Updates new information from a "touch" object.
--]]
function Touch:update(touch)
-- save previous state
self.laststate = touch.state
self.deltatime = ElapsedTime - self.updatedat
self.updatedat = ElapsedTime
table.insert(self.velocities,{touch.x,touch.y,ElapsedTime})
self.ntouches = self.ntouches + 1
-- Update the touch
self.touch = touch
-- Regard ourselves as "refreshed"
self.updated = true
if self.gesture then
self.gesture.updated = true
self.gesture.updatedat = ElapsedTime
end
if self.laststate == BEGAN then
self.startedat = ElapsedTime
end
-- record whether we've moved
if touch.state == MOVING then
self.moved = true
end
-- if it was a long time since we began, we're long
if self.laststate == BEGAN and self.deltatime > 1 then
self.long = true
end
if touch.state == ENDED then
-- If we've ended and it's less than a second since we
-- actually did something, we're short
if (ElapsedTime - self.startedat) < 1 then
self.short = true
end
end
return true
end
--[[
Regard ourselves as "dealt with" until new information comes in.
--]]
function Touch:handled()
self.updated = false
end
--[[
Do our level best to get rid of ourselves, removing ourself from the
gesture and touch controller.
--]]
function Touch:destroy()
if self.gesture then
self.gesture:removeTouch(self)
end
if self.container then
if self.container.actives[self.touch.id] == self.id then
self.container.actives[self.touch.id] = nil
end
self.container.touches[self.id] = nil
end
self = nil
end
--[[
Test to find out if we are "long"
--]]
function Touch:islong()
if self.long then
return self.long
elseif self.touch.state == BEGAN and (ElapsedTime - self.createdat) > 1 then
self.long = true
return true
else
return false
end
end
--[[
Test to find out if we are "short"
--]]
function Touch:isshort()
if self.short then
return self.short
elseif (ElapsedTime - self.startedat) < 1 then
return true
else
return false
end
end
function Touch:instVelocity()
if self.deltatime > 0 then
return vec2(
self.touch.deltaX/self.deltatime,
self.touch.deltaY/self.deltatime
)
else
return vec2(0,0)
end
end
function Touch:meanVelocity()
if self.updatedat > self.createdat then
return vec2(
self.touch.x - self.firsttouch.x,
self.touch.y - self.firsttouch.y
)/(self.updatedat - self.createdat)
else
return vec2(0,0)
end
end
function Touch:velocity()
local dt = 0
local dx = 0
local dy = 0
local i
local n = 0
local rec = false
local vel = self.velocities
for k,v in ipairs(vel) do
n = n + 1
if v[3] > ElapsedTime - 2*self.velDelta then
rec = true
end
if i and v[3] > ElapsedTime - self.velDelta then
rec = false
end
if rec then
i = k
end
end
if i == 0 then
i = 1
end
dt = ElapsedTime - vel[i][3]
if dt == 0 then
return vec2(0,0)
end
dx = vel[n][1] - vel[i][1]
dy = vel[n][2] - vel[i][2]
return vec2(dx/dt,dy/dt)
end
function Touch:draw(img)
sprite(img,self.touch.x,self.touch.y)
end
return Touches
--]==]
--[==[
-- UTF8 functions
-- Author: Andrew Stacey
-- Website: http://www.math.ntnu.no/~stacey/HowDidIDoThat/iPad/Codea.html
-- Licence: CC0 http://wiki.creativecommons.org/CC0
--[[
This file provides some basic functionality for dealing with utf8
strings. The basic lua string operations act on a byte-by-byte basis
and these have to be modified to work on a utf8-character basis.
--]]
local UTF8 = class()
cimport "BinDecHex"
local utf8_upper, utf8_lower = unpack(cimport "utf8Case",nil)
function UTF8:init(c,l)
if type(c) == "table" then
if c.is_a and c:is_a(UTF8) then
self.length = c.length
local t = {}
for _,v in ipairs(c.uchars) do
table.insert(t,v)
end
self.uchars = t
else
self.uchars = c or {}
if not l then
l = 0
for k,v in ipairs(c) do
l = k
end
end
self.length = l
end
elseif type(c) == "string" then
self:process(c)
elseif type(c) == "number" then
self.uchars = {c}
self.length = 1
else
self.uchars = {}
self.length = 0
end
end
function UTF8:clone()
return UTF8(self)
end
function UTF8:process(s)
local t = {}
local l
local n = 0
for c in s:gmatch"." do
a = string.byte(c)
if a < 127 then
table.insert(t,a)
n = n + 1
elseif a > 191 then
-- first byte
l = 1
a = a - 192
if a > 31 then
l = l + 1
a = a - 32
if a > 15 then
l = l + 1
a = a - 16
end
end
d = a
else
l = l - 1
d = d * 64 + (a - 128)
if l == 0 then
table.insert(t,d)
n = n + 1
end
end
end
self.uchars = t
self.length = n
end
local function int_utf8dec(a)
local t = {}
if a < 128 then
table.insert(t,a)
elseif a < 2048 then
local b,c
b = a%64 + 128
c = math.floor(a/64) + 192
table.insert(t,c)
table.insert(t,b)
elseif a < 65536 then
local b,c,d
b = a%64 + 128
c = math.floor(a/64)%64 + 128
d = math.floor(a/4096) + 224
table.insert(t,d)
table.insert(t,c)
table.insert(t,b)
elseif a < 2097152 then
local b,c,d,e
b = a%64 + 128
c = math.floor(a/64)%64 + 128
d = math.floor(a/4096)%64 + 128
e = math.floor(a/262144) + 240
table.insert(t,e)
table.insert(t,d)
table.insert(t,c)
table.insert(t,b)
end
return t
end
--[[
Concatenate two UTF8 objects
--]]
function UTF8:append(u)
for k,v in ipairs(u.uchars) do
table.insert(self.uchars,v)
end
self.length = self.length + u.length
end
function UTF8:prepend(u)
local c = {}
for k,v in ipairs(u.uchars) do
table.insert(c,v)
end
for k,v in ipairs(self.uchars) do
table.insert(c,v)
end
self.uchars = c
self.length = self.length + u.length
end
--[[
Returns the length of a utf8 string.
--]]
function UTF8:length()
return self.length
end
--[[
Returns the substring from i to j of the utf8 string s. The arguments
behave in the same fashion as string.sub with regard to negatives.
--]]
function UTF8:sub(i,j)
local ln = self.length
if i < 0 then
i = i + ln + 1
end
if j < 0 then
j = j + ln + 1
end
if i < 1 or i > ln or j < 1 or j > ln or i > j then
return UTF8({},0)
end
local c = {}
local l = j - i + 1
local lc = self.uchars
for k = i,j do
table.insert(c,lc[k])
end
return UTF8(c,l)
end
--[[
This splits a utf8 string at the specified spot.
--]]
function UTF8:split(i)
local ln = self.length
if i < 0 then
i = i + ln + 1
end
if i < 1 then
return self,UTF8({},0)
end
if i > ln then
return UTF8({},0),self
end
local sc = {}
local sl = i - 1
local ec = {}
local el = ln - i + 1
local lc = self.uchars
for k = 1,i-1 do
table.insert(sc,lc[k])
end
for k = i,ln do
table.insert(ec,lc[k])
end
return UTF8(sc,sl),UTF8(ec,el)
end
function UTF8:tostring()
local t = {}
local it
for k,a in ipairs(self.uchars) do
it = int_utf8dec(a)
for l,u in ipairs(it) do
table.insert(t,u)
end
end
return string.char(unpack(t))
end
function UTF8:__concat(v)
if type(v) == "table"
and v:is_a(UTF8) then
return self .. v:tostring()
else
return self:tostring() .. v
end
end
function UTF8:__tostring()
return self:tostring()
end
--[[
This takes in a hexadecimal number and converts it to a utf8 character.
--]]
local function utf8hex(s)
return UTF8(tonumber(Hex2Dec(s)))
end
--[[
This takes in a decimal number and converts it to a utf8 character.
--]]
local function utf8dec(n)
n = tonumber(n)
local t = int_utf8dec(n)
return string.char(unpack(t))
end
--[[
This uses the utf8_upper array to convert a character to its
corresponding uppercase variant, if such exists.
--]]
function UTF8:toupper()
for k,c in ipairs(self.uchars) do
if utf8_upper[c] then
self.uchars[k] = utf8_upper[c]
end
end
end
--[[
This uses the utf8_lower array to convert a character to its
corresponding lowercase variant, if such exists.
--]]
function UTF8:tolower()
for k,c in ipairs(self.uchars) do
if utf8_lower[c] then
self.uchars[k] = utf8_lower[c]
end
end
end
--[[
This function splits the UTF8 "string" at the whitespace at or before
the given number
--]]
function UTF8:splitBy(n)
local l = self.length
local i = 1
local c = self.uchars
return function()
if i > l then
return nil
end
local t = {}
local m
if i + n > l then
local p = l
-- look from start for new line chars
for k=i,l do
if c[k] == 10 then
p = k
break
end
end
for k = i,p do
table.insert(t,c[k])
end
m = p - i + 1
i = p + 1
else
local p,q
-- look from start for new line chars
for k=i,i + n do
if not q and c[k] == 10 then
q = k + 1
p = k
break
end
end
if not p then
-- none found, look from end for space chars
for k=i + n,i,-1 do
if not q and c[k] == 32 then
q = k + 1
end
if q and c[k] ~= 32 then
p = k
break
end
end
end
p = p or i + n
q = q or i + n + 1
for k=i,p do
table.insert(t,c[k])
end
m = p - i + 1
i = q
end
return UTF8(t,m)
end
end
function UTF8:chars()
local i = 0
local n = self.length
local c = self.uchars
return function()
if i == n then
return nil
end
i = i + 1
return c[i]
end
end
function UTF8:firstchar()
return self.uchars[1]
end
function UTF8:char(n)
return self.uchars[n]
end
function UTF8:lastchar()
return self.uchars[self.length]
end
function UTF8:chomp()
while self.uchars[self.length] == 10 do
self:pop()
end
end
function UTF8:trim(u)
if type(u) == "table" and u.is_a and u.is_a(UTF8) then
u = u:firstchar()
else
u = tonumber(u)
end
while self.uchars[self.length] == u do
self:pop()
end
while self.uchars[1] == u do
self:shift()
end
end
function UTF8:del_char(n)
local s = ""
if self.length > 0 then
if n < 0 then
n = n + self.length + 1
end
if n >= 1 and n <= self.length then
s = table.remove(self.uchars,n)
self.length = self.length - 1
end
end
return UTF8(s)
end
function UTF8:pop()
return self:del_char(-1)
end
function UTF8:unshift()
return self:del_char(1)
end
cmodule.gexport {
utf8dec = utf8dec,
uft8hex = utf8hex
}
return UTF8
--]==]
--[==[
-- UTF8 case mapping
-- Author: Andrew Stacey
-- Website: http://www.math.ntnu.no/~stacey/HowDidIDoThat/iPad/Codea.html
-- ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt
-- Licence: CC0 http://wiki.creativecommons.org/CC0
--[[
Array of upper/lower case correspondences in utf8.
--]]
local utf8_upper = {
[97] = 65,
[98] = 66,
[99] = 67,
[100] = 68,
[101] = 69,
[102] = 70,
[103] = 71,
[104] = 72,
[105] = 73,
[106] = 74,
[107] = 75,
[108] = 76,
[109] = 77,
[110] = 78,
[111] = 79,
[112] = 80,
[113] = 81,
[114] = 82,
[115] = 83,
[116] = 84,
[117] = 85,
[118] = 86,
[119] = 87,
[120] = 88,
[121] = 89,
[122] = 90,
[181] = 924,
[224] = 192,
[225] = 193,
[226] = 194,
[227] = 195,
[228] = 196,
[229] = 197,
[230] = 198,
[231] = 199,
[232] = 200,
[233] = 201,
[234] = 202,
[235] = 203,
[236] = 204,
[237] = 205,
[238] = 206,
[239] = 207,
[240] = 208,
[241] = 209,
[242] = 210,
[243] = 211,
[244] = 212,
[245] = 213,
[246] = 214,
[248] = 216,
[249] = 217,
[250] = 218,
[251] = 219,
[252] = 220,
[253] = 221,
[254] = 222,
[255] = 376,
[257] = 256,
[259] = 258,
[261] = 260,
[263] = 262,
[265] = 264,
[267] = 266,
[269] = 268,
[271] = 270,
[273] = 272,
[275] = 274,
[277] = 276,
[279] = 278,
[281] = 280,
[283] = 282,
[285] = 284,
[287] = 286,
[289] = 288,
[291] = 290,
[293] = 292,
[295] = 294,
[297] = 296,
[299] = 298,
[301] = 300,
[303] = 302,
[305] = 73,
[307] = 306,
[309] = 308,
[311] = 310,
[314] = 313,
[316] = 315,
[318] = 317,
[320] = 319,
[322] = 321,
[324] = 323,
[326] = 325,
[328] = 327,
[331] = 330,
[333] = 332,
[335] = 334,
[337] = 336,
[339] = 338,
[341] = 340,
[343] = 342,
[345] = 344,
[347] = 346,
[349] = 348,
[351] = 350,
[353] = 352,
[355] = 354,
[357] = 356,
[359] = 358,
[361] = 360,
[363] = 362,
[365] = 364,
[367] = 366,
[369] = 368,
[371] = 370,
[373] = 372,
[375] = 374,
[378] = 377,
[380] = 379,
[382] = 381,
[383] = 83,
[384] = 579,
[387] = 386,
[389] = 388,
[392] = 391,
[396] = 395,
[402] = 401,
[405] = 502,
[409] = 408,
[410] = 573,
[414] = 544,
[417] = 416,
[419] = 418,
[421] = 420,
[424] = 423,
[429] = 428,
[432] = 431,
[436] = 435,
[438] = 437,
[441] = 440,
[445] = 444,
[447] = 503,
[453] = 452,
[454] = 452,
[456] = 455,
[457] = 455,
[459] = 458,
[460] = 458,
[462] = 461,
[464] = 463,
[466] = 465,
[468] = 467,
[470] = 469,
[472] = 471,
[474] = 473,
[476] = 475,
[477] = 398,
[479] = 478,
[481] = 480,
[483] = 482,
[485] = 484,
[487] = 486,
[489] = 488,
[491] = 490,
[493] = 492,
[495] = 494,
[498] = 497,
[499] = 497,
[501] = 500,
[505] = 504,
[507] = 506,
[509] = 508,
[511] = 510,
[513] = 512,
[515] = 514,
[517] = 516,
[519] = 518,
[521] = 520,
[523] = 522,
[525] = 524,
[527] = 526,
[529] = 528,
[531] = 530,
[533] = 532,
[535] = 534,
[537] = 536,
[539] = 538,
[541] = 540,
[543] = 542,
[547] = 546,
[549] = 548,
[551] = 550,
[553] = 552,
[555] = 554,
[557] = 556,
[559] = 558,
[561] = 560,
[563] = 562,
[572] = 571,
[575] = 11390,
[576] = 11391,
[578] = 577,
[583] = 582,
[585] = 584,
[587] = 586,
[589] = 588,
[591] = 590,
[592] = 11375,
[593] = 11373,
[594] = 11376,
[595] = 385,
[596] = 390,
[598] = 393,
[599] = 394,
[601] = 399,
[603] = 400,
[608] = 403,
[611] = 404,
[613] = 42893,
[616] = 407,
[617] = 406,
[619] = 11362,
[623] = 412,
[625] = 11374,
[626] = 413,
[629] = 415,
[637] = 11364,
[640] = 422,
[643] = 425,
[648] = 430,
[649] = 580,
[650] = 433,
[651] = 434,
[652] = 581,
[658] = 439,
[837] = 921,
[881] = 880,
[883] = 882,
[887] = 886,
[891] = 1021,
[892] = 1022,
[893] = 1023,
[940] = 902,
[941] = 904,
[942] = 905,
[943] = 906,
[945] = 913,
[946] = 914,
[947] = 915,
[948] = 916,
[949] = 917,
[950] = 918,
[951] = 919,
[952] = 920,
[953] = 921,
[954] = 922,
[955] = 923,
[956] = 924,
[957] = 925,
[958] = 926,
[959] = 927,
[960] = 928,
[961] = 929,
[962] = 931,
[963] = 931,
[964] = 932,
[965] = 933,
[966] = 934,
[967] = 935,
[968] = 936,
[969] = 937,
[970] = 938,
[971] = 939,
[972] = 908,
[973] = 910,
[974] = 911,
[976] = 914,
[977] = 920,
[981] = 934,
[982] = 928,
[983] = 975,
[985] = 984,
[987] = 986,
[989] = 988,
[991] = 990,
[993] = 992,
[995] = 994,
[997] = 996,
[999] = 998,
[1001] = 1000,
[1003] = 1002,
[1005] = 1004,
[1007] = 1006,
[1008] = 922,
[1009] = 929,
[1010] = 1017,
[1013] = 917,
[1016] = 1015,
[1019] = 1018,
[1072] = 1040,
[1073] = 1041,
[1074] = 1042,
[1075] = 1043,
[1076] = 1044,
[1077] = 1045,
[1078] = 1046,
[1079] = 1047,
[1080] = 1048,
[1081] = 1049,
[1082] = 1050,
[1083] = 1051,
[1084] = 1052,
[1085] = 1053,
[1086] = 1054,
[1087] = 1055,
[1088] = 1056,
[1089] = 1057,
[1090] = 1058,
[1091] = 1059,
[1092] = 1060,
[1093] = 1061,
[1094] = 1062,
[1095] = 1063,
[1096] = 1064,
[1097] = 1065,
[1098] = 1066,
[1099] = 1067,
[1100] = 1068,
[1101] = 1069,
[1102] = 1070,
[1103] = 1071,
[1104] = 1024,
[1105] = 1025,
[1106] = 1026,
[1107] = 1027,
[1108] = 1028,
[1109] = 1029,
[1110] = 1030,
[1111] = 1031,
[1112] = 1032,
[1113] = 1033,
[1114] = 1034,
[1115] = 1035,
[1116] = 1036,
[1117] = 1037,
[1118] = 1038,
[1119] = 1039,
[1121] = 1120,
[1123] = 1122,
[1125] = 1124,
[1127] = 1126,
[1129] = 1128,
[1131] = 1130,
[1133] = 1132,
[1135] = 1134,
[1137] = 1136,
[1139] = 1138,
[1141] = 1140,
[1143] = 1142,
[1145] = 1144,
[1147] = 1146,
[1149] = 1148,
[1151] = 1150,
[1153] = 1152,
[1163] = 1162,
[1165] = 1164,
[1167] = 1166,
[1169] = 1168,
[1171] = 1170,
[1173] = 1172,
[1175] = 1174,
[1177] = 1176,
[1179] = 1178,
[1181] = 1180,
[1183] = 1182,
[1185] = 1184,
[1187] = 1186,
[1189] = 1188,
[1191] = 1190,
[1193] = 1192,
[1195] = 1194,
[1197] = 1196,
[1199] = 1198,
[1201] = 1200,
[1203] = 1202,
[1205] = 1204,
[1207] = 1206,
[1209] = 1208,
[1211] = 1210,
[1213] = 1212,
[1215] = 1214,
[1218] = 1217,
[1220] = 1219,
[1222] = 1221,
[1224] = 1223,
[1226] = 1225,
[1228] = 1227,
[1230] = 1229,
[1231] = 1216,
[1233] = 1232,
[1235] = 1234,
[1237] = 1236,
[1239] = 1238,
[1241] = 1240,
[1243] = 1242,
[1245] = 1244,
[1247] = 1246,
[1249] = 1248,
[1251] = 1250,
[1253] = 1252,
[1255] = 1254,
[1257] = 1256,
[1259] = 1258,
[1261] = 1260,
[1263] = 1262,
[1265] = 1264,
[1267] = 1266,
[1269] = 1268,
[1271] = 1270,
[1273] = 1272,
[1275] = 1274,
[1277] = 1276,
[1279] = 1278,
[1281] = 1280,
[1283] = 1282,
[1285] = 1284,
[1287] = 1286,
[1289] = 1288,
[1291] = 1290,
[1293] = 1292,
[1295] = 1294,
[1297] = 1296,
[1299] = 1298,
[1301] = 1300,
[1303] = 1302,
[1305] = 1304,
[1307] = 1306,
[1309] = 1308,
[1311] = 1310,
[1313] = 1312,
[1315] = 1314,
[1317] = 1316,
[1319] = 1318,
[1377] = 1329,
[1378] = 1330,
[1379] = 1331,
[1380] = 1332,
[1381] = 1333,
[1382] = 1334,
[1383] = 1335,
[1384] = 1336,
[1385] = 1337,
[1386] = 1338,
[1387] = 1339,
[1388] = 1340,
[1389] = 1341,
[1390] = 1342,
[1391] = 1343,
[1392] = 1344,
[1393] = 1345,
[1394] = 1346,
[1395] = 1347,
[1396] = 1348,
[1397] = 1349,
[1398] = 1350,
[1399] = 1351,
[1400] = 1352,
[1401] = 1353,
[1402] = 1354,
[1403] = 1355,
[1404] = 1356,
[1405] = 1357,
[1406] = 1358,
[1407] = 1359,
[1408] = 1360,
[1409] = 1361,
[1410] = 1362,
[1411] = 1363,
[1412] = 1364,
[1413] = 1365,
[1414] = 1366,
[7545] = 42877,
[7549] = 11363,
[7681] = 7680,
[7683] = 7682,
[7685] = 7684,
[7687] = 7686,
[7689] = 7688,
[7691] = 7690,
[7693] = 7692,
[7695] = 7694,
[7697] = 7696,
[7699] = 7698,
[7701] = 7700,
[7703] = 7702,
[7705] = 7704,
[7707] = 7706,
[7709] = 7708,
[7711] = 7710,
[7713] = 7712,
[7715] = 7714,
[7717] = 7716,
[7719] = 7718,
[7721] = 7720,
[7723] = 7722,
[7725] = 7724,
[7727] = 7726,
[7729] = 7728,
[7731] = 7730,
[7733] = 7732,
[7735] = 7734,
[7737] = 7736,
[7739] = 7738,
[7741] = 7740,
[7743] = 7742,
[7745] = 7744,
[7747] = 7746,
[7749] = 7748,
[7751] = 7750,
[7753] = 7752,
[7755] = 7754,
[7757] = 7756,
[7759] = 7758,
[7761] = 7760,
[7763] = 7762,
[7765] = 7764,
[7767] = 7766,
[7769] = 7768,
[7771] = 7770,
[7773] = 7772,
[7775] = 7774,
[7777] = 7776,
[7779] = 7778,
[7781] = 7780,
[7783] = 7782,
[7785] = 7784,
[7787] = 7786,
[7789] = 7788,
[7791] = 7790,
[7793] = 7792,
[7795] = 7794,
[7797] = 7796,
[7799] = 7798,
[7801] = 7800,
[7803] = 7802,
[7805] = 7804,
[7807] = 7806,
[7809] = 7808,
[7811] = 7810,
[7813] = 7812,
[7815] = 7814,
[7817] = 7816,
[7819] = 7818,
[7821] = 7820,
[7823] = 7822,
[7825] = 7824,
[7827] = 7826,
[7829] = 7828,
[7835] = 7776,
[7841] = 7840,
[7843] = 7842,
[7845] = 7844,
[7847] = 7846,
[7849] = 7848,
[7851] = 7850,
[7853] = 7852,
[7855] = 7854,
[7857] = 7856,
[7859] = 7858,
[7861] = 7860,
[7863] = 7862,
[7865] = 7864,
[7867] = 7866,
[7869] = 7868,
[7871] = 7870,
[7873] = 7872,
[7875] = 7874,
[7877] = 7876,
[7879] = 7878,
[7881] = 7880,
[7883] = 7882,
[7885] = 7884,
[7887] = 7886,
[7889] = 7888,
[7891] = 7890,
[7893] = 7892,
[7895] = 7894,
[7897] = 7896,
[7899] = 7898,
[7901] = 7900,
[7903] = 7902,
[7905] = 7904,
[7907] = 7906,
[7909] = 7908,
[7911] = 7910,
[7913] = 7912,
[7915] = 7914,
[7917] = 7916,
[7919] = 7918,
[7921] = 7920,
[7923] = 7922,
[7925] = 7924,
[7927] = 7926,
[7929] = 7928,
[7931] = 7930,
[7933] = 7932,
[7935] = 7934,
[7936] = 7944,
[7937] = 7945,
[7938] = 7946,
[7939] = 7947,
[7940] = 7948,
[7941] = 7949,
[7942] = 7950,
[7943] = 7951,
[7952] = 7960,
[7953] = 7961,
[7954] = 7962,
[7955] = 7963,
[7956] = 7964,
[7957] = 7965,
[7968] = 7976,
[7969] = 7977,
[7970] = 7978,
[7971] = 7979,
[7972] = 7980,
[7973] = 7981,
[7974] = 7982,
[7975] = 7983,
[7984] = 7992,
[7985] = 7993,
[7986] = 7994,
[7987] = 7995,
[7988] = 7996,
[7989] = 7997,
[7990] = 7998,
[7991] = 7999,
[8000] = 8008,
[8001] = 8009,
[8002] = 8010,
[8003] = 8011,
[8004] = 8012,
[8005] = 8013,
[8017] = 8025,
[8019] = 8027,
[8021] = 8029,
[8023] = 8031,
[8032] = 8040,
[8033] = 8041,
[8034] = 8042,
[8035] = 8043,
[8036] = 8044,
[8037] = 8045,
[8038] = 8046,
[8039] = 8047,
[8048] = 8122,
[8049] = 8123,
[8050] = 8136,
[8051] = 8137,
[8052] = 8138,
[8053] = 8139,
[8054] = 8154,
[8055] = 8155,
[8056] = 8184,
[8057] = 8185,
[8058] = 8170,
[8059] = 8171,
[8060] = 8186,
[8061] = 8187,
[8064] = 8072,
[8065] = 8073,
[8066] = 8074,
[8067] = 8075,
[8068] = 8076,
[8069] = 8077,
[8070] = 8078,
[8071] = 8079,
[8080] = 8088,
[8081] = 8089,
[8082] = 8090,
[8083] = 8091,
[8084] = 8092,
[8085] = 8093,
[8086] = 8094,
[8087] = 8095,
[8096] = 8104,
[8097] = 8105,
[8098] = 8106,
[8099] = 8107,
[8100] = 8108,
[8101] = 8109,
[8102] = 8110,
[8103] = 8111,
[8112] = 8120,
[8113] = 8121,
[8115] = 8124,
[8126] = 921,
[8131] = 8140,
[8144] = 8152,
[8145] = 8153,
[8160] = 8168,
[8161] = 8169,
[8165] = 8172,
[8179] = 8188,
[8526] = 8498,
[8560] = 8544,
[8561] = 8545,
[8562] = 8546,
[8563] = 8547,
[8564] = 8548,
[8565] = 8549,
[8566] = 8550,
[8567] = 8551,
[8568] = 8552,
[8569] = 8553,
[8570] = 8554,
[8571] = 8555,
[8572] = 8556,
[8573] = 8557,
[8574] = 8558,
[8575] = 8559,
[8580] = 8579,
[9424] = 9398,
[9425] = 9399,
[9426] = 9400,
[9427] = 9401,
[9428] = 9402,
[9429] = 9403,
[9430] = 9404,
[9431] = 9405,
[9432] = 9406,
[9433] = 9407,
[9434] = 9408,
[9435] = 9409,
[9436] = 9410,
[9437] = 9411,
[9438] = 9412,
[9439] = 9413,
[9440] = 9414,
[9441] = 9415,
[9442] = 9416,
[9443] = 9417,
[9444] = 9418,
[9445] = 9419,
[9446] = 9420,
[9447] = 9421,
[9448] = 9422,
[9449] = 9423,
[11312] = 11264,
[11313] = 11265,
[11314] = 11266,
[11315] = 11267,
[11316] = 11268,
[11317] = 11269,
[11318] = 11270,
[11319] = 11271,
[11320] = 11272,
[11321] = 11273,
[11322] = 11274,
[11323] = 11275,
[11324] = 11276,
[11325] = 11277,
[11326] = 11278,
[11327] = 11279,
[11328] = 11280,
[11329] = 11281,
[11330] = 11282,
[11331] = 11283,
[11332] = 11284,
[11333] = 11285,
[11334] = 11286,
[11335] = 11287,
[11336] = 11288,
[11337] = 11289,
[11338] = 11290,
[11339] = 11291,
[11340] = 11292,
[11341] = 11293,
[11342] = 11294,
[11343] = 11295,
[11344] = 11296,
[11345] = 11297,
[11346] = 11298,
[11347] = 11299,
[11348] = 11300,
[11349] = 11301,
[11350] = 11302,
[11351] = 11303,
[11352] = 11304,
[11353] = 11305,
[11354] = 11306,
[11355] = 11307,
[11356] = 11308,
[11357] = 11309,
[11358] = 11310,
[11361] = 11360,
[11365] = 570,
[11366] = 574,
[11368] = 11367,
[11370] = 11369,
[11372] = 11371,
[11379] = 11378,
[11382] = 11381,
[11393] = 11392,
[11395] = 11394,
[11397] = 11396,
[11399] = 11398,
[11401] = 11400,
[11403] = 11402,
[11405] = 11404,
[11407] = 11406,
[11409] = 11408,
[11411] = 11410,
[11413] = 11412,
[11415] = 11414,
[11417] = 11416,
[11419] = 11418,
[11421] = 11420,
[11423] = 11422,
[11425] = 11424,
[11427] = 11426,
[11429] = 11428,
[11431] = 11430,
[11433] = 11432,
[11435] = 11434,
[11437] = 11436,
[11439] = 11438,
[11441] = 11440,
[11443] = 11442,
[11445] = 11444,
[11447] = 11446,
[11449] = 11448,
[11451] = 11450,
[11453] = 11452,
[11455] = 11454,
[11457] = 11456,
[11459] = 11458,
[11461] = 11460,
[11463] = 11462,
[11465] = 11464,
[11467] = 11466,
[11469] = 11468,
[11471] = 11470,
[11473] = 11472,
[11475] = 11474,
[11477] = 11476,
[11479] = 11478,
[11481] = 11480,
[11483] = 11482,
[11485] = 11484,
[11487] = 11486,
[11489] = 11488,
[11491] = 11490,
[11500] = 11499,
[11502] = 11501,
[11520] = 4256,
[11521] = 4257,
[11522] = 4258,
[11523] = 4259,
[11524] = 4260,
[11525] = 4261,
[11526] = 4262,
[11527] = 4263,
[11528] = 4264,
[11529] = 4265,
[11530] = 4266,
[11531] = 4267,
[11532] = 4268,
[11533] = 4269,
[11534] = 4270,
[11535] = 4271,
[11536] = 4272,
[11537] = 4273,
[11538] = 4274,
[11539] = 4275,
[11540] = 4276,
[11541] = 4277,
[11542] = 4278,
[11543] = 4279,
[11544] = 4280,
[11545] = 4281,
[11546] = 4282,
[11547] = 4283,
[11548] = 4284,
[11549] = 4285,
[11550] = 4286,
[11551] = 4287,
[11552] = 4288,
[11553] = 4289,
[11554] = 4290,
[11555] = 4291,
[11556] = 4292,
[11557] = 4293,
[42561] = 42560,
[42563] = 42562,
[42565] = 42564,
[42567] = 42566,
[42569] = 42568,
[42571] = 42570,
[42573] = 42572,
[42575] = 42574,
[42577] = 42576,
[42579] = 42578,
[42581] = 42580,
[42583] = 42582,
[42585] = 42584,
[42587] = 42586,
[42589] = 42588,
[42591] = 42590,
[42593] = 42592,
[42595] = 42594,
[42597] = 42596,
[42599] = 42598,
[42601] = 42600,
[42603] = 42602,
[42605] = 42604,
[42625] = 42624,
[42627] = 42626,
[42629] = 42628,
[42631] = 42630,
[42633] = 42632,
[42635] = 42634,
[42637] = 42636,
[42639] = 42638,
[42641] = 42640,
[42643] = 42642,
[42645] = 42644,
[42647] = 42646,
[42787] = 42786,
[42789] = 42788,
[42791] = 42790,
[42793] = 42792,
[42795] = 42794,
[42797] = 42796,
[42799] = 42798,
[42803] = 42802,
[42805] = 42804,
[42807] = 42806,
[42809] = 42808,
[42811] = 42810,
[42813] = 42812,
[42815] = 42814,
[42817] = 42816,
[42819] = 42818,
[42821] = 42820,
[42823] = 42822,
[42825] = 42824,
[42827] = 42826,
[42829] = 42828,
[42831] = 42830,
[42833] = 42832,
[42835] = 42834,
[42837] = 42836,
[42839] = 42838,
[42841] = 42840,
[42843] = 42842,
[42845] = 42844,
[42847] = 42846,
[42849] = 42848,
[42851] = 42850,
[42853] = 42852,
[42855] = 42854,
[42857] = 42856,
[42859] = 42858,
[42861] = 42860,
[42863] = 42862,
[42874] = 42873,
[42876] = 42875,
[42879] = 42878,
[42881] = 42880,
[42883] = 42882,
[42885] = 42884,
[42887] = 42886,
[42892] = 42891,
[42897] = 42896,
[42913] = 42912,
[42915] = 42914,
[42917] = 42916,
[42919] = 42918,
[42921] = 42920,
[65345] = 65313,
[65346] = 65314,
[65347] = 65315,
[65348] = 65316,
[65349] = 65317,
[65350] = 65318,
[65351] = 65319,
[65352] = 65320,
[65353] = 65321,
[65354] = 65322,
[65355] = 65323,
[65356] = 65324,
[65357] = 65325,
[65358] = 65326,
[65359] = 65327,
[65360] = 65328,
[65361] = 65329,
[65362] = 65330,
[65363] = 65331,
[65364] = 65332,
[65365] = 65333,
[65366] = 65334,
[65367] = 65335,
[65368] = 65336,
[65369] = 65337,
[65370] = 65338,
[66600] = 66560,
[66601] = 66561,
[66602] = 66562,
[66603] = 66563,
[66604] = 66564,
[66605] = 66565,
[66606] = 66566,
[66607] = 66567,
[66608] = 66568,
[66609] = 66569,
[66610] = 66570,
[66611] = 66571,
[66612] = 66572,
[66613] = 66573,
[66614] = 66574,
[66615] = 66575,
[66616] = 66576,
[66617] = 66577,
[66618] = 66578,
[66619] = 66579,
[66620] = 66580,
[66621] = 66581,
[66622] = 66582,
[66623] = 66583,
[66624] = 66584,
[66625] = 66585,
[66626] = 66586,
[66627] = 66587,
[66628] = 66588,
[66629] = 66589,
[66630] = 66590,
[66631] = 66591,
[66632] = 66592,
[66633] = 66593,
[66634] = 66594,
[66635] = 66595,
[66636] = 66596,
[66637] = 66597,
[66638] = 66598,
[66639] = 66599,
}
local utf8_lower = {
[65] = 97,
[66] = 98,
[67] = 99,
[68] = 100,
[69] = 101,
[70] = 102,
[71] = 103,
[72] = 104,
[73] = 105,
[74] = 106,
[75] = 107,
[76] = 108,
[77] = 109,
[78] = 110,
[79] = 111,
[80] = 112,
[81] = 113,
[82] = 114,
[83] = 115,
[84] = 116,
[85] = 117,
[86] = 118,
[87] = 119,
[88] = 120,
[89] = 121,
[90] = 122,
[192] = 224,
[193] = 225,
[194] = 226,
[195] = 227,
[196] = 228,
[197] = 229,
[198] = 230,
[199] = 231,
[200] = 232,
[201] = 233,
[202] = 234,
[203] = 235,
[204] = 236,
[205] = 237,
[206] = 238,
[207] = 239,
[208] = 240,
[209] = 241,
[210] = 242,
[211] = 243,
[212] = 244,
[213] = 245,
[214] = 246,
[216] = 248,
[217] = 249,
[218] = 250,
[219] = 251,
[220] = 252,
[221] = 253,
[222] = 254,
[256] = 257,
[258] = 259,
[260] = 261,
[262] = 263,
[264] = 265,
[266] = 267,
[268] = 269,
[270] = 271,
[272] = 273,
[274] = 275,
[276] = 277,
[278] = 279,
[280] = 281,
[282] = 283,
[284] = 285,
[286] = 287,
[288] = 289,
[290] = 291,
[292] = 293,
[294] = 295,
[296] = 297,
[298] = 299,
[300] = 301,
[302] = 303,
[304] = 105,
[306] = 307,
[308] = 309,
[310] = 311,
[313] = 314,
[315] = 316,
[317] = 318,
[319] = 320,
[321] = 322,
[323] = 324,
[325] = 326,
[327] = 328,
[330] = 331,
[332] = 333,
[334] = 335,
[336] = 337,
[338] = 339,
[340] = 341,
[342] = 343,
[344] = 345,
[346] = 347,
[348] = 349,
[350] = 351,
[352] = 353,
[354] = 355,
[356] = 357,
[358] = 359,
[360] = 361,
[362] = 363,
[364] = 365,
[366] = 367,
[368] = 369,
[370] = 371,
[372] = 373,
[374] = 375,
[376] = 255,
[377] = 378,
[379] = 380,
[381] = 382,
[385] = 595,
[386] = 387,
[388] = 389,
[390] = 596,
[391] = 392,
[393] = 598,
[394] = 599,
[395] = 396,
[398] = 477,
[399] = 601,
[400] = 603,
[401] = 402,
[403] = 608,
[404] = 611,
[406] = 617,
[407] = 616,
[408] = 409,
[412] = 623,
[413] = 626,
[415] = 629,
[416] = 417,
[418] = 419,
[420] = 421,
[422] = 640,
[423] = 424,
[425] = 643,
[428] = 429,
[430] = 648,
[431] = 432,
[433] = 650,
[434] = 651,
[435] = 436,
[437] = 438,
[439] = 658,
[440] = 441,
[444] = 445,
[452] = 454,
[453] = 454,
[455] = 457,
[456] = 457,
[458] = 460,
[459] = 460,
[461] = 462,
[463] = 464,
[465] = 466,
[467] = 468,
[469] = 470,
[471] = 472,
[473] = 474,
[475] = 476,
[478] = 479,
[480] = 481,
[482] = 483,
[484] = 485,
[486] = 487,
[488] = 489,
[490] = 491,
[492] = 493,
[494] = 495,
[497] = 499,
[498] = 499,
[500] = 501,
[502] = 405,
[503] = 447,
[504] = 505,
[506] = 507,
[508] = 509,
[510] = 511,
[512] = 513,
[514] = 515,
[516] = 517,
[518] = 519,
[520] = 521,
[522] = 523,
[524] = 525,
[526] = 527,
[528] = 529,
[530] = 531,
[532] = 533,
[534] = 535,
[536] = 537,
[538] = 539,
[540] = 541,
[542] = 543,
[544] = 414,
[546] = 547,
[548] = 549,
[550] = 551,
[552] = 553,
[554] = 555,
[556] = 557,
[558] = 559,
[560] = 561,
[562] = 563,
[570] = 11365,
[571] = 572,
[573] = 410,
[574] = 11366,
[577] = 578,
[579] = 384,
[580] = 649,
[581] = 652,
[582] = 583,
[584] = 585,
[586] = 587,
[588] = 589,
[590] = 591,
[880] = 881,
[882] = 883,
[886] = 887,
[902] = 940,
[904] = 941,
[905] = 942,
[906] = 943,
[908] = 972,
[910] = 973,
[911] = 974,
[913] = 945,
[914] = 946,
[915] = 947,
[916] = 948,
[917] = 949,
[918] = 950,
[919] = 951,
[920] = 952,
[921] = 953,
[922] = 954,
[923] = 955,
[924] = 956,
[925] = 957,
[926] = 958,
[927] = 959,
[928] = 960,
[929] = 961,
[931] = 963,
[932] = 964,
[933] = 965,
[934] = 966,
[935] = 967,
[936] = 968,
[937] = 969,
[938] = 970,
[939] = 971,
[975] = 983,
[984] = 985,
[986] = 987,
[988] = 989,
[990] = 991,
[992] = 993,
[994] = 995,
[996] = 997,
[998] = 999,
[1000] = 1001,
[1002] = 1003,
[1004] = 1005,
[1006] = 1007,
[1012] = 952,
[1015] = 1016,
[1017] = 1010,
[1018] = 1019,
[1021] = 891,
[1022] = 892,
[1023] = 893,
[1024] = 1104,
[1025] = 1105,
[1026] = 1106,
[1027] = 1107,
[1028] = 1108,
[1029] = 1109,
[1030] = 1110,
[1031] = 1111,
[1032] = 1112,
[1033] = 1113,
[1034] = 1114,
[1035] = 1115,
[1036] = 1116,
[1037] = 1117,
[1038] = 1118,
[1039] = 1119,
[1040] = 1072,
[1041] = 1073,
[1042] = 1074,
[1043] = 1075,
[1044] = 1076,
[1045] = 1077,
[1046] = 1078,
[1047] = 1079,
[1048] = 1080,
[1049] = 1081,
[1050] = 1082,
[1051] = 1083,
[1052] = 1084,
[1053] = 1085,
[1054] = 1086,
[1055] = 1087,
[1056] = 1088,
[1057] = 1089,
[1058] = 1090,
[1059] = 1091,
[1060] = 1092,
[1061] = 1093,
[1062] = 1094,
[1063] = 1095,
[1064] = 1096,
[1065] = 1097,
[1066] = 1098,
[1067] = 1099,
[1068] = 1100,
[1069] = 1101,
[1070] = 1102,
[1071] = 1103,
[1120] = 1121,
[1122] = 1123,
[1124] = 1125,
[1126] = 1127,
[1128] = 1129,
[1130] = 1131,
[1132] = 1133,
[1134] = 1135,
[1136] = 1137,
[1138] = 1139,
[1140] = 1141,
[1142] = 1143,
[1144] = 1145,
[1146] = 1147,
[1148] = 1149,
[1150] = 1151,
[1152] = 1153,
[1162] = 1163,
[1164] = 1165,
[1166] = 1167,
[1168] = 1169,
[1170] = 1171,
[1172] = 1173,
[1174] = 1175,
[1176] = 1177,
[1178] = 1179,
[1180] = 1181,
[1182] = 1183,
[1184] = 1185,
[1186] = 1187,
[1188] = 1189,
[1190] = 1191,
[1192] = 1193,
[1194] = 1195,
[1196] = 1197,
[1198] = 1199,
[1200] = 1201,
[1202] = 1203,
[1204] = 1205,
[1206] = 1207,
[1208] = 1209,
[1210] = 1211,
[1212] = 1213,
[1214] = 1215,
[1216] = 1231,
[1217] = 1218,
[1219] = 1220,
[1221] = 1222,
[1223] = 1224,
[1225] = 1226,
[1227] = 1228,
[1229] = 1230,
[1232] = 1233,
[1234] = 1235,
[1236] = 1237,
[1238] = 1239,
[1240] = 1241,
[1242] = 1243,
[1244] = 1245,
[1246] = 1247,
[1248] = 1249,
[1250] = 1251,
[1252] = 1253,
[1254] = 1255,
[1256] = 1257,
[1258] = 1259,
[1260] = 1261,
[1262] = 1263,
[1264] = 1265,
[1266] = 1267,
[1268] = 1269,
[1270] = 1271,
[1272] = 1273,
[1274] = 1275,
[1276] = 1277,
[1278] = 1279,
[1280] = 1281,
[1282] = 1283,
[1284] = 1285,
[1286] = 1287,
[1288] = 1289,
[1290] = 1291,
[1292] = 1293,
[1294] = 1295,
[1296] = 1297,
[1298] = 1299,
[1300] = 1301,
[1302] = 1303,
[1304] = 1305,
[1306] = 1307,
[1308] = 1309,
[1310] = 1311,
[1312] = 1313,
[1314] = 1315,
[1316] = 1317,
[1318] = 1319,
[1329] = 1377,
[1330] = 1378,
[1331] = 1379,
[1332] = 1380,
[1333] = 1381,
[1334] = 1382,
[1335] = 1383,
[1336] = 1384,
[1337] = 1385,
[1338] = 1386,
[1339] = 1387,
[1340] = 1388,
[1341] = 1389,
[1342] = 1390,
[1343] = 1391,
[1344] = 1392,
[1345] = 1393,
[1346] = 1394,
[1347] = 1395,
[1348] = 1396,
[1349] = 1397,
[1350] = 1398,
[1351] = 1399,
[1352] = 1400,
[1353] = 1401,
[1354] = 1402,
[1355] = 1403,
[1356] = 1404,
[1357] = 1405,
[1358] = 1406,
[1359] = 1407,
[1360] = 1408,
[1361] = 1409,
[1362] = 1410,
[1363] = 1411,
[1364] = 1412,
[1365] = 1413,
[1366] = 1414,
[4256] = 11520,
[4257] = 11521,
[4258] = 11522,
[4259] = 11523,
[4260] = 11524,
[4261] = 11525,
[4262] = 11526,
[4263] = 11527,
[4264] = 11528,
[4265] = 11529,
[4266] = 11530,
[4267] = 11531,
[4268] = 11532,
[4269] = 11533,
[4270] = 11534,
[4271] = 11535,
[4272] = 11536,
[4273] = 11537,
[4274] = 11538,
[4275] = 11539,
[4276] = 11540,
[4277] = 11541,
[4278] = 11542,
[4279] = 11543,
[4280] = 11544,
[4281] = 11545,
[4282] = 11546,
[4283] = 11547,
[4284] = 11548,
[4285] = 11549,
[4286] = 11550,
[4287] = 11551,
[4288] = 11552,
[4289] = 11553,
[4290] = 11554,
[4291] = 11555,
[4292] = 11556,
[4293] = 11557,
[7680] = 7681,
[7682] = 7683,
[7684] = 7685,
[7686] = 7687,
[7688] = 7689,
[7690] = 7691,
[7692] = 7693,
[7694] = 7695,
[7696] = 7697,
[7698] = 7699,
[7700] = 7701,
[7702] = 7703,
[7704] = 7705,
[7706] = 7707,
[7708] = 7709,
[7710] = 7711,
[7712] = 7713,
[7714] = 7715,
[7716] = 7717,
[7718] = 7719,
[7720] = 7721,
[7722] = 7723,
[7724] = 7725,
[7726] = 7727,
[7728] = 7729,
[7730] = 7731,
[7732] = 7733,
[7734] = 7735,
[7736] = 7737,
[7738] = 7739,
[7740] = 7741,
[7742] = 7743,
[7744] = 7745,
[7746] = 7747,
[7748] = 7749,
[7750] = 7751,
[7752] = 7753,
[7754] = 7755,
[7756] = 7757,
[7758] = 7759,
[7760] = 7761,
[7762] = 7763,
[7764] = 7765,
[7766] = 7767,
[7768] = 7769,
[7770] = 7771,
[7772] = 7773,
[7774] = 7775,
[7776] = 7777,
[7778] = 7779,
[7780] = 7781,
[7782] = 7783,
[7784] = 7785,
[7786] = 7787,
[7788] = 7789,
[7790] = 7791,
[7792] = 7793,
[7794] = 7795,
[7796] = 7797,
[7798] = 7799,
[7800] = 7801,
[7802] = 7803,
[7804] = 7805,
[7806] = 7807,
[7808] = 7809,
[7810] = 7811,
[7812] = 7813,
[7814] = 7815,
[7816] = 7817,
[7818] = 7819,
[7820] = 7821,
[7822] = 7823,
[7824] = 7825,
[7826] = 7827,
[7828] = 7829,
[7838] = 223,
[7840] = 7841,
[7842] = 7843,
[7844] = 7845,
[7846] = 7847,
[7848] = 7849,
[7850] = 7851,
[7852] = 7853,
[7854] = 7855,
[7856] = 7857,
[7858] = 7859,
[7860] = 7861,
[7862] = 7863,
[7864] = 7865,
[7866] = 7867,
[7868] = 7869,
[7870] = 7871,
[7872] = 7873,
[7874] = 7875,
[7876] = 7877,
[7878] = 7879,
[7880] = 7881,
[7882] = 7883,
[7884] = 7885,
[7886] = 7887,
[7888] = 7889,
[7890] = 7891,
[7892] = 7893,
[7894] = 7895,
[7896] = 7897,
[7898] = 7899,
[7900] = 7901,
[7902] = 7903,
[7904] = 7905,
[7906] = 7907,
[7908] = 7909,
[7910] = 7911,
[7912] = 7913,
[7914] = 7915,
[7916] = 7917,
[7918] = 7919,
[7920] = 7921,
[7922] = 7923,
[7924] = 7925,
[7926] = 7927,
[7928] = 7929,
[7930] = 7931,
[7932] = 7933,
[7934] = 7935,
[7944] = 7936,
[7945] = 7937,
[7946] = 7938,
[7947] = 7939,
[7948] = 7940,
[7949] = 7941,
[7950] = 7942,
[7951] = 7943,
[7960] = 7952,
[7961] = 7953,
[7962] = 7954,
[7963] = 7955,
[7964] = 7956,
[7965] = 7957,
[7976] = 7968,
[7977] = 7969,
[7978] = 7970,
[7979] = 7971,
[7980] = 7972,
[7981] = 7973,
[7982] = 7974,
[7983] = 7975,
[7992] = 7984,
[7993] = 7985,
[7994] = 7986,
[7995] = 7987,
[7996] = 7988,
[7997] = 7989,
[7998] = 7990,
[7999] = 7991,
[8008] = 8000,
[8009] = 8001,
[8010] = 8002,
[8011] = 8003,
[8012] = 8004,
[8013] = 8005,
[8025] = 8017,
[8027] = 8019,
[8029] = 8021,
[8031] = 8023,
[8040] = 8032,
[8041] = 8033,
[8042] = 8034,
[8043] = 8035,
[8044] = 8036,
[8045] = 8037,
[8046] = 8038,
[8047] = 8039,
[8072] = 8064,
[8073] = 8065,
[8074] = 8066,
[8075] = 8067,
[8076] = 8068,
[8077] = 8069,
[8078] = 8070,
[8079] = 8071,
[8088] = 8080,
[8089] = 8081,
[8090] = 8082,
[8091] = 8083,
[8092] = 8084,
[8093] = 8085,
[8094] = 8086,
[8095] = 8087,
[8104] = 8096,
[8105] = 8097,
[8106] = 8098,
[8107] = 8099,
[8108] = 8100,
[8109] = 8101,
[8110] = 8102,
[8111] = 8103,
[8120] = 8112,
[8121] = 8113,
[8122] = 8048,
[8123] = 8049,
[8124] = 8115,
[8136] = 8050,
[8137] = 8051,
[8138] = 8052,
[8139] = 8053,
[8140] = 8131,
[8152] = 8144,
[8153] = 8145,
[8154] = 8054,
[8155] = 8055,
[8168] = 8160,
[8169] = 8161,
[8170] = 8058,
[8171] = 8059,
[8172] = 8165,
[8184] = 8056,
[8185] = 8057,
[8186] = 8060,
[8187] = 8061,
[8188] = 8179,
[8486] = 969,
[8490] = 107,
[8491] = 229,
[8498] = 8526,
[8544] = 8560,
[8545] = 8561,
[8546] = 8562,
[8547] = 8563,
[8548] = 8564,
[8549] = 8565,
[8550] = 8566,
[8551] = 8567,
[8552] = 8568,
[8553] = 8569,
[8554] = 8570,
[8555] = 8571,
[8556] = 8572,
[8557] = 8573,
[8558] = 8574,
[8559] = 8575,
[8579] = 8580,
[9398] = 9424,
[9399] = 9425,
[9400] = 9426,
[9401] = 9427,
[9402] = 9428,
[9403] = 9429,
[9404] = 9430,
[9405] = 9431,
[9406] = 9432,
[9407] = 9433,
[9408] = 9434,
[9409] = 9435,
[9410] = 9436,
[9411] = 9437,
[9412] = 9438,
[9413] = 9439,
[9414] = 9440,
[9415] = 9441,
[9416] = 9442,
[9417] = 9443,
[9418] = 9444,
[9419] = 9445,
[9420] = 9446,
[9421] = 9447,
[9422] = 9448,
[9423] = 9449,
[11264] = 11312,
[11265] = 11313,
[11266] = 11314,
[11267] = 11315,
[11268] = 11316,
[11269] = 11317,
[11270] = 11318,
[11271] = 11319,
[11272] = 11320,
[11273] = 11321,
[11274] = 11322,
[11275] = 11323,
[11276] = 11324,
[11277] = 11325,
[11278] = 11326,
[11279] = 11327,
[11280] = 11328,
[11281] = 11329,
[11282] = 11330,
[11283] = 11331,
[11284] = 11332,
[11285] = 11333,
[11286] = 11334,
[11287] = 11335,
[11288] = 11336,
[11289] = 11337,
[11290] = 11338,
[11291] = 11339,
[11292] = 11340,
[11293] = 11341,
[11294] = 11342,
[11295] = 11343,
[11296] = 11344,
[11297] = 11345,
[11298] = 11346,
[11299] = 11347,
[11300] = 11348,
[11301] = 11349,
[11302] = 11350,
[11303] = 11351,
[11304] = 11352,
[11305] = 11353,
[11306] = 11354,
[11307] = 11355,
[11308] = 11356,
[11309] = 11357,
[11310] = 11358,
[11360] = 11361,
[11362] = 619,
[11363] = 7549,
[11364] = 637,
[11367] = 11368,
[11369] = 11370,
[11371] = 11372,
[11373] = 593,
[11374] = 625,
[11375] = 592,
[11376] = 594,
[11378] = 11379,
[11381] = 11382,
[11390] = 575,
[11391] = 576,
[11392] = 11393,
[11394] = 11395,
[11396] = 11397,
[11398] = 11399,
[11400] = 11401,
[11402] = 11403,
[11404] = 11405,
[11406] = 11407,
[11408] = 11409,
[11410] = 11411,
[11412] = 11413,
[11414] = 11415,
[11416] = 11417,
[11418] = 11419,
[11420] = 11421,
[11422] = 11423,
[11424] = 11425,
[11426] = 11427,
[11428] = 11429,
[11430] = 11431,
[11432] = 11433,
[11434] = 11435,
[11436] = 11437,
[11438] = 11439,
[11440] = 11441,
[11442] = 11443,
[11444] = 11445,
[11446] = 11447,
[11448] = 11449,
[11450] = 11451,
[11452] = 11453,
[11454] = 11455,
[11456] = 11457,
[11458] = 11459,
[11460] = 11461,
[11462] = 11463,
[11464] = 11465,
[11466] = 11467,
[11468] = 11469,
[11470] = 11471,
[11472] = 11473,
[11474] = 11475,
[11476] = 11477,
[11478] = 11479,
[11480] = 11481,
[11482] = 11483,
[11484] = 11485,
[11486] = 11487,
[11488] = 11489,
[11490] = 11491,
[11499] = 11500,
[11501] = 11502,
[42560] = 42561,
[42562] = 42563,
[42564] = 42565,
[42566] = 42567,
[42568] = 42569,
[42570] = 42571,
[42572] = 42573,
[42574] = 42575,
[42576] = 42577,
[42578] = 42579,
[42580] = 42581,
[42582] = 42583,
[42584] = 42585,
[42586] = 42587,
[42588] = 42589,
[42590] = 42591,
[42592] = 42593,
[42594] = 42595,
[42596] = 42597,
[42598] = 42599,
[42600] = 42601,
[42602] = 42603,
[42604] = 42605,
[42624] = 42625,
[42626] = 42627,
[42628] = 42629,
[42630] = 42631,
[42632] = 42633,
[42634] = 42635,
[42636] = 42637,
[42638] = 42639,
[42640] = 42641,
[42642] = 42643,
[42644] = 42645,
[42646] = 42647,
[42786] = 42787,
[42788] = 42789,
[42790] = 42791,
[42792] = 42793,
[42794] = 42795,
[42796] = 42797,
[42798] = 42799,
[42802] = 42803,
[42804] = 42805,
[42806] = 42807,
[42808] = 42809,
[42810] = 42811,
[42812] = 42813,
[42814] = 42815,
[42816] = 42817,
[42818] = 42819,
[42820] = 42821,
[42822] = 42823,
[42824] = 42825,
[42826] = 42827,
[42828] = 42829,
[42830] = 42831,
[42832] = 42833,
[42834] = 42835,
[42836] = 42837,
[42838] = 42839,
[42840] = 42841,
[42842] = 42843,
[42844] = 42845,
[42846] = 42847,
[42848] = 42849,
[42850] = 42851,
[42852] = 42853,
[42854] = 42855,
[42856] = 42857,
[42858] = 42859,
[42860] = 42861,
[42862] = 42863,
[42873] = 42874,
[42875] = 42876,
[42877] = 7545,
[42878] = 42879,
[42880] = 42881,
[42882] = 42883,
[42884] = 42885,
[42886] = 42887,
[42891] = 42892,
[42893] = 613,
[42896] = 42897,
[42912] = 42913,
[42914] = 42915,
[42916] = 42917,
[42918] = 42919,
[42920] = 42921,
[65313] = 65345,
[65314] = 65346,
[65315] = 65347,
[65316] = 65348,
[65317] = 65349,
[65318] = 65350,
[65319] = 65351,
[65320] = 65352,
[65321] = 65353,
[65322] = 65354,
[65323] = 65355,
[65324] = 65356,
[65325] = 65357,
[65326] = 65358,
[65327] = 65359,
[65328] = 65360,
[65329] = 65361,
[65330] = 65362,
[65331] = 65363,
[65332] = 65364,
[65333] = 65365,
[65334] = 65366,
[65335] = 65367,
[65336] = 65368,
[65337] = 65369,
[65338] = 65370,
[66560] = 66600,
[66561] = 66601,
[66562] = 66602,
[66563] = 66603,
[66564] = 66604,
[66565] = 66605,
[66566] = 66606,
[66567] = 66607,
[66568] = 66608,
[66569] = 66609,
[66570] = 66610,
[66571] = 66611,
[66572] = 66612,
[66573] = 66613,
[66574] = 66614,
[66575] = 66615,
[66576] = 66616,
[66577] = 66617,
[66578] = 66618,
[66579] = 66619,
[66580] = 66620,
[66581] = 66621,
[66582] = 66622,
[66583] = 66623,
[66584] = 66624,
[66585] = 66625,
[66586] = 66626,
[66587] = 66627,
[66588] = 66628,
[66589] = 66629,
[66590] = 66630,
[66591] = 66631,
[66592] = 66632,
[66593] = 66633,
[66594] = 66634,
[66595] = 66635,
[66596] = 66636,
[66597] = 66637,
[66598] = 66638,
[66599] = 66639,
}
return {utf8_upper, utf8_lower}
--]==]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment