Created
May 20, 2013 10:38
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Library 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[==[ | |
-- 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 | |
--]==] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
ChangeLog | |
========= | |
v2.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. | |
--]] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[==[ | |
-- 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} | |
--]==] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[==[ | |
-- 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) | |
--]==] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[==[ | |
-- 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 | |
} | |
--]==] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[==[ | |
-- 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 | |
--]==] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[==[ | |
-- 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} | |
--]==] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
VERSION = 2.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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[==[ | |
-- 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 | |
--]==] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[==[ | |
-- 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 } | |
--]==] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[==[ | |
-- 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 | |
--]==] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[==[ | |
-- 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 | |
--]==] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[==[ | |
-- 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