Compare commits
7 commits
1894988ac3
...
71e06141d0
Author | SHA1 | Date | |
---|---|---|---|
71e06141d0 | |||
05bfac2221 | |||
f73073a9c6 | |||
63b1a08df8 | |||
83d23d8d72 | |||
4ad840fb1f | |||
110c0bf19d |
7 changed files with 494 additions and 430 deletions
7
.vscode/settings.json
vendored
Normal file
7
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"Lua.runtime.version": "LuaJIT",
|
||||
"Lua.workspace.library": [
|
||||
"${3rd}/love2d/library"
|
||||
],
|
||||
"Lua.workspace.checkThirdParty": false
|
||||
}
|
234
dropdown.lua
Normal file
234
dropdown.lua
Normal file
|
@ -0,0 +1,234 @@
|
|||
local self = {}
|
||||
|
||||
local dropdowns = {}
|
||||
|
||||
local dropdownValueCache = {}
|
||||
|
||||
local maxDropdown = 16
|
||||
|
||||
self.openDropdown = 0
|
||||
local dropdownScroll = 0
|
||||
local dropdownScrollE = 0
|
||||
|
||||
local function skeys(t)
|
||||
local k = {}
|
||||
for n,v in pairs(t) do table.insert(k, {n, v}) end
|
||||
table.sort(k, function(a, b) return a[2].i < b[2].i end)
|
||||
local k2 = {}
|
||||
for _,v in ipairs(k) do table.insert(k2, v[1]) end
|
||||
return k2
|
||||
end
|
||||
|
||||
function self.get(index)
|
||||
return dropdowns[index]
|
||||
end
|
||||
|
||||
function self.selected(index)
|
||||
return dropdowns[index].options[dropdowns[index].selected]
|
||||
end
|
||||
|
||||
function self.kget(key)
|
||||
for _, v in ipairs(dropdowns) do
|
||||
if v.name == key then
|
||||
return v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function self.kselected(key)
|
||||
for _, v in ipairs(dropdowns) do
|
||||
if v.name == key then
|
||||
return v.options[v.selected]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local dropdownId
|
||||
local function insertDropdown(tab, f)
|
||||
dropdownId = dropdownId + 1
|
||||
f.selected = (self.kget(f.name) or dropdownValueCache[f.name] or {selected = 1}).selected
|
||||
f.selected = (f.selected - 1) % #f.options + 1
|
||||
return table.insert(tab, f)
|
||||
end
|
||||
function self.createDropdowns()
|
||||
local d = {}
|
||||
dropdownId = 0
|
||||
insertDropdown(d, {
|
||||
x = outerpadding,
|
||||
y = outerpadding,
|
||||
width = dropdownWidth,
|
||||
options = {
|
||||
'Preview Ease',
|
||||
'Mix Eases',
|
||||
'Create Ease'
|
||||
},
|
||||
name = 'mode'
|
||||
})
|
||||
|
||||
if d[dropdownId].selected == 1 then -- preview ease
|
||||
insertDropdown(d, {
|
||||
x = outerpadding + dropdownWidth + padding,
|
||||
y = outerpadding,
|
||||
width = dropdownWidth,
|
||||
options = skeys(ease.eases),
|
||||
name = 'ease1'
|
||||
})
|
||||
ease.ease = ease.eases[d[dropdownId].options[d[dropdownId].selected]].f
|
||||
elseif d[dropdownId].selected == 2 then -- mix eases
|
||||
insertDropdown(d, {
|
||||
x = outerpadding + dropdownWidth + padding,
|
||||
y = outerpadding,
|
||||
width = dropdownWidth,
|
||||
options = skeys(ease.eases),
|
||||
name = 'ease1'
|
||||
})
|
||||
insertDropdown(d, {
|
||||
x = outerpadding + dropdownWidth + padding + dropdownWidth + padding,
|
||||
y = outerpadding,
|
||||
width = dropdownWidth,
|
||||
options = skeys(ease.eases),
|
||||
name = 'ease2'
|
||||
})
|
||||
ease.ease = ease.mixEase(ease.eases[d[dropdownId - 1].options[d[dropdownId - 1].selected]].f, ease.eases[d[dropdownId].options[d[dropdownId].selected]].f, mixpoint)
|
||||
elseif d[dropdownId].selected == 3 then -- create eases
|
||||
insertDropdown(d, {
|
||||
x = outerpadding + dropdownWidth + padding,
|
||||
y = outerpadding,
|
||||
width = dropdownWidth,
|
||||
options = skeys(ease.eases),
|
||||
name = 'ease1'
|
||||
})
|
||||
end
|
||||
|
||||
dropdowns = d
|
||||
minEase = (self.kselected('ease1') and ease.eases[self.kselected('ease1')].min == -1) or (self.kselected('ease2') and ease.eases[self.kselected('ease2')].min == -1)
|
||||
end
|
||||
|
||||
function self.update(dt)
|
||||
if self.openDropdown ~= 0 then
|
||||
dropdownScroll = math.max(dropdownScroll, -(#self.get(self.openDropdown).options - maxDropdown + 1))
|
||||
dropdownScroll = math.min(dropdownScroll, 0)
|
||||
dropdownScrollE = mix(dropdownScrollE, dropdownScroll, dt * 10)
|
||||
end
|
||||
end
|
||||
|
||||
function self.render()
|
||||
local mx, my = love.mouse.getPosition()
|
||||
for i,v in ipairs(dropdowns) do
|
||||
local x, y, w, h = v.x, v.y, v.width, love.graphics.getFont():getHeight() + margin
|
||||
|
||||
love.graphics.setColor(0.06, 0.06, 0.12, 0.3)
|
||||
if love.mouse.getX() > x and love.mouse.getX() < x + w and love.mouse.getY() > y and love.mouse.getY() < y + h then
|
||||
love.graphics.setColor(0.8, 0.8, 1, love.mouse.isDown(1) and 0.4 or 0.3)
|
||||
end
|
||||
love.graphics.rectangle('fill', x, y, w, h)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
|
||||
love.graphics.rectangle('line', x, y, w, h)
|
||||
love.graphics.print(self.selected(i), x + margin/2, y + margin/2)
|
||||
love.graphics.rectangle('line', x + w - h, y, h, h)
|
||||
love.graphics.polygon('line', x + w - h/2 + 0.3 * h, y + h/2 - 0.3 * h, x + w - h/2 - 0.3 * h, y + h/2 - 0.3 * h, x + w - h/2, y + h/2 + 0.3 * h)
|
||||
|
||||
if self.openDropdown == i then
|
||||
for i,o in ipairs(v.options) do
|
||||
local x, y, w, h = x, y + i * h, w, h
|
||||
|
||||
y = y + dropdownScrollE * h
|
||||
local gi = y / h
|
||||
if gi > (maxDropdown + 1) or gi < 1 then
|
||||
goto continue
|
||||
end
|
||||
|
||||
-- help
|
||||
local a = (1 - math.min(math.max((1 - (maxDropdown - gi)) * (1 - (math.abs(dropdownScrollE) / (#v.options - maxDropdown + 1))), 0), 1)) * math.max(math.min(gi - 1, 1), 0)
|
||||
|
||||
love.graphics.setColor(0.06, 0.06, 0.12, 0.3 * a)
|
||||
if mx > x and mx < x + w and my > y and my < y + h then
|
||||
love.graphics.setColor(0.8, 0.8, 1, (love.mouse.isDown(1) and 0.4 or 0.3) * a)
|
||||
end
|
||||
love.graphics.rectangle('fill', x, y, w, h)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 0.75 * a)
|
||||
love.graphics.rectangle('line', x, y, w, h)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1 * a)
|
||||
love.graphics.print(v.options[i], x + 2, y + 2)
|
||||
|
||||
::continue::
|
||||
end
|
||||
|
||||
-- scrollwheel
|
||||
|
||||
if #v.options > maxDropdown then
|
||||
local displayed = maxDropdown / (#v.options)
|
||||
local scroll = math.abs(dropdownScrollE) / (#v.options - maxDropdown + 1)
|
||||
local size = margin
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 0.9)
|
||||
love.graphics.rectangle('fill', x + w - size, y + h + scroll * (1 - displayed) * (maxDropdown - 1) * h, size, displayed * (maxDropdown - 1) * h)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function self.mousepressed(x, y, m)
|
||||
local clickedDropdown = false
|
||||
for i,v in ipairs(dropdowns) do
|
||||
local h = love.graphics.getFont():getHeight() + margin
|
||||
if self.openDropdown == 0 then
|
||||
if x > v.x and x < v.x + v.width and y > v.y and y < v.y + h + margin then
|
||||
if m == 1 then
|
||||
self.openDropdown = i
|
||||
clickedDropdown = true
|
||||
dropdownScroll = 0
|
||||
dropdownScrollE = 0
|
||||
elseif m == 3 then
|
||||
dropdowns[i].selected = math.random(1, #dropdowns[i].options)
|
||||
self.createDropdowns()
|
||||
end
|
||||
end
|
||||
elseif self.openDropdown == i then
|
||||
if x > v.x and x < v.x + v.width and y > v.y + h and y < v.y + h * (math.min(#v.options, maxDropdown) + 1) and m == 1 then
|
||||
clickedDropdown = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not clickedDropdown and m == 1 then
|
||||
self.openDropdown = 0
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function self.mousereleased(x, y, m)
|
||||
for i,v in ipairs(dropdowns) do
|
||||
local h = love.graphics.getFont():getHeight() + margin
|
||||
if self.openDropdown == i then
|
||||
if x > v.x and x < v.x + v.width and y > v.y + h and y < v.y + h * (math.min(#v.options, maxDropdown) + 1) and m == 1 then
|
||||
v.selected = math.floor((y - v.y) / h - dropdownScrollE)
|
||||
self.openDropdown = 0
|
||||
dropdownValueCache[v.name] = {selected = v.selected}
|
||||
self.createDropdowns()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function self.wheelmoved(x, y)
|
||||
if self.openDropdown ~= 0 then
|
||||
dropdownScroll = dropdownScroll + y
|
||||
else
|
||||
local mx, my = love.mouse.getPosition()
|
||||
for i,v in ipairs(dropdowns) do
|
||||
local h = love.graphics.getFont():getHeight() + margin
|
||||
if mx > v.x and mx < v.x + v.width and my > v.y and my < v.y + h + margin then
|
||||
dropdowns[i].selected = dropdowns[i].selected - math.floor(y)
|
||||
dropdowns[i].selected = (dropdowns[i].selected - 1) % #dropdowns[i].options + 1
|
||||
self.createDropdowns()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
38
ease.lua
Normal file
38
ease.lua
Normal file
|
@ -0,0 +1,38 @@
|
|||
local self = {}
|
||||
|
||||
local easelib = require 'easelib'
|
||||
|
||||
function self.mixEase(e1, e2, point)
|
||||
if not point then point = 0.5 end
|
||||
|
||||
return function(a)
|
||||
if a < point then
|
||||
return e1(a / point) * point
|
||||
else
|
||||
return e2((a - point) / (1 - point)) * (1 - point) + point
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.eases = {}
|
||||
for i,v in pairs(easelib) do
|
||||
local min = 0
|
||||
|
||||
local q = 10
|
||||
for i = 0, q do
|
||||
local s = v[2](i / q)
|
||||
if s < 0 then min = -1 end
|
||||
end
|
||||
|
||||
self.eases[v[1]] = {
|
||||
f = v[2],
|
||||
max = 1,
|
||||
min = min,
|
||||
i = i
|
||||
}
|
||||
end
|
||||
|
||||
self.ease = nil
|
||||
self.minEase = false
|
||||
|
||||
return self
|
114
easelib.lua
114
easelib.lua
|
@ -33,100 +33,100 @@ table.insert(self, {'spike', function(t) return exp(-10 * abs(2 * t - 1)) end})
|
|||
table.insert(self, {'inverse', function(t) return t * t * (1 - t) * (1 - t) / (0.5 - t) end})
|
||||
|
||||
table.insert(self, {'inSine', function(x)
|
||||
return 1 - cos(x * (pi * 0.5))
|
||||
return 1 - cos(x * (pi * 0.5))
|
||||
end})
|
||||
|
||||
table.insert(self, {'outSine', function(x)
|
||||
return sin(x * (pi * 0.5))
|
||||
return sin(x * (pi * 0.5))
|
||||
end})
|
||||
|
||||
table.insert(self, {'inOutSine', function(x)
|
||||
return 0.5 - 0.5 * cos(x * pi)
|
||||
return 0.5 - 0.5 * cos(x * pi)
|
||||
end})
|
||||
|
||||
table.insert(self, {'inQuad', function(t) return t * t end})
|
||||
table.insert(self, {'outQuad', function(t) return -t * (t - 2) end})
|
||||
table.insert(self, {'inOutQuad', function(t)
|
||||
t = t * 2
|
||||
if t < 1 then
|
||||
return 0.5 * t ^ 2
|
||||
else
|
||||
return 1 - 0.5 * (2 - t) ^ 2
|
||||
end
|
||||
t = t * 2
|
||||
if t < 1 then
|
||||
return 0.5 * t ^ 2
|
||||
else
|
||||
return 1 - 0.5 * (2 - t) ^ 2
|
||||
end
|
||||
end})
|
||||
table.insert(self, {'inCubic', function(t) return t * t * t end})
|
||||
table.insert(self, {'outCubic', function(t) return 1 - (1 - t) ^ 3 end})
|
||||
table.insert(self, {'inOutCubic', function(t)
|
||||
t = t * 2
|
||||
if t < 1 then
|
||||
return 0.5 * t ^ 3
|
||||
else
|
||||
return 1 - 0.5 * (2 - t) ^ 3
|
||||
end
|
||||
t = t * 2
|
||||
if t < 1 then
|
||||
return 0.5 * t ^ 3
|
||||
else
|
||||
return 1 - 0.5 * (2 - t) ^ 3
|
||||
end
|
||||
end})
|
||||
table.insert(self, {'inQuart', function(t) return t * t * t * t end})
|
||||
table.insert(self, {'outQuart', function(t) return 1 - (1 - t) ^ 4 end})
|
||||
table.insert(self, {'inOutQuart', function(t)
|
||||
t = t * 2
|
||||
if t < 1 then
|
||||
return 0.5 * t ^ 4
|
||||
else
|
||||
return 1 - 0.5 * (2 - t) ^ 4
|
||||
end
|
||||
t = t * 2
|
||||
if t < 1 then
|
||||
return 0.5 * t ^ 4
|
||||
else
|
||||
return 1 - 0.5 * (2 - t) ^ 4
|
||||
end
|
||||
end})
|
||||
table.insert(self, {'inQuint', function(t) return t ^ 5 end})
|
||||
table.insert(self, {'outQuint', function(t) return 1 - (1 - t) ^ 5 end})
|
||||
table.insert(self, {'inOutQuint', function(t)
|
||||
t = t * 2
|
||||
if t < 1 then
|
||||
return 0.5 * t ^ 5
|
||||
else
|
||||
return 1 - 0.5 * (2 - t) ^ 5
|
||||
end
|
||||
t = t * 2
|
||||
if t < 1 then
|
||||
return 0.5 * t ^ 5
|
||||
else
|
||||
return 1 - 0.5 * (2 - t) ^ 5
|
||||
end
|
||||
end})
|
||||
table.insert(self, {'inExpo', function(t) return 1000 ^ (t - 1) - 0.001 end})
|
||||
table.insert(self, {'outExpo', function(t) return 1.001 - 1000 ^ -t end})
|
||||
table.insert(self, {'inOutExpo', function(t)
|
||||
t = t * 2
|
||||
if t < 1 then
|
||||
return 0.5 * 1000 ^ (t - 1) - 0.0005
|
||||
else
|
||||
return 1.0005 - 0.5 * 1000 ^ (1 - t)
|
||||
end
|
||||
t = t * 2
|
||||
if t < 1 then
|
||||
return 0.5 * 1000 ^ (t - 1) - 0.0005
|
||||
else
|
||||
return 1.0005 - 0.5 * 1000 ^ (1 - t)
|
||||
end
|
||||
end})
|
||||
table.insert(self, {'inCirc', function(t) return 1 - sqrt(1 - t * t) end})
|
||||
table.insert(self, {'outCirc', function(t) return sqrt(-t * t + 2 * t) end})
|
||||
table.insert(self, {'inOutCirc', function(t)
|
||||
t = t * 2
|
||||
if t < 1 then
|
||||
return 0.5 - 0.5 * sqrt(1 - t * t)
|
||||
else
|
||||
t = t - 2
|
||||
return 0.5 + 0.5 * sqrt(1 - t * t)
|
||||
end
|
||||
t = t * 2
|
||||
if t < 1 then
|
||||
return 0.5 - 0.5 * sqrt(1 - t * t)
|
||||
else
|
||||
t = t - 2
|
||||
return 0.5 + 0.5 * sqrt(1 - t * t)
|
||||
end
|
||||
end})
|
||||
|
||||
table.insert(self, {'outBounce', function(t)
|
||||
if t < 1 / 2.75 then
|
||||
return 7.5625 * t * t
|
||||
elseif t < 2 / 2.75 then
|
||||
t = t - 1.5 / 2.75
|
||||
return 7.5625 * t * t + 0.75
|
||||
elseif t < 2.5 / 2.75 then
|
||||
t = t - 2.25 / 2.75
|
||||
return 7.5625 * t * t + 0.9375
|
||||
else
|
||||
t = t - 2.625 / 2.75
|
||||
return 7.5625 * t * t + 0.984375
|
||||
end
|
||||
if t < 1 / 2.75 then
|
||||
return 7.5625 * t * t
|
||||
elseif t < 2 / 2.75 then
|
||||
t = t - 1.5 / 2.75
|
||||
return 7.5625 * t * t + 0.75
|
||||
elseif t < 2.5 / 2.75 then
|
||||
t = t - 2.25 / 2.75
|
||||
return 7.5625 * t * t + 0.9375
|
||||
else
|
||||
t = t - 2.625 / 2.75
|
||||
return 7.5625 * t * t + 0.984375
|
||||
end
|
||||
end})
|
||||
table.insert(self, {'inBounce', function(t) return 1 - self.outBounce(1 - t) end})
|
||||
table.insert(self, {'inOutBounce', function(t)
|
||||
if t < 0.5 then
|
||||
return self.inBounce(t * 2) * 0.5
|
||||
else
|
||||
return self.outBounce(t * 2 - 1) * 0.5 + 0.5
|
||||
end
|
||||
if t < 0.5 then
|
||||
return self.inBounce(t * 2) * 0.5
|
||||
else
|
||||
return self.outBounce(t * 2 - 1) * 0.5 + 0.5
|
||||
end
|
||||
end})
|
||||
|
||||
return self
|
||||
|
|
95
graph.lua
Normal file
95
graph.lua
Normal file
|
@ -0,0 +1,95 @@
|
|||
local self = {}
|
||||
|
||||
local quality = 256
|
||||
local graph = {}
|
||||
|
||||
function self.update(dt)
|
||||
for i = 1, quality do
|
||||
local a = (i - 1) / (quality - 1)
|
||||
if not graph[i] then
|
||||
graph[i] = ease.ease(a)
|
||||
end
|
||||
end
|
||||
for i,v in ipairs(graph) do
|
||||
local a = (i - 1) / (quality - 1)
|
||||
local b = ease.ease(a)
|
||||
if minEase then
|
||||
b = b / 2 + 0.5
|
||||
end
|
||||
graph[i] = mix(v, b, math.min(dt * 18, 1))
|
||||
end
|
||||
end
|
||||
|
||||
function self.render()
|
||||
local sw, sh = love.graphics.getDimensions()
|
||||
|
||||
if mode == 1 or mode == 2 then
|
||||
local csize = 10 -- preview point size
|
||||
local size = math.min((sw - outerpadding) - ((dropdown.kget('ease2') or dropdown.kget('ease1')).x + dropdownWidth + padding), sh - outerpadding * 2 - padding * 3 - csize)
|
||||
|
||||
local x, y, w, h = sw - outerpadding - size, outerpadding, size, size
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.rectangle('line', x, y, w, h)
|
||||
|
||||
-- grid
|
||||
love.graphics.setColor(0.2, 0.2, 0.4, 0.2)
|
||||
local gridsize = 64
|
||||
for gx = 1, gridsize - 2 do
|
||||
love.graphics.line(x + margin + gx * w/gridsize, y + margin, x + margin + gx * w/gridsize, y + h - margin)
|
||||
end
|
||||
for gy = 1, gridsize - 2 do
|
||||
love.graphics.line(x + margin, y + margin + gy * h/gridsize, x + w - margin, y + margin + gy * h/gridsize)
|
||||
end
|
||||
|
||||
-- mixease point
|
||||
if mode == 2 then
|
||||
love.graphics.setColor(1, 1, 1, 0.8)
|
||||
love.graphics.line(x + margin + mixpoint * w, y, x + margin + mixpoint * w, y + h)
|
||||
end
|
||||
|
||||
-- preview point
|
||||
local t = love.timer.getTime() % 1
|
||||
love.graphics.setColor(0.4, 0.4, 1, 0.4)
|
||||
love.graphics.line(x + margin + t * w, y, x + margin + t * w, y + h)
|
||||
|
||||
-- y = 0 point
|
||||
-- todo: this will break with eases that dont have the first point at y0
|
||||
local py = graph[1]
|
||||
love.graphics.setColor(0.7, 0.7, 0.7, 0.4 * (1 - math.abs(py - 0.5) / 0.5))
|
||||
love.graphics.line(x, y + h - py * h, x + w, y + py * h)
|
||||
|
||||
-- polygone
|
||||
-- this isnt done with a polygon because else itd waste a Bunch of ram and i kinda, dont want to do that?
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
local last = graph[1] or 0
|
||||
for gx = 1, quality - 1 do
|
||||
local a = gx/quality
|
||||
local b = graph[gx + 1] or 0
|
||||
local px, py = x + margin + gx * ((w - margin)/quality), y + h - margin - b * (h - margin * 2)
|
||||
local ox, oy = x + margin + (gx - 1) * ((w - margin)/quality), y + h - margin - last * (h - margin * 2)
|
||||
if math.abs(b - last) < 1 then
|
||||
love.graphics.line(ox, oy, px, py)
|
||||
end
|
||||
last = b
|
||||
end
|
||||
|
||||
-- preview
|
||||
love.graphics.setColor(1, 1, 1, 0.2)
|
||||
love.graphics.line(x + margin, y + h + padding * 2 + csize/2, x + w - margin, y + h + padding * 2 + csize/2)
|
||||
|
||||
love.graphics.setColor(0.4, 0.4, 1, 1)
|
||||
local a1 = ease.ease(t)
|
||||
local a2 = ease.ease(math.max(math.min(t - 0.1, 1), 0))
|
||||
local da = a1
|
||||
if love.timer.getTime() % 2 < 1 and math.floor(ease.ease(0) + 0.5) ~= math.floor(ease.ease(1) + 0.5) then
|
||||
da = 1 - da
|
||||
end
|
||||
if minEase then
|
||||
da = da / 2 + 0.5
|
||||
end
|
||||
|
||||
love.graphics.ellipse('fill', x + margin + (w - margin * 2) * da, y + h + padding * 2 + csize/2, csize * (1 + math.min(math.abs(a1 - a2), 3) * 1.2), csize)
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
433
main.lua
433
main.lua
|
@ -1,445 +1,132 @@
|
|||
local easelib = require 'easelib'
|
||||
|
||||
-- utils
|
||||
|
||||
local function keys(t)
|
||||
local k = {}
|
||||
for n in pairs(t) do table.insert(k, n) end
|
||||
return k
|
||||
end
|
||||
local function skeys(t)
|
||||
local k = {}
|
||||
for n,v in pairs(t) do table.insert(k, {n, v}) end
|
||||
table.sort(k, function(a, b) return a[2].i < b[2].i end)
|
||||
local k2 = {}
|
||||
for _,v in ipairs(k) do table.insert(k2, v[1]) end
|
||||
return k2
|
||||
local default_G = {}
|
||||
for k, v in pairs(_G) do
|
||||
table.insert(default_G, k)
|
||||
end
|
||||
|
||||
local function mix(x, y, a)
|
||||
return x * (1 - a) + y * a
|
||||
end
|
||||
ease = require 'ease'
|
||||
|
||||
-- eases
|
||||
dropdown = require 'dropdown'
|
||||
graph = require 'graph'
|
||||
|
||||
local function mixEase(e1, e2, point)
|
||||
if not point then point = 0.5 end
|
||||
|
||||
return function(a)
|
||||
if a < point then
|
||||
return e1(a / point) * point
|
||||
else
|
||||
return e2((a - point) / (1 - point)) * (1 - point) + point
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local eases = {}
|
||||
for i,v in pairs(easelib) do
|
||||
local min = 0
|
||||
|
||||
local q = 10
|
||||
for i = 0, q do
|
||||
local s = v[2](i / q)
|
||||
if s < 0 then min = -1 end
|
||||
end
|
||||
|
||||
eases[v[1]] = {
|
||||
f = v[2],
|
||||
max = 1,
|
||||
min = min,
|
||||
i = i
|
||||
}
|
||||
end
|
||||
|
||||
local ease
|
||||
local minEase = false
|
||||
require 'util' -- exports into global table
|
||||
|
||||
-- rendering constants
|
||||
|
||||
local padding = 6
|
||||
local margin = 4
|
||||
local quality = 256
|
||||
padding = 14
|
||||
outerpadding = 22
|
||||
margin = 6
|
||||
dropdownWidth = 106
|
||||
|
||||
local mixpoint = 0.5
|
||||
local oldmixpoint = 0.5
|
||||
-- slider
|
||||
|
||||
mixpoint = 0.5
|
||||
oldmixpoint = 0.5
|
||||
local mixpointtimer = 0 -- easter egg thing
|
||||
local mixpointspin = 0
|
||||
|
||||
local maxDropdown = 16
|
||||
|
||||
-- graph
|
||||
|
||||
local graph = {}
|
||||
|
||||
-- dropdown bullshit
|
||||
|
||||
local dropdowns = {}
|
||||
|
||||
local dropdownValueCache = {}
|
||||
|
||||
local openDropdown = 0
|
||||
local dropdownScroll = 0
|
||||
local dropdownScrollE = 0
|
||||
|
||||
local function selected(index)
|
||||
return dropdowns[index].options[dropdowns[index].selected]
|
||||
end
|
||||
|
||||
local function kget(key)
|
||||
for _, v in ipairs(dropdowns) do
|
||||
if v.name == key then
|
||||
return v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function kselected(key)
|
||||
for _, v in ipairs(dropdowns) do
|
||||
if v.name == key then
|
||||
return v.options[v.selected]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local dropdownId
|
||||
local function insertDropdown(tab, f)
|
||||
dropdownId = dropdownId + 1
|
||||
f.selected = (kget(f.name) or dropdownValueCache[f.name] or {selected = 1}).selected
|
||||
f.selected = (f.selected - 1) % #f.options + 1
|
||||
return table.insert(tab, f)
|
||||
end
|
||||
local function createDropdowns()
|
||||
local d = {}
|
||||
dropdownId = 0
|
||||
insertDropdown(d, {
|
||||
x = padding,
|
||||
y = padding,
|
||||
width = 128,
|
||||
options = {
|
||||
'Preview Ease',
|
||||
'Mix Eases',
|
||||
'Create Ease'
|
||||
},
|
||||
name = 'mode'
|
||||
})
|
||||
|
||||
if d[dropdownId].selected == 1 then -- preview ease
|
||||
insertDropdown(d, {
|
||||
x = padding + 128 + padding,
|
||||
y = padding,
|
||||
width = 128,
|
||||
options = skeys(eases),
|
||||
name = 'ease1'
|
||||
})
|
||||
ease = eases[d[dropdownId].options[d[dropdownId].selected]].f
|
||||
elseif d[dropdownId].selected == 2 then -- mix eases
|
||||
insertDropdown(d, {
|
||||
x = padding + 128 + padding,
|
||||
y = padding,
|
||||
width = 128,
|
||||
options = skeys(eases),
|
||||
name = 'ease1'
|
||||
})
|
||||
insertDropdown(d, {
|
||||
x = padding + 128 + padding + 128 + padding,
|
||||
y = padding,
|
||||
width = 128,
|
||||
options = skeys(eases),
|
||||
name = 'ease2'
|
||||
})
|
||||
ease = mixEase(eases[d[dropdownId - 1].options[d[dropdownId - 1].selected]].f, eases[d[dropdownId].options[d[dropdownId].selected]].f, mixpoint)
|
||||
elseif d[dropdownId].selected == 3 then -- create eases
|
||||
insertDropdown(d, {
|
||||
x = padding + 128 + padding,
|
||||
y = padding,
|
||||
width = 128,
|
||||
options = skeys(eases),
|
||||
name = 'ease1'
|
||||
})
|
||||
end
|
||||
|
||||
minEase = (kselected('ease1') and eases[kselected('ease1')].min == -1) or (kselected('ease2') and eases[kselected('ease2')].min == -1)
|
||||
|
||||
dropdowns = d
|
||||
end
|
||||
|
||||
-- rendering
|
||||
|
||||
function love.load()
|
||||
createDropdowns()
|
||||
dropdown.createDropdowns()
|
||||
end
|
||||
|
||||
function love.update(dt)
|
||||
for i = 1, quality do
|
||||
local a = (i - 1) / (quality - 1)
|
||||
if not graph[i] then
|
||||
graph[i] = ease(a)
|
||||
end
|
||||
end
|
||||
for i,v in ipairs(graph) do
|
||||
local a = (i - 1) / (quality - 1)
|
||||
local b = ease(a)
|
||||
if minEase then
|
||||
b = b / 2 + 0.5
|
||||
end
|
||||
graph[i] = mix(v, b, math.min(dt * 18, 1))
|
||||
end
|
||||
|
||||
graph.update(dt)
|
||||
|
||||
-- slider
|
||||
mixpointtimer = mix(mixpointtimer + math.abs(mixpoint - oldmixpoint), 0, math.min(dt * 8))
|
||||
oldmixpoint = mix(oldmixpoint, mixpoint, math.min(dt * 20, 1))
|
||||
|
||||
|
||||
if mixpointtimer > 2 then
|
||||
mixpointtimer = mixpointtimer - 2
|
||||
mixpointspin = mixpointspin + 4
|
||||
end
|
||||
|
||||
|
||||
mixpointspin = mix(mixpointspin, 0, dt * 3)
|
||||
|
||||
if openDropdown ~= 0 then
|
||||
dropdownScroll = math.max(dropdownScroll, -(#dropdowns[openDropdown].options - maxDropdown))
|
||||
dropdownScroll = math.min(dropdownScroll, 0)
|
||||
dropdownScrollE = mix(dropdownScrollE, dropdownScroll, dt * 10)
|
||||
end
|
||||
|
||||
dropdown.update(dt)
|
||||
end
|
||||
|
||||
function love.draw()
|
||||
local mode = kget('mode').selected
|
||||
mode = dropdown.kget('mode').selected
|
||||
local sw, sh = love.graphics.getDimensions()
|
||||
local mx, my = love.mouse.getPosition()
|
||||
|
||||
|
||||
love.graphics.setColor(0.09, 0.09, 0.12, 1)
|
||||
love.graphics.rectangle('fill', 0, 0, sw, sh)
|
||||
love.graphics.setColor(0.08, 0.08, 0.1, 1)
|
||||
love.graphics.rectangle('line', 0, 0, sw, sh)
|
||||
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.print('Box of Eases by oatmealine', padding, sh - love.graphics.getFont():getHeight() - padding)
|
||||
|
||||
love.graphics.print('Box of Eases by oatmealine', outerpadding, sh - love.graphics.getFont():getHeight() - outerpadding)
|
||||
|
||||
-- sliders
|
||||
-- yeah we do a lil' hardcoding
|
||||
|
||||
|
||||
if mode == 2 then
|
||||
local x, y, w, h = padding, padding * 2 + love.graphics.getFont():getHeight() + margin, 128, 32
|
||||
|
||||
local x, y, w, h = outerpadding, outerpadding * 2 + love.graphics.getFont():getHeight() + margin, dropdownWidth, 32
|
||||
|
||||
love.graphics.setColor(0.7, 0.7, 0.7, 0.4)
|
||||
love.graphics.line(x, y + h/2, x + w, y + h/2)
|
||||
|
||||
|
||||
local sx, sy = x + w * oldmixpoint, y + h/2
|
||||
local ssize = h * 0.5
|
||||
|
||||
|
||||
love.graphics.push()
|
||||
|
||||
|
||||
love.graphics.translate(sx, sy)
|
||||
love.graphics.rotate((mixpoint - oldmixpoint) * 4 + mixpointspin * math.pi * 2)
|
||||
|
||||
|
||||
love.graphics.setColor(0, 0, 0, 1)
|
||||
if mx > sx - ssize/2 and mx < sx + ssize/2 and my > sy - ssize/2 and my < sy + ssize/2 and openDropdown == 0 then
|
||||
if mx > sx - ssize/2 and mx < sx + ssize/2 and my > sy - ssize/2 and my < sy + ssize/2 and dropdown.openDropdown == 0 then
|
||||
love.graphics.setColor(0.2, 0.2, 0.3, 1)
|
||||
end
|
||||
love.graphics.rectangle('fill', -ssize/2, -ssize/2, ssize, ssize)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.rectangle('line', -ssize/2, -ssize/2, ssize, ssize)
|
||||
|
||||
|
||||
love.graphics.rotate((mixpoint - oldmixpoint) * -2)
|
||||
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.printf(math.floor(mixpoint * 100)/100, -ssize * 6, ssize - 2, ssize * 12, 'center')
|
||||
|
||||
|
||||
love.graphics.pop()
|
||||
|
||||
if mx > x and mx < x + w and my > y and my < y + h and love.mouse.isDown(1) and openDropdown == 0 then
|
||||
|
||||
if mx > x and mx < x + w and my > y and my < y + h and love.mouse.isDown(1) and dropdown.openDropdown == 0 then
|
||||
mixpoint = (mx - x) / w
|
||||
createDropdowns()
|
||||
dropdown.createDropdowns()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- dropdowns
|
||||
for i,v in ipairs(dropdowns) do
|
||||
local x, y, w, h = v.x, v.y, v.width, love.graphics.getFont():getHeight() + margin
|
||||
|
||||
love.graphics.setColor(0, 0, 0, 0.3)
|
||||
if love.mouse.getX() > x and love.mouse.getX() < x + w and love.mouse.getY() > y and love.mouse.getY() < y + h then
|
||||
love.graphics.setColor(0.8, 0.8, 1, love.mouse.isDown(1) and 0.4 or 0.3)
|
||||
end
|
||||
love.graphics.rectangle('fill', x, y, w, h)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
|
||||
love.graphics.rectangle('line', x, y, w, h)
|
||||
love.graphics.print(selected(i), x + margin/2, y + margin/2)
|
||||
love.graphics.rectangle('line', x + w - h, y, h, h)
|
||||
love.graphics.polygon('line', x + w - h/2 + 0.3 * h, y + h/2 - 0.3 * h, x + w - h/2 - 0.3 * h, y + h/2 - 0.3 * h, x + w - h/2, y + h/2 + 0.3 * h)
|
||||
|
||||
if openDropdown == i then
|
||||
for i,o in ipairs(v.options) do
|
||||
local x, y, w, h = x, y + i * h, w, h
|
||||
|
||||
y = y + dropdownScrollE * h
|
||||
local gi = y / h
|
||||
if gi > maxDropdown or gi < 1 then
|
||||
goto continue
|
||||
end
|
||||
|
||||
local a = 1 - math.min(math.max((1 - (maxDropdown - gi)) * (1 - (math.abs(dropdownScrollE) /(#v.options - maxDropdown))), 0), 1)
|
||||
|
||||
love.graphics.setColor(0, 0, 0, 0.3 * a)
|
||||
if mx > x and mx < x + w and my > y and my < y + h then
|
||||
love.graphics.setColor(0.8, 0.8, 1, (love.mouse.isDown(1) and 0.4 or 0.3) * a)
|
||||
end
|
||||
love.graphics.rectangle('fill', x, y, w, h)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 0.75 * a)
|
||||
love.graphics.rectangle('line', x, y, w, h)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1 * a)
|
||||
love.graphics.print(v.options[i], x + 2, y + 2)
|
||||
|
||||
::continue::
|
||||
end
|
||||
|
||||
-- scrollwheel
|
||||
|
||||
if #v.options > maxDropdown then
|
||||
local displayed = maxDropdown / #v.options
|
||||
local scroll = math.abs(dropdownScrollE) / (#v.options - maxDropdown)
|
||||
local size = margin
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 0.9)
|
||||
love.graphics.rectangle('fill', x + w - size, y + h + scroll * (1 - displayed) * (maxDropdown - 1) * h, size, displayed * (maxDropdown - 1) * h)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
dropdown.render()
|
||||
|
||||
-- graph
|
||||
|
||||
if mode == 1 or mode == 2 then
|
||||
local csize = 10 -- preview point size
|
||||
local size = math.min((sw - padding) - ((kget('ease2') or kget('ease1')).x + 128 + padding), sh - padding * 5 - csize)
|
||||
|
||||
local x, y, w, h = sw - padding - size, padding, size, size
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.rectangle('line', x, y, w, h)
|
||||
|
||||
-- grid
|
||||
love.graphics.setColor(0.2, 0.2, 0.4, 0.2)
|
||||
local gridsize = 64
|
||||
for gx = 1, gridsize - 2 do
|
||||
love.graphics.line(x + margin + gx * w/gridsize, y + margin, x + margin + gx * w/gridsize, y + h - margin)
|
||||
end
|
||||
for gy = 1, gridsize - 2 do
|
||||
love.graphics.line(x + margin, y + margin + gy * h/gridsize, x + w - margin, y + margin + gy * h/gridsize)
|
||||
end
|
||||
|
||||
-- mixease point
|
||||
if mode == 2 then
|
||||
love.graphics.setColor(1, 1, 1, 0.8)
|
||||
love.graphics.line(x + margin + mixpoint * w, y, x + margin + mixpoint * w, y + h)
|
||||
end
|
||||
|
||||
-- preview point
|
||||
local t = love.timer.getTime() % 1
|
||||
love.graphics.setColor(0.4, 0.4, 1, 0.4)
|
||||
love.graphics.line(x + margin + t * w, y, x + margin + t * w, y + h)
|
||||
|
||||
-- y = 0 point
|
||||
-- todo: this will break with eases that dont have the first point at y0
|
||||
local py = graph[1]
|
||||
love.graphics.setColor(0.7, 0.7, 0.7, 0.4 * (1 - math.abs(py - 0.5) / 0.5))
|
||||
love.graphics.line(x, y + h - py * h, x + w, y + py * h)
|
||||
|
||||
-- polygone
|
||||
-- this isnt done with a polygon because else itd waste a Bunch of ram and i kinda, dont want to do that?
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
local last = graph[1] or 0
|
||||
for gx = 1, quality - 1 do
|
||||
local a = gx/quality
|
||||
local b = graph[gx + 1] or 0
|
||||
local px, py = x + margin + gx * ((w - margin)/quality), y + h - margin - b * (h - margin * 2)
|
||||
local ox, oy = x + margin + (gx - 1) * ((w - margin)/quality), y + h - margin - last * (h - margin * 2)
|
||||
if math.abs(b - last) < 1 then
|
||||
love.graphics.line(ox, oy, px, py)
|
||||
end
|
||||
last = b
|
||||
end
|
||||
|
||||
-- preview
|
||||
love.graphics.setColor(1, 1, 1, 0.2)
|
||||
love.graphics.line(x + margin, y + h + padding * 2 + csize/2, x + w - margin, y + h + padding * 2 + csize/2)
|
||||
|
||||
love.graphics.setColor(0.4, 0.4, 1, 1)
|
||||
local a1 = ease(t)
|
||||
local a2 = ease(math.max(math.min(t - 0.1, 1), 0))
|
||||
local da = a1
|
||||
if love.timer.getTime() % 2 < 1 and math.floor(ease(0) + 0.5) ~= math.floor(ease(1) + 0.5) then
|
||||
da = 1 - da
|
||||
end
|
||||
if minEase then
|
||||
da = da / 2 + 0.5
|
||||
end
|
||||
|
||||
love.graphics.ellipse('fill', x + margin + (w - margin * 2) * da, y + h + padding * 2 + csize/2, csize * (1 + math.min(math.abs(a1 - a2), 3) * 1.2), csize)
|
||||
end
|
||||
graph.render()
|
||||
end
|
||||
|
||||
function love.mousepressed(x, y, m)
|
||||
local clickedDropdown = false
|
||||
for i,v in ipairs(dropdowns) do
|
||||
local h = love.graphics.getFont():getHeight() + margin
|
||||
if openDropdown == 0 then
|
||||
if x > v.x and x < v.x + v.width and y > v.y and y < v.y + h + margin then
|
||||
if m == 1 then
|
||||
openDropdown = i
|
||||
clickedDropdown = true
|
||||
dropdownScroll = 0
|
||||
dropdownScrollE = 0
|
||||
elseif m == 3 then
|
||||
dropdowns[i].selected = math.random(1, #dropdowns[i].options)
|
||||
createDropdowns()
|
||||
end
|
||||
end
|
||||
end
|
||||
if openDropdown == i then
|
||||
if x > v.x and x < v.x + v.width and y > v.y + h and y < v.y + h * (math.min(#v.options, maxDropdown) + 1) and m == 1 then
|
||||
clickedDropdown = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not clickedDropdown and m == 1 then
|
||||
openDropdown = 0
|
||||
end
|
||||
if dropdown.mousepressed(x, y, m) then return end
|
||||
end
|
||||
|
||||
function love.mousereleased(x, y, m)
|
||||
for i,v in ipairs(dropdowns) do
|
||||
local h = love.graphics.getFont():getHeight() + margin
|
||||
if openDropdown == i then
|
||||
if x > v.x and x < v.x + v.width and y > v.y + h and y < v.y + h * (math.min(#v.options, maxDropdown) + 1) and m == 1 then
|
||||
v.selected = math.floor((y - v.y) / h - dropdownScrollE)
|
||||
openDropdown = 0
|
||||
dropdownValueCache[v.name] = {selected = v.selected}
|
||||
createDropdowns()
|
||||
end
|
||||
end
|
||||
end
|
||||
if dropdown.mousereleased(x, y, m) then return end
|
||||
end
|
||||
|
||||
function love.wheelmoved(x, y)
|
||||
if y == 0 then return end
|
||||
|
||||
if openDropdown ~= 0 then
|
||||
dropdownScroll = dropdownScroll + y
|
||||
else
|
||||
local mx, my = love.mouse.getPosition()
|
||||
for i,v in ipairs(dropdowns) do
|
||||
local h = love.graphics.getFont():getHeight() + margin
|
||||
if mx > v.x and mx < v.x + v.width and my > v.y and my < v.y + h + margin then
|
||||
dropdowns[i].selected = dropdowns[i].selected - math.floor(y)
|
||||
dropdowns[i].selected = (dropdowns[i].selected - 1) % #dropdowns[i].options + 1
|
||||
createDropdowns()
|
||||
|
||||
if dropdown.wheelmoved(x, y) then return end
|
||||
end
|
||||
|
||||
function love.keypressed(key)
|
||||
if key == 'f6' then -- print all globals
|
||||
for k, v in pairs(_G) do
|
||||
for _, g in ipairs(default_G) do
|
||||
if g == k then goto continue end
|
||||
end
|
||||
print(k, v)
|
||||
::continue::
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
3
util.lua
Normal file
3
util.lua
Normal file
|
@ -0,0 +1,3 @@
|
|||
function mix(x, y, a)
|
||||
return x * (1 - a) + y * a
|
||||
end
|
Loading…
Reference in a new issue