--# Passdrag | |
Passdrag | |
= class() -- Looks odd as code, but nice if it crashes | |
local ellipseMesh = function(width, height, top, bottom, change) -- Function to create a(n) circle/ellipse mesh with a gradient | |
local pwidth, pheight, ptop, pbototom, pchange | |
pwidth = width or 128 | |
if type(height) == "number" or height == nil then | |
pheight = height or width | |
ptop = top or color(255) | |
if type(bottom) == "userdata" then | |
pbottom = bottom or ptop | |
pchange = change or 1 | |
else | |
pbottom = ptop | |
pchange = bottom or 1 | |
end | |
else | |
pheight = width | |
ptop = height or color(255) | |
if type(top) == "userdata" then | |
pbottom = top or ptop | |
pchange = bottom or 1 | |
else | |
pbottom = ptop | |
pchange = top or 1 | |
end | |
end | |
pwidth, pheight = pwidth / 2, pheight / 2 | |
local m = mesh() | |
local v = {} | |
local c = {} | |
local p = {} | |
for d = 0, 360, pchange do | |
local r = math.rad(d) | |
table.insert(p, vec2(pwidth * math.sin(r), pheight * math.cos(r))) | |
end | |
v = triangulate(p) | |
m.vertices = v | |
for k, v in ipairs(v) do | |
table.insert(c, ptop:mix(pbottom, v:normalize().y * 0.5 + 0.5)) | |
end | |
m.colors = c | |
return m | |
end | |
function Passdrag:init(x, y, size) -- Passdrag GUI & handler setup | |
self.rot = {a = 0} | |
self.tid = 0 | |
self.size = size | |
self.x, self.y = x, y | |
self.ePos = vec2(0, size) | |
self.styles = { | |
Light = { | |
bg = color(255), | |
txt = color(200), | |
cFill = color(255), | |
cStroke = color(220), | |
cDark = color(210), | |
pWidth = 5, | |
}, | |
Dark = { | |
bg = color(0), | |
txt = color(40), | |
cFill = color(30), | |
cStroke = color(25), | |
cDark = color(20), | |
pWidth = 5 | |
}, | |
Retro = { | |
bg = color(128, 96, 64, 255), | |
txt = color(64, 24, 16), | |
cFill = color(136, 102, 74, 255), | |
cStroke = color(74, 32, 24), | |
cDark = color(64, 24, 16), | |
pWidth = 3, | |
}, | |
Nature = { | |
bg = color(191, 255, 191), | |
txt = color(31, 63, 31), | |
cFill = color(74, 136, 74), | |
cStroke = color(63, 127, 63), | |
cDark = color(48, 112, 48), | |
pWidth = 5, | |
}, | |
Sky = { | |
bg = color(191, 223, 255), | |
txt = color(255), | |
cFill = color(95, 191, 255), | |
cStroke = color(240, 240), | |
cDark = color(74, 120, 255), | |
pWidth = 8, | |
}, | |
} | |
self.oldStyles = self.styles | |
self.style = "Light" | |
self.darkness = {a = 0} | |
self:updateMesh() | |
self.sequence = "" | |
self.lastChar = "" | |
end | |
function Passdrag:updateMesh() -- Update the gradiented mesh, not recommended for manual use | |
self.m = ellipseMesh(self.size / 3 - self.styles[self.style].pWidth, self.styles[self.style].cFill, self.styles[self.style].cDark, 10) | |
end | |
function Passdrag:setStyle(x) -- Set style to x | |
self.style = x | |
self:updateMesh() | |
end | |
function Passdrag:addStyle(x, y) -- Add style x with a preset of y | |
self.styles[x] = y | |
end | |
function Passdrag:duplicateStyle(x, y) -- Make a new style y, with the presets of x | |
self.styles[y] = self.styles[x] | |
end | |
function Passdrag:deleteStyle(x) -- Delete style x | |
self.styles[x] = nil | |
end | |
function Passdrag:restoreStyle(x) -- Restore style (x or current) to its original preset | |
x = x or self.style | |
self.styles[x] = self.oldStyles[x] | |
end | |
function Passdrag:restoreStyles() -- Restore all styles to their original presets | |
self.styles = self.oldStyles | |
end | |
function Passdrag:addChar(x) -- Add character to current drag, not recommended for manual use | |
if x ~= self.lastChar then | |
self.sequence = self.sequence .. x | |
self.lastChar = x | |
end | |
end | |
function Passdrag:resetSequence() -- Reset drag | |
self.sequence = "" | |
self.lastChar = "" | |
end | |
function Passdrag:draw() -- Draw the GUI and update | |
for k, v in pairs(self.styles[self.style]) do | |
self["g" .. k] = v | |
end | |
local checks = {"bg", "txt", "cFill", "cStroke", "cDark", "pWidth"} | |
for k, v in ipairs(checks) do | |
if self["s" .. v] ~= nil then | |
self.styles[style][v] = self["s" .. v] | |
self["s" .. v] = nil | |
end | |
end | |
local deg = math.floor(math.abs((math.deg(self.rot.a) - 90) / 45) + 0.5) | |
if deg == 8 then | |
deg = 0 | |
end | |
for i = 1, 3 do | |
if deg <= 5 then | |
deg = deg + 2 | |
elseif deg >= 6 then | |
deg = deg - 6 | |
end | |
end | |
self:addChar(tostring(deg + 1)) | |
local c = {} | |
for k, v in ipairs(self.m.vertices) do | |
table.insert(c, self.styles[self.style].cFill:mix(self.styles[self.style].cDark:mix(self.styles[self.style].cFill, self.darkness.a), v:normalize().y * 0.5 + 0.5)) | |
end | |
self.m.colors = c | |
pushMatrix() | |
pushStyle() | |
fill(self.styles[self.style].cFill) | |
stroke(self.styles[self.style].cStroke) | |
strokeWidth(self.styles[self.style].pWidth) | |
self.ePos.x, self.ePos.y = math.sin(-self.rot.a) * self.size / 2, math.cos(-self.rot.a) * self.size / 2 | |
translate(self.x, self.y) | |
pushMatrix() | |
translate(self.ePos.x, self.ePos.y) | |
ellipse(0, 0, self.size / 3) | |
self.m:draw() | |
fill(0, 0) | |
ellipse(0, 0, self.size / 3) | |
popMatrix() | |
for i = 0, 7 do | |
pushMatrix() | |
rotate(i * (360 / 8)) | |
line(self.size - self.size / 4, 0, self.size, 0) | |
popMatrix() | |
end | |
popStyle() | |
popMatrix() | |
end | |
function Passdrag:touched(touch) -- Handles touches & animations on the GUI | |
if self.tid == 0 and touch.state == BEGAN and vec2(touch.x - self.x, touch.y - self.y):dist(self.ePos) < self.size / 3 then | |
self.tid = touch.id | |
if self.t ~= nil then | |
tween.stop(self.t) | |
end | |
if self.t2 ~= nil then | |
tween.stop(self.t2) | |
end | |
self.t = tween(0.45, self.darkness, {a = 1}, tween.easing.quadInOut) | |
end | |
if touch.id == self.tid and (touch.state == BEGAN or touch.state == MOVING) then | |
self.rot.a = math.atan2(touch.y - HEIGHT / 2, touch.x - WIDTH / 2) - math.pi / 2 | |
local deg = math.floor(math.abs((math.deg(self.rot.a) - 90) / 45) + 0.5) | |
if deg == 8 then | |
deg = 0 | |
end | |
for i = 1, 3 do | |
if deg <= 5 then | |
deg = deg + 2 | |
elseif deg >= 6 then | |
deg = deg - 6 | |
end | |
end | |
self:addChar(tostring(deg + 1)) | |
end | |
if touch.id == self.tid and (touch.state == ENDED or touch.state == CANCELLED) then | |
self.tid = 0 | |
tween.stop(self.t) | |
self.t = tween(0.45, self.darkness, {a = 0}, tween.easing.quadInOut) | |
self.t2 = tween(0.45, self.rot, {a = math.rad(((math.floor(math.deg(self.rot.a) / 45 - 0.5) + 0.5) + 0.5) * 45)}) | |
end | |
end | |
--# Main | |
-- Passdrag | |
function setup() | |
print() | |
pd = Passdrag(WIDTH / 2, HEIGHT / 2, 200) | |
styles = {"Light", "Dark", "Retro", "Nature", "Sky"} | |
parameter.integer("Style", 1, #styles, 1) | |
parameter.watch("styles[Style]") | |
parameter.watch("pd.sequence") | |
parameter.action("Save Passdrag", function() | |
savePassdrag() | |
output.clear() | |
print() | |
tween.delay(0.1, function() | |
output.clear() | |
print() | |
print("Passdrag saved") | |
end) | |
end) | |
parameter.watch("Saved_Passdrag") | |
parameter.watch("Passdrag_Matches_Saved") | |
end | |
function draw() | |
pd:setStyle(styles[Style]) | |
background(pd.styles[pd.style].bg) | |
Saved_Passdrag = readPassdrag() | |
Passdrag_Matches_Saved = pd.sequence == Saved_Passdrag | |
pd:draw() | |
font("HelveticaNeue-Light") | |
fontSize(48) | |
fill(pd.styles[pd.style].txt) | |
text("Passdrag", WIDTH / 2, HEIGHT - HEIGHT / 16) | |
fontSize(24) | |
text("Your drag: " .. pd.sequence, WIDTH / 2, HEIGHT - HEIGHT / 8) | |
text("Drag length: " .. pd.sequence:len(), WIDTH / 2, HEIGHT - HEIGHT / 6) | |
end | |
function touched(touch) | |
pd:touched(touch) | |
end | |
function savePassdrag() | |
local fileName = "passdrag.txt" | |
local file = os.getenv("HOME") .. "/Documents/" .. fileName | |
local wFd = io.open(file, "w") | |
wFd:write(pd.sequence) | |
wFd:close() | |
end | |
function readPassdrag() | |
local fileName = "passdrag.txt" | |
local file = os.getenv("HOME") .. "/Documents/" .. fileName | |
local rFd = io.open(file, "r") | |
local v = "" | |
if rFd ~= nil then | |
for i in rFd:lines() do | |
v = v .. i | |
end | |
end | |
if rFd ~= nil then | |
rFd:close() | |
end | |
return v | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment